| /****************************************************************************** |
| * |
| * <:copyright-BRCM:2016:DUAL/GPL:standard |
| * |
| * Copyright (c) 2016 Broadcom |
| * All Rights Reserved |
| * |
| * Unless you and Broadcom execute a separate written software license |
| * agreement governing use of this software, this software is licensed |
| * to you under the terms of the GNU General Public License version 2 |
| * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, |
| * with the following added to such license: |
| * |
| * As a special exception, the copyright holders of this software give |
| * you permission to link this software with independent modules, and |
| * to copy and distribute the resulting executable under terms of your |
| * choice, provided that you also meet, for each linked independent |
| * module, the terms and conditions of the license of that module. |
| * An independent module is a module which is not derived from this |
| * software. The special exception does not apply to any modifications |
| * of the software. |
| * |
| * Not withstanding the above, under no circumstances may you combine |
| * this software in any way with any other Broadcom software provided |
| * under a license other than the GPL, without Broadcom's express prior |
| * written consent. |
| * |
| * :> |
| * |
| *****************************************************************************/ |
| |
| /** |
| * @file bal_esw_flow.c |
| * @brief BAL Switch util functions that handle flow requests |
| * @addtogroup sw_util |
| */ |
| |
| /*@{*/ |
| #include <bal_common.h> |
| #include <bcm_dev_log.h> |
| #include <bal_msg.h> |
| #include "bal_switch_flow.h" |
| #include "flow_fsm.h" |
| #include "bcmos_errno.h" |
| #include "bal_switch_util.h" |
| |
| #ifndef TEST_SW_UTIL_LOOPBACK |
| #include <bcm/types.h> |
| #include <sal/core/libc.h> |
| #ifndef sal_memset |
| #define sal_memset memset |
| #endif |
| #include <bcm/port.h> |
| #include <bcm/vlan.h> |
| #include <bcm/field.h> |
| #include <bcm/error.h> |
| #include <sal/core/libc.h> |
| |
| #include "bal_switch_acc_term.h" |
| #include "bal_esw_flow.h" |
| /** |
| * @brief The acl add function add an Access Control Rule in the switch VCAP/ICAP/ECAP |
| * to perform action based on flow classifier |
| * |
| * @param unit the switch unit this rule is to be added |
| * @param p_flow a pointer to the flow definition the created rule will be based on |
| * @return error code |
| */ |
| static bcm_field_group_t esw_group_id = 0; |
| /* add an ingress ACL rule */ |
| static bcmos_errno bal_swapp_esw_acl_add(int unit, bcmbal_flow_cfg *p_flow) |
| { |
| uint32_t ret, j; |
| uint32_t nni_phy; |
| int vid; |
| bcm_field_qset_t qset; |
| bcm_field_entry_t eid; |
| bcm_mac_t bcast_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| bcm_mac_t dst_mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| |
| do |
| { |
| vid = p_flow->data.classifier.o_vid; |
| |
| /* VCAP - bcmFieldQualifyStageLookup, ICAP - bcmFieldQualifyStageIngress, ECAP - bcmFieldQualifyStageEgress */ |
| /* The KT2 resources allow only limit number of qset - indexed by esw_group_id, create qset when necessary */ |
| if (0 == esw_group_id) |
| { |
| BCM_FIELD_QSET_INIT(qset); |
| BCM_FIELD_QSET_ADD(qset, bcmFieldQualifyStageLookup); |
| BCM_FIELD_QSET_ADD(qset, bcmFieldQualifyOuterVlan); |
| BCM_FIELD_QSET_ADD(qset, bcmFieldQualifyInPort); |
| BCM_FIELD_QSET_ADD(qset, bcmFieldQualifyDstMac); |
| ret = bcm_field_group_create(unit, qset, BCM_FIELD_GROUP_PRIO_ANY, &esw_group_id); |
| if (ret != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to create field - %d\n", ret ); |
| break; |
| } |
| } |
| |
| /* if action is to drop broadcast, add an ACL in nni VCAP to drop it */ |
| if ( p_flow->data.action.cmds_bitmask & BCMBAL_ACTION_CMD_ID_DISCARD_DS_BCAST) |
| { |
| /* loop through all nni ports */ |
| for(j=0; -1 != (nni_phy = bal_bcm_net_inf_pbm_get(j)); j++) |
| { |
| if ( bal_bcm_net_inf_dev_get(j) != unit) |
| { |
| continue; |
| } |
| |
| ret = bcm_field_entry_create(unit, esw_group_id, &eid); |
| if (ret != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to create field entry for port %d - ret = %d\n", nni_phy, ret ); |
| break; |
| } |
| |
| ret = bcm_field_qualify_DstMac(unit, eid, bcast_mac, dst_mask); |
| if (ret != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to set field dst mac qualifier %d - ret = %d\n", nni_phy, ret ); |
| break; |
| } |
| |
| ret = bcm_field_qualify_OuterVlanId(unit, eid, vid, 0xffff); |
| if (ret != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to set field outer vlan qualifier %d - ret = %d\n", nni_phy, ret ); |
| break; |
| } |
| ret = bcm_field_action_add(unit, eid, bcmFieldActionDrop, 0, 0); |
| if (ret != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to add action to the field entry %d - ret = %d\n", nni_phy, ret ); |
| break; |
| } |
| |
| ret = bcm_field_entry_install(unit, eid); |
| if (ret != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to install field entry %d - ret = %d\n", nni_phy, ret ); |
| break; |
| } |
| } /* for loop */ |
| } /* if drop bcast */ |
| } while(0); |
| |
| |
| if (ret != BCM_E_NONE) |
| { |
| return BCM_ERR_INTERNAL; |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| /** |
| * @brief The ingress vlan translation function program switch to |
| * translate packet vlan attributes before the switch vaidate the |
| * vlan membership of the packets |
| * @param unit the switch unit this translation is perfromed |
| * @param p_flow a pointer to the flow that contains translation action |
| * @return error code |
| */ |
| static bcmos_errno bal_swapp_esw_ivlanx(int unit, bcmbal_flow_cfg *p_flow) |
| { |
| bcmos_errno ret = BCM_ERR_OK; |
| bcm_gport_t pon_gport; |
| uint32_t pon_phy_pbm; |
| int bcm_rc; |
| |
| /* find out which PON this action is to be performed */ |
| /* map pon logical port to physical port */ |
| pon_phy_pbm = bal_bcm_pon_inf_pbm_get(p_flow->data.access_int_id); |
| /* create local gport based on pon physical port number */ |
| BCM_GPORT_LOCAL_SET(pon_gport, pon_phy_pbm); |
| |
| do |
| { |
| /* For TR-156 1:1 uptream single tagged packets, |
| S-tag add acton is performed in the INGRESS vlan translator. |
| */ |
| if (BCMBAL_FLOW_TYPE_UPSTREAM == p_flow->key.flow_type) |
| { |
| if ( p_flow->data.action.cmds_bitmask & BCMBAL_ACTION_CMD_ID_ADD_OUTER_TAG ) |
| { |
| uint32_t u_ovid, u_ivid; |
| bcm_vlan_action_set_t u_action; |
| bcm_vlan_action_set_t_init(&u_action); |
| |
| /* set gpon ingress translaton to add outer tag on upstream packets */ |
| u_action.new_outer_vlan = p_flow->data.action.o_vid; |
| u_action.priority = p_flow->data.action.o_pbits; |
| |
| switch(p_flow->data.classifier.pkt_tag_type) |
| { |
| case BCMBAL_PKT_TAG_TYPE_SINGLE_TAG: |
| u_action.ot_outer = bcmVlanActionAdd; |
| u_action.ot_outer_pkt_prio = bcmVlanActionReplace; |
| u_ovid = p_flow->data.classifier.o_vid; |
| u_ivid = BCM_VLAN_NONE; |
| break; |
| default: /* not supported, goto while(0) */ |
| u_ovid = BCM_VLAN_NONE; |
| u_ivid = BCM_VLAN_NONE; |
| ret = BCM_ERR_INTERNAL; |
| continue; |
| } |
| /* enable ingress vlan translation on specified port */ |
| bcm_rc = bcm_vlan_control_port_set(unit, pon_gport, bcmVlanTranslateIngressEnable, 1); |
| if (bcm_rc != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to enable ingress vlan translation on port %d - %d\n", |
| pon_phy_pbm, bcm_rc ); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| /* set the outer vlan id as lookup key - i.e. packet filtering key */ |
| bcm_rc = bcm_vlan_control_port_set(unit, pon_gport, bcmVlanPortTranslateKeyFirst, bcmVlanTranslateKeyOuter); |
| if (bcm_rc != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to set upstream lookup key on port %d - %d\n", |
| pon_phy_pbm, bcm_rc ); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| /* install the action into ingress vlan translation table */ |
| bcm_rc = bcm_vlan_translate_action_add(unit, pon_gport, bcmVlanTranslateKeyOuter, u_ovid, u_ivid, &u_action); |
| if (bcm_rc != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to set ingress action on port %d - %d\n", |
| pon_phy_pbm, bcm_rc ); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| } |
| else |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow upstream action 0x%x not supported\n", p_flow->data.action.cmds_bitmask); |
| |
| ret = BCM_ERR_NOT_SUPPORTED; |
| } |
| } |
| |
| } while(0); |
| |
| return ret; |
| } |
| |
| /** |
| * @brief The engress vlan translation function program switch to |
| * translate packet vlan attributes before the packets were sent out |
| * |
| * @param unit the switch unit this translation is perfromed |
| * @param p_flow a pointer to the flow that contains translation action |
| * @return error code |
| */ |
| static bcmos_errno bal_swapp_esw_evlanx(int unit, bcmbal_flow_cfg *p_flow) |
| { |
| bcmos_errno ret = BCM_ERR_OK; |
| bcm_gport_t pon_gport; |
| uint32_t pon_phy_pbm; |
| int bcm_rc; |
| |
| /* find out which PON this action is to be performed */ |
| /* map pon logical port to physical port */ |
| pon_phy_pbm = bal_bcm_pon_inf_pbm_get(p_flow->data.access_int_id); |
| /* create local gport based on pon physical port number */ |
| BCM_GPORT_LOCAL_SET(pon_gport, pon_phy_pbm); |
| |
| do |
| { |
| /* For TR-156 1:1 downstream double tagged packets, |
| S-tag remove acton is performed in the EGRESS vlan translator. |
| */ |
| if (BCMBAL_FLOW_TYPE_DOWNSTREAM == p_flow->key.flow_type) |
| { |
| |
| if ( p_flow->data.action.cmds_bitmask & BCMBAL_ACTION_CMD_ID_REMOVE_OUTER_TAG ) |
| { |
| /* set gpon egress translaton to drop outer tag of double tag downstream packets */ |
| uint32_t d_ovid, d_ivid; |
| bcm_vlan_action_set_t d_action; |
| bcm_vlan_action_set_t_init(&d_action); |
| switch(p_flow->data.classifier.pkt_tag_type) |
| { |
| case BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG: |
| d_action.dt_outer = bcmVlanActionDelete; |
| d_ovid = p_flow->data.classifier.o_vid; |
| d_ivid = p_flow->data.classifier.i_vid; |
| break; |
| |
| default: /* not supported, goto while(0) */ |
| d_ovid = BCM_VLAN_NONE; |
| d_ivid = BCM_VLAN_NONE; |
| ret = BCM_ERR_INTERNAL; |
| continue; |
| } |
| /* enable egress vlan translation on specified port */ |
| bcm_rc = bcm_vlan_control_port_set(unit, pon_gport, bcmVlanTranslateEgressEnable, 1); |
| if (bcm_rc != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to enable egress vlan translation on port %d - %d\n", |
| pon_phy_pbm, bcm_rc ); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| |
| /* install the action into egress vlan translation table */ |
| bcm_rc = bcm_vlan_translate_egress_action_add(unit, pon_gport, d_ovid, d_ivid, &d_action); |
| if (bcm_rc != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to set egress action on port %d - %d\n", |
| pon_phy_pbm, bcm_rc ); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| } |
| else |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow downstream action 0x%x not supported\n", p_flow->data.action.cmds_bitmask); |
| |
| ret = BCM_ERR_NOT_SUPPORTED; |
| } |
| |
| } |
| |
| } while(0); |
| |
| return ret; |
| } |
| |
| |
| /** |
| * @brief The flow add function program KT2 to forward packets that have |
| * specified attributes to the designated ports. |
| * The packets is modified before egress |
| * On the downstream, an access id (outer vlan tag) is added to the packets |
| * On the upstream, outer vlan tag (access id) is removed from the packets |
| * |
| * @param iwf_mode The InterWorking Function mode - DIRECT or PER-FLOW |
| * @param p_flow A pointer to the requested add flow info |
| * @return error code |
| */ |
| bcmos_errno bal_sw_util_esw_flow_add(bcmbal_iwf_mode iwf_mode, bcmbal_flow_cfg *p_flow) |
| { |
| bcmos_errno ret = BCM_ERR_OK; |
| bcm_gport_t pon_gport; |
| uint32_t pon_phy_pbm; |
| bcm_vlan_t vlan_id; |
| bcm_gport_t nni_gport; |
| uint32_t nni_phy_pbm; |
| int ii; |
| int bcm_rc; |
| int unit = bal_bcm_pon_inf_dev_get(p_flow->data.access_int_id); |
| |
| BCM_LOG(INFO, log_id_sw_util, |
| " Got an ESW flow request - iwf_mode=%d flow_id=%d sub_port=%d svc_id=%d\n", |
| iwf_mode, |
| p_flow->key.flow_id, p_flow->data.access_int_id, p_flow->data.svc_port_id); |
| BCM_LOG(DEBUG, log_id_sw_util, |
| " classifier - mask=0x%llx otpid=%x itpid=%x ovid=%x ivid=%x\n", |
| (unsigned long long)p_flow->data.classifier.presence_mask, |
| p_flow->data.classifier.o_tpid, p_flow->data.classifier.i_tpid, |
| p_flow->data.classifier.o_vid, p_flow->data.classifier.i_vid); |
| |
| /* |
| * First, validate that the specified PON has at least one NNI port on the same device. |
| * If not, return an error, as this is not supported. |
| */ |
| ii = 0; |
| ret = BCM_ERR_NOT_SUPPORTED; |
| /* walk through the entire mapping table */ |
| while(-1 != bal_bcm_net_inf_pbm_get(ii)) |
| { |
| if(bal_bcm_net_inf_dev_get(ii) == unit) |
| { |
| ret = BCM_ERR_OK; |
| break; |
| } |
| ii++; /* Next NNI */ |
| } |
| |
| do |
| { |
| /* |
| * Check return code from device check above. Return if there was an error. |
| */ |
| if(BCM_ERR_OK != ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: no network port is on the same device as access port %d\n", p_flow->data.access_int_id); |
| break; |
| } |
| |
| /* create vlan domain for this flow */ |
| vlan_id = (bcm_vlan_t)p_flow->data.classifier.o_vid; |
| bcm_rc = bcm_vlan_create(unit, vlan_id); |
| |
| /* if OK or already existed, continue */ |
| if (bcm_rc != BCM_E_NONE && bcm_rc != BCM_E_EXISTS) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " failed to create vlan %d on unit %d - bcm_rc:%d\n", vlan_id, unit, bcm_rc ); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, |
| " vlan %d %s on unit %d - bcm_rc:%d\n", |
| vlan_id, |
| (BCM_E_EXISTS == bcm_rc) ? "reused" : "created", |
| unit, |
| bcm_rc ); |
| } |
| |
| /* map pon logical port to physical port */ |
| pon_phy_pbm = bal_bcm_pon_inf_pbm_get(p_flow->data.access_int_id); |
| |
| /* create gport based on pon physical port number */ |
| BCM_GPORT_LOCAL_SET(pon_gport, pon_phy_pbm); |
| |
| /* add the specified pon to vlan */ |
| bcm_rc = bcm_vlan_gport_add(unit, vlan_id, pon_gport, BCM_VLAN_GPORT_ADD_VP_VLAN_MEMBERSHIP); |
| |
| if (bcm_rc != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to add pon %d (pbm %d, gport %d) into vlan %d - %d\n", |
| p_flow->data.access_int_id, pon_phy_pbm, pon_gport, vlan_id, bcm_rc ); |
| |
| ret = BCM_ERR_INTERNAL; |
| |
| break; |
| } |
| |
| /* Add all the NNI ports that are on the same device to the vlan as well */ |
| |
| ii = 0; /* Start with the first NNI logical interface */ |
| |
| /* map nni logical ports to physical ports */ |
| while(-1 != (nni_phy_pbm = bal_bcm_net_inf_pbm_get(ii))) |
| { |
| if ( bal_bcm_net_inf_dev_get(ii) != unit) |
| { |
| continue; |
| } |
| /* create gport based on nni physical port number */ |
| BCM_GPORT_LOCAL_SET(nni_gport, nni_phy_pbm); |
| |
| bcm_rc = bcm_vlan_gport_add(unit, vlan_id, nni_gport, BCM_VLAN_GPORT_ADD_VP_VLAN_MEMBERSHIP); |
| |
| if (bcm_rc != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to add nni %d (pbm %d, gport %d) into vlan %d - %d\n", |
| ii, nni_phy_pbm, nni_gport, vlan_id, bcm_rc ); |
| |
| ret = BCM_ERR_INTERNAL; |
| |
| break; |
| } |
| |
| ii++; /* Next NNI */ |
| } |
| |
| /* perform the ACTION */ |
| if (BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, action) |
| /* && BCMOS_TRUE == BCMBAL_ATTRIBUTE_CFG_PROP_IS_SET(&p_flow->data.action, action, action_cmds_bitmask) */ |
| ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, |
| " Got a flow action - flow type = %d, cmd=%d in_ovid=%d, out_ovid=%d\n", |
| p_flow->key.flow_type, p_flow->data.action.cmds_bitmask, |
| p_flow->data.classifier.i_vid, p_flow->data.classifier.o_vid); |
| |
| /* enable vlan translation */ |
| bcm_rc = bcm_vlan_control_set(unit, bcmVlanTranslate, 1); |
| if (bcm_rc != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to enable vlan translation - %d\n", |
| bcm_rc ); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| /* For TR-156 1:1 downstream, |
| ACTIONs are performed on the EGRESS vlan translator. |
| For TR-156 1:1 upstream, |
| ACTIONs are perform on the INGRESS vlan translator. |
| For TR-156 N:1 there is no actions for switch |
| The outer tag adding is per PON base, i.e. upstream packets with same |
| inner vid can add different outer vid based on receiving PON |
| */ |
| if (BCMBAL_FLOW_TYPE_UPSTREAM == p_flow->key.flow_type) |
| { |
| if ( p_flow->data.action.cmds_bitmask & BCMBAL_ACTION_CMD_ID_ADD_OUTER_TAG ) |
| { |
| ret = bal_swapp_esw_ivlanx(unit, p_flow); |
| } |
| } |
| else if (BCMBAL_FLOW_TYPE_DOWNSTREAM == p_flow->key.flow_type)/* downstream */ |
| { |
| if ( p_flow->data.action.cmds_bitmask & BCMBAL_ACTION_CMD_ID_REMOVE_OUTER_TAG ) |
| { |
| ret = bal_swapp_esw_evlanx(unit, p_flow); |
| } |
| } |
| else /* broadcast */ |
| { |
| if ( p_flow->data.action.cmds_bitmask & BCMBAL_ACTION_CMD_ID_DISCARD_DS_BCAST) |
| { |
| ret = bal_swapp_esw_acl_add(unit, p_flow); |
| } |
| |
| } |
| } /* end if ACTION set */ |
| |
| } while(0); |
| |
| |
| return ret; |
| } |
| |
| /** |
| * @brief The flow remove function remove switch resource that were allocated during ADD |
| * |
| * @param iwf_mode The InterWorking Function mode - DIRECT or PER-FLOW |
| * @param p_flow A pointer to the requested add flow info |
| * @return error code |
| */ |
| bcmos_errno bal_sw_util_esw_flow_remove(bcmbal_iwf_mode iwf_mode, bcmbal_flow_cfg *p_flow) |
| { |
| return BCM_ERR_OK; |
| } |
| |
| #endif /* #ifndef TEST_SW_UTIL_LOOPBACK */ |
| |
| /*@}*/ |