| /****************************************************************************** |
| * |
| * <: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_dpp_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_utils_msg.h> |
| #include "bal_switch_flow.h" |
| #include "flow_fsm.h" |
| #include "bcmos_errno.h" |
| #include "bal_switch_util.h" |
| #include "bal_dpp_qos_map.h" |
| |
| #ifndef TEST_SW_UTIL_LOOPBACK |
| #define _SHR_PBMP_WIDTH 567 |
| /* match the WIDTH size of bcm_field_group_config_t in bcm/field.h with ARAD */ |
| /* we build swich_util without #define created by the sdk make/Make.local */ |
| #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 <bcm/vswitch.h> |
| #include <bcm/qos.h> |
| #include <bcm/l2.h> |
| |
| #include "bal_switch_acc_term.h" |
| #include "bal_dpp_flow.h" |
| #include "bal_dpp_qos.h" |
| #include "bal_dpp_vswitch.h" |
| #include "bal_dpp_group.h" |
| |
| /** Local routines declarations */ |
| static bcmos_errno bal_sw_util_reverse_flow_create(bcmbal_flow_cfg *p_flow, bcmbal_flow_cfg *p_flow_rev); |
| static bcmos_bool bal_sw_util_is_symmetry_flows(bcmbal_flow_cfg *p_flow, bcmbal_flow_cfg *p_ref_flow); |
| |
| /* perform initialization before any dpp flow function calls */ |
| void bal_sw_util_dpp_flow_init(void) |
| { |
| /* nothing to do here yet, place holder */ |
| return; |
| } |
| |
| /** |
| * @brief The create trap gport function create a trap port that allow TRAP action to |
| * associate with and perform packet trapping to the CPU port. |
| * |
| * @param unit the switch unit this trap port to be created |
| * @param p_trap_gport a pointer that the created gport will be return |
| * @param p_trap_code a pointer that the created trap code will be return |
| * @return bcm error code |
| */ |
| static int bal_sw_util_create_trap_gport(int unit, bcm_gport_t *p_trap_gport, uint32_t *p_trap_code) |
| { |
| int ret, trap_id; |
| bcm_rx_trap_config_t trap_config; |
| |
| ret = bcm_rx_trap_type_create(unit, 0, bcmRxTrapUserDefine, &trap_id); |
| if(ret != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "create trap type return %d\n", ret); |
| return ret; |
| } |
| |
| bcm_rx_trap_config_t_init(&trap_config); |
| trap_config.flags = (BCM_RX_TRAP_UPDATE_DEST | BCM_RX_TRAP_TRAP | BCM_RX_TRAP_REPLACE); |
| trap_config.trap_strength = 0; |
| trap_config.dest_port = BCM_GPORT_LOCAL_CPU; |
| ret = bcm_rx_trap_set(unit, trap_id, &trap_config); |
| if(ret != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "set rx trap return %d\n", ret); |
| return ret; |
| } |
| BCM_GPORT_TRAP_SET(*p_trap_gport, trap_id, 7 /* trap_strength */, 0); |
| *p_trap_code = trap_id; |
| return ret; |
| } |
| /* disable the field group (FG) source ip support to restrict the FG size */ |
| #define BAL_ACL_FG_SRC_IP_ENABLE 0 |
| /* create a default field group that used by all default ACL. |
| When a FLOW is created,a LIF is created to direct the packets to a VSwitch. |
| The LIF can only match packet with ingress port and VID. |
| If a FLOW needs to be classified with more attributes, an ACL is created to override the LIF. |
| A default ACL has the same packet classification as the LIF but has lower priority than the overlapped ACL. |
| The default ACL dropped packets that match the LIF but do not match the ACL. This enforce the FLOW |
| to only forward packets with exact match */ |
| static bcm_field_group_t dpp_dft_group_id = 1; |
| /* create a field group that used by all ACL */ |
| static bcm_field_group_t dpp_group_id = 0; |
| /** |
| * @brief Create a field group in the switch ICAP, |
| * The field group allow flow classifier to create ACL rules |
| * |
| * @param unit the switch unit this rule is to be added |
| * @param p_group_id a pointer to a variable that return the created group id |
| * @return error code |
| */ |
| static bcmos_errno bal_sw_util_dpp_fg_create(int unit, bcm_field_group_t *p_group_id) |
| { |
| bcm_field_group_status_t fg_status; |
| bcm_field_group_config_t grp; |
| int32_t ret = 0; |
| |
| /* VCAP - bcmFieldQualifyStageLookup, ICAP - bcmFieldQualifyStageIngress, ECAP - bcmFieldQualifyStageEgress */ |
| /* The DPP resources allow only limit number of qset - indexed by dpp_group_id, create qset when necessary */ |
| /* create one if not exist */ |
| if (BCM_E_NOT_FOUND == bcm_field_group_status_get(unit, *p_group_id, &fg_status)) |
| { |
| bcm_field_group_config_t_init(&grp); |
| |
| BCM_FIELD_QSET_INIT(grp.qset); |
| /* TCAM entry limit to 80 bits per slice, but up to 4 slices for a group |
| use ModeAuto to automatically adjust it */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyInPort); /* 32 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyDstMac); /* 48 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifySrcMac); /* 48 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyDstIp); /* 32 bits */ |
| #if (BAL_ACL_FG_SRC_IP_ENABLE == 1) |
| /* save source IP space for other classifier, try to keep total size under 4 slices */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifySrcIp); /* 32 bits */ |
| #endif |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyEtherType); /* 16 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyIpProtocol); /* 8 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyL4DstPort); /* 16 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyL4SrcPort); /* 16 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyOuterVlanId); /* 16 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyOuterVlanPri); /* 8 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyInnerVlanId); /* 16 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyInnerVlanPri); /* 8 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyInVPort); /* 32 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyPacketRes); |
| |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyStageIngress); |
| |
| BCM_FIELD_ASET_INIT(grp.aset); |
| BCM_FIELD_ASET_ADD(grp.aset, bcmFieldActionTrap); |
| BCM_FIELD_ASET_ADD(grp.aset, bcmFieldActionDrop); |
| BCM_FIELD_ASET_ADD(grp.aset, bcmFieldActionRedirect); |
| BCM_FIELD_ASET_ADD(grp.aset, bcmFieldActionVportNew); |
| |
| grp.priority = 12; /* the higher the number the higher the priority */ |
| grp.flags = (BCM_FIELD_GROUP_CREATE_WITH_MODE | BCM_FIELD_GROUP_CREATE_WITH_ASET); |
| grp.mode = bcmFieldGroupModeAuto; |
| |
| ret = bcm_field_group_config_create(unit, &grp); |
| |
| if (ret != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to create field - %d\n", ret ); |
| return BCM_ERR_INTERNAL; |
| } |
| |
| *p_group_id = grp.group; |
| BCM_LOG(INFO, log_id_sw_util, "Field Group %d created\n", *p_group_id); |
| return BCM_ERR_OK; |
| } |
| return BCM_ERR_ALREADY; |
| } |
| |
| /** |
| * @brief Create a default field group in the switch ICAP, |
| * This field group allow flow classifier to create default ACL rules |
| * |
| * @param unit the switch unit this rule is to be added |
| * @param p_group_id a pointer to a variable that return the created group id |
| * @return error code |
| */ |
| static bcmos_errno bal_sw_util_dpp_dft_fg_create(int unit, bcm_field_group_t *p_group_id) |
| { |
| bcm_field_group_status_t fg_status; |
| bcm_field_group_config_t grp; |
| int32_t ret = 0; |
| |
| /* VCAP - bcmFieldQualifyStageLookup, ICAP - bcmFieldQualifyStageIngress, ECAP - bcmFieldQualifyStageEgress */ |
| /* The DPP resources allow only limit number of qset - indexed by dpp_group_id, create qset when necessary */ |
| /* create one if not exist */ |
| if (BCM_E_NOT_FOUND == bcm_field_group_status_get(unit, *p_group_id, &fg_status)) |
| { |
| bcm_field_group_config_t_init(&grp); |
| |
| BCM_FIELD_QSET_INIT(grp.qset); |
| /* TCAM entry limit to 80 bits per slice, but up to 4 slices for a group |
| use ModeAuto to automatically adjust it */ |
| |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyInVPort); /* 32 bits */ |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyPacketRes); |
| |
| BCM_FIELD_QSET_ADD(grp.qset, bcmFieldQualifyStageIngress); |
| |
| BCM_FIELD_ASET_INIT(grp.aset); |
| BCM_FIELD_ASET_ADD(grp.aset, bcmFieldActionDrop); |
| |
| grp.priority = 10; /* the higher the number the higher the priority */ |
| grp.flags = (BCM_FIELD_GROUP_CREATE_WITH_MODE | BCM_FIELD_GROUP_CREATE_WITH_ASET); |
| grp.mode = bcmFieldGroupModeAuto; |
| |
| ret = bcm_field_group_config_create(unit, &grp); |
| |
| if (ret != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " flow fail to create default field - %d\n", ret ); |
| return BCM_ERR_INTERNAL; |
| } |
| |
| *p_group_id = grp.group; |
| BCM_LOG(INFO, log_id_sw_util, "Default Field Group %d created\n", *p_group_id); |
| return BCM_ERR_OK; |
| } |
| return BCM_ERR_ALREADY; |
| } |
| |
| /** |
| * @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 |
| * @param p_flow_elm a pointer to the switch internal flow list |
| * @param in_gport ingress virtual port (LIF) this acl applied |
| * @param out_gport egress virtual port (LIF) this acl applied |
| * @return error code |
| */ |
| static bcmos_errno bal_sw_util_dpp_acl_add(int unit, bcmbal_flow_cfg *p_flow, bal_sw_flow *p_flow_elm, uint32_t in_gport, uint32_t out_gport) |
| { |
| int32_t ret = 0; |
| uint16_t udp_port, flow_pri; |
| bcm_field_entry_t eid; |
| bcm_gport_t trap_gport; |
| bcm_mac_t dst_mac, src_mac; |
| bcm_mac_t mac_mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| uint32_t trap_code = 0; |
| bcmos_errno err; |
| |
| BCM_LOG(INFO, log_id_sw_util, |
| " Add an ACL to a flow w type = %d\n", p_flow->key.flow_type); |
| |
| if(p_flow_elm->num_eid >= MAX_FIELD_EID) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Too many ACL rules in flow id %d\n", p_flow_elm->id); |
| return BCM_ERR_NORES; |
| } |
| |
| err = bal_sw_util_dpp_fg_create(unit, &dpp_group_id); |
| |
| if (BCM_ERR_ALREADY != err && BCM_ERR_OK != err) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Field Group %d create failed\n", dpp_group_id); |
| return err; |
| } |
| |
| /* fill in the match fields */ |
| do |
| { |
| ret = bcm_field_entry_create(unit, dpp_group_id, &eid); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_entry_create failed - %d\n", ret); |
| break; |
| } |
| /* if upstream, match ingress port */ |
| if(p_flow->key.flow_type == BCMBAL_FLOW_TYPE_UPSTREAM && BCMBAL_CFG_PROP_IS_SET(p_flow, flow, access_int_id)) |
| { |
| ret = bcm_field_qualify_InPort(unit, eid, bal_bcm_pon_inf_pbm_get(p_flow->data.access_int_id), 0xffffffff); |
| if (BCM_E_NONE != ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_InPort failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add InPort %d to qualifier\n", bal_bcm_pon_inf_pbm_get(p_flow->data.access_int_id)); |
| } |
| |
| } |
| /* if downstream, match ingress nni port */ |
| if(p_flow->key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM && BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, network_int_id)) |
| { |
| ret = bcm_field_qualify_InPort(unit, eid, bal_bcm_net_inf_pbm_get(p_flow->data.network_int_id), 0xffffffff); |
| if (BCM_E_NONE != ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_InPort failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add network port %d to qualifier\n", bal_bcm_net_inf_pbm_get(p_flow->data.network_int_id)); |
| } |
| } |
| /* match ether type */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, ether_type)) |
| { |
| ret = bcm_field_qualify_EtherType(unit, eid, p_flow->data.classifier.ether_type, 0xffff); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_EtherType failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add ether type 0x%x to qualifier\n", p_flow->data.classifier.ether_type ); |
| } |
| } |
| /* add IP protocol to match rule */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, ip_proto)) |
| { |
| ret = bcm_field_qualify_IpProtocol(unit, eid, p_flow->data.classifier.ip_proto, 0xff); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_IpProtocol failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add ip protocol 0x%x to qualifier\n", p_flow->data.classifier.ip_proto ); |
| } |
| } |
| /* add L3 source port to match rule - Don't be confused by the API name */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, src_port)) |
| { |
| udp_port = p_flow->data.classifier.src_port; |
| ret = bcm_field_qualify_L4SrcPort(unit, eid, udp_port, 0xffff); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_L4SrcPort failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add src port %d to qualifier\n", udp_port ); |
| } |
| } |
| /* add L3 destination port to match rule */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, dst_port)) |
| { |
| udp_port = p_flow->data.classifier.dst_port; |
| ret = bcm_field_qualify_L4DstPort(unit, eid, udp_port, 0xffff); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_L4DstPort failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add dst port %d to qualifier\n", udp_port ); |
| } |
| } |
| /* add Outer vid to match rule */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, o_vid)) |
| { |
| ret = bcm_field_qualify_OuterVlanId(unit, eid, p_flow->data.classifier.o_vid, 0x0fff); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_OuterVlanId failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add outer vid 0x%x to qualifier\n", p_flow->data.classifier.o_vid ); |
| } |
| } |
| /* add Inner vid to match rule */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, i_vid)) |
| { |
| ret = bcm_field_qualify_InnerVlanId(unit, eid, p_flow->data.classifier.i_vid, 0x0fff); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_InnerVlanId failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add Inner vid 0x%x to qualifier\n", p_flow->data.classifier.i_vid ); |
| } |
| } |
| /* add Outer priority to match rule - only accept 3 bits */ |
| /* use ACL for downstream, upstream pbit classification will be done through PON LIF */ |
| if(p_flow->key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM && |
| BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, o_pbits)) |
| { |
| ret = bcm_field_qualify_OuterVlanPri(unit, eid, p_flow->data.classifier.o_pbits, 0x7); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_OuterVlanPri failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add outer pri 0x%x to qualifier\n", p_flow->data.classifier.o_pbits ); |
| } |
| } |
| /* add Inner priority to match rule - only accept 3 bits */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, i_pbits)) |
| { |
| ret = bcm_field_qualify_InnerVlanPri(unit, eid, p_flow->data.classifier.i_pbits, 0x7); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_InnerVlanPri failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add inner pri 0x%x to qualifier\n", p_flow->data.classifier.i_pbits ); |
| } |
| } |
| /* add Dst Mac to match rule */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, dst_mac)) |
| { |
| memcpy(&dst_mac, &(p_flow->data.classifier.dst_mac), sizeof(bcm_mac_t)); |
| ret = bcm_field_qualify_DstMac(unit, eid, dst_mac, mac_mask); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_DstMac failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add DstMac %x:%x:%x:%x:%x:%x to qualifier\n", |
| p_flow->data.classifier.dst_mac.u8[0], |
| p_flow->data.classifier.dst_mac.u8[1], |
| p_flow->data.classifier.dst_mac.u8[2], |
| p_flow->data.classifier.dst_mac.u8[3], |
| p_flow->data.classifier.dst_mac.u8[4], |
| p_flow->data.classifier.dst_mac.u8[5] ); |
| } |
| } |
| /* add Src Mac to match rule */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, src_mac)) |
| { |
| memcpy(&src_mac, &(p_flow->data.classifier.src_mac), sizeof(bcm_mac_t)); |
| ret = bcm_field_qualify_SrcMac(unit, eid, src_mac, mac_mask); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_SrcMac failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add SrcMac[5] 0x%x to qualifier\n", p_flow->data.classifier.src_mac.u8[5] ); |
| } |
| } |
| /* add Dst IP to match rule */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, dst_ip)) |
| { |
| ret = bcm_field_qualify_DstIp(unit, eid, p_flow->data.classifier.dst_ip.u32, 0xffffffff); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_DstIp failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add DstIp 0x%x to qualifier\n", p_flow->data.classifier.dst_ip.u32 ); |
| } |
| } |
| |
| /* add Src IP to match rule */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, src_ip)) |
| { |
| ret = bcm_field_qualify_SrcIp(unit, eid, p_flow->data.classifier.src_ip.u32, 0xffffffff); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_SrcIp failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add SrcIp 0x%x to qualifier\n", p_flow->data.classifier.src_ip.u32 ); |
| } |
| } |
| /* set ACL priority */ |
| if(BCMBAL_CFG_PROP_IS_SET(p_flow, flow, priority)) |
| { |
| flow_pri = p_flow->data.priority; |
| } |
| else /* default to 10, valid range 0 - 65535 */ |
| { |
| flow_pri = BAL_FLOW_DEFAULT_PRIORITY; |
| /* set the presence of priority bit in the flow to reflect insertion of DEFAULT_PRIORITY */ |
| BCMBAL_CFG_PROP_SET(p_flow, flow, priority, flow_pri); |
| } |
| ret = bcm_field_entry_prio_set(unit, eid, flow_pri); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field entry priority failed - %d\n", ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "field entry priority set to - %d\n", flow_pri); |
| } |
| |
| }while(0); |
| |
| if (BCM_E_NONE == ret) /* make sure the field qualifier settings are good */ |
| { |
| if( BCMBAL_CFG_PROP_IS_SET(p_flow, flow, action) && |
| BCMBAL_ACTION_CMD_ID_IS_SET( &(p_flow->data.action), BCMBAL_ACTION_CMD_ID_TRAP_TO_HOST) ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, |
| " Got a flow action cmd=0x%x \n", p_flow->data.action.cmds_bitmask); |
| do |
| { |
| ret = bal_sw_util_create_trap_gport(unit, &trap_gport, &trap_code); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "create_trap_gport failed - %d\n", ret); |
| break; |
| } |
| /* qualify with in LIF */ |
| if(in_gport) |
| { |
| /* add vPort to match rule */ |
| ret = bcm_field_qualify_InVPort32(unit, eid, in_gport, 0xffff); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_InVPort failed - %d\n", ret); |
| break; |
| } |
| } |
| ret = bcm_field_action_add(unit, eid, bcmFieldActionTrap, trap_gport, 0); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field action add failed - %d\n", ret); |
| break; |
| }; |
| ret = bcm_field_entry_install(unit, eid); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_entry_install %d failed - %d\n", eid, ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add rule %d Success\n", eid); |
| } |
| |
| }while(0); |
| } |
| /* regular forwarding ACL */ |
| else |
| { |
| do |
| { |
| /* redirect to the egress LIF */ |
| ret = bcm_field_action_add(unit, eid, bcmFieldActionRedirect, 0, out_gport); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field action add failed - %d\n", ret); |
| break; |
| }; |
| /* trigger vlan translation at the egress LIF */ |
| ret = bcm_field_action_add(unit, eid, bcmFieldActionVportNew, out_gport, 0); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field action add new vport failed - %d\n", ret); |
| break; |
| }; |
| |
| ret = bcm_field_entry_install(unit, eid); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_entry_install %d failed - %d\n", eid, ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add rule %d to gport 0x%x Success\n", eid, out_gport); |
| } |
| }while(0); |
| } |
| } |
| |
| if (ret != BCM_E_NONE) |
| { |
| /* TBD - release the eid and trap port resource */ |
| return BCM_ERR_INTERNAL; |
| } |
| else |
| { |
| /* add flow info into the internal link list */ |
| p_flow_elm->trap_code = trap_code; |
| p_flow_elm->trap_port = trap_gport; |
| p_flow_elm->field_entry_id[p_flow_elm->num_eid] = eid; |
| p_flow_elm->num_eid += 1; |
| p_flow_elm->valid = 1; |
| return BCM_ERR_OK; |
| } |
| } |
| |
| /** |
| * @brief The default acl add function add a DROP Access Control Rule on the ingress LIF |
| * |
| * @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 |
| * @param p_flow_elm a pointer to the switch internal flow list |
| * @param in_gport ingress virtual port (LIF) this acl applied |
| * @return error code |
| */ |
| static bcmos_errno bal_sw_util_dpp_dft_acl_add(int unit, bcmbal_flow_cfg *p_flow, bal_sw_flow *p_flow_elm, uint32_t in_gport) |
| { |
| bcmos_errno err; |
| bcm_field_entry_t dft_eid = 0; |
| int32_t ret = 0; |
| |
| if(p_flow_elm->num_eid >= MAX_FIELD_EID) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Too many dft ACL rules in flow id %d\n", p_flow_elm->id); |
| return BCM_ERR_NORES; |
| } |
| |
| err = bal_sw_util_dpp_dft_fg_create(unit, &dpp_group_id); |
| |
| if (BCM_ERR_ALREADY != err && BCM_ERR_OK != err) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Field Default Group %d create failed\n", dpp_dft_group_id); |
| return err; |
| } |
| do |
| { |
| /* Install a default drop ACL at lowest priority(0). |
| This will drop unmatch packets received on the same ingress LIF |
| */ |
| |
| ret = bcm_field_entry_create(unit, dpp_group_id, &dft_eid); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_entry_create failed - %d\n", ret); |
| break; |
| } |
| |
| /* add vPort to match rule */ |
| ret = bcm_field_qualify_InVPort32(unit, dft_eid, in_gport, 0xffff); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_InVPort failed - %d\n", ret); |
| break; |
| } |
| |
| /* add Packet type to match rule, drop only unicast */ |
| ret = bcm_field_qualify_PacketRes(unit, dft_eid, BCM_FIELD_PKT_RES_L2UC, 0); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_qualify_PacketRes failed - %d\n", ret); |
| break; |
| } |
| |
| |
| /* set to lowest priority 0 */ |
| ret = bcm_field_entry_prio_set(unit, dft_eid, 0); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field entry priority failed - %d\n", ret); |
| break; |
| }; |
| |
| /* Drop the packet */ |
| ret = bcm_field_action_add(unit, dft_eid, bcmFieldActionDrop, 0, 0); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field action add failed - %d\n", ret); |
| break; |
| }; |
| ret = bcm_field_entry_install(unit, dft_eid); |
| if (ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "field_entry_install %d failed - %d\n", dft_eid, ret); |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Add default rule %d Success\n", dft_eid); |
| } |
| }while(0); |
| if (ret != BCM_E_NONE) |
| { |
| return BCM_ERR_INTERNAL; |
| } |
| else |
| { |
| p_flow_elm->field_entry_id[p_flow_elm->num_eid] = dft_eid; |
| p_flow_elm->num_eid += 1; |
| return BCM_ERR_OK; |
| } |
| } |
| |
| /** |
| * @brief The ingress vlan translation function modified the packet VLAN tag |
| * before sending it out |
| * |
| * @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 |
| * @param ingport the gport the translation will be applied |
| * @return error code |
| */ |
| static bcmos_errno bal_sw_util_dpp_invlanx(int unit, bcmbal_flow_cfg *p_flow, uint32_t ingport) |
| { |
| int rv = 0; |
| bcm_vlan_action_set_t action; |
| bcm_vlan_action_set_t_init(&action); |
| bcmos_errno ret; |
| |
| if(BCMBAL_ACTION_CMD_ID_IS_SET( &(p_flow->data.action), BCMBAL_ACTION_CMD_ID_REMARK_PBITS)) |
| { |
| int qos_map_id, i_pri, o_pri; |
| |
| do |
| { |
| switch(p_flow->data.classifier.pkt_tag_type) |
| { |
| case BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG: |
| /* should be |
| action.dt_outer_pkt_prio = bcmVlanActionReplace; |
| action.new_outer_vlan = p_flow->data.classifier.o_vid;; |
| action.dt_outer = bcmVlanActionReplace; |
| but it does not seems to work. Using ot_outer seems to also work for double tagged packets |
| */ |
| action.ot_outer_pkt_prio = bcmVlanActionReplace; |
| action.new_outer_vlan = p_flow->data.classifier.o_vid; |
| action.ot_outer = bcmVlanActionReplace; |
| break; |
| case BCMBAL_PKT_TAG_TYPE_SINGLE_TAG: |
| action.ot_outer_pkt_prio = bcmVlanActionReplace; |
| action.new_outer_vlan = p_flow->data.classifier.o_vid; |
| action.ot_outer = bcmVlanActionReplace; |
| |
| break; |
| default: |
| BCM_LOG(ERROR, log_id_sw_util, |
| "Error, pbits translation on untagged packets\n"); |
| return BCM_ERR_NOT_SUPPORTED; |
| break; |
| } |
| |
| |
| /* perform o_pbits translation */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, o_pbits)) |
| { |
| i_pri = p_flow->data.classifier.o_pbits; |
| } |
| else |
| { |
| /* mark the source pcp DONTCARE */ |
| i_pri = -1; |
| } |
| /* flow validation already make sure the action.o_pbits is set for REMARK_PBITS */ |
| o_pri = p_flow->data.action.o_pbits; |
| |
| ret = bal_sw_dpp_pcp_remark_map_get(i_pri, o_pri, &qos_map_id); |
| |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| "Error, faile to obtain pcp map id, ret = %d\n", ret); |
| rv = BCM_E_UNAVAIL; |
| break; |
| } |
| |
| rv = bcm_qos_port_map_set(unit, ingport, qos_map_id, -1); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| "Error, pbits translation bcm_qos_port_map_set ret = %d\n", rv); |
| break; |
| } |
| |
| /* set the action priority profile */ |
| action.priority = qos_map_id; |
| action.new_inner_pkt_prio = qos_map_id; |
| }while(0); |
| } |
| else |
| { |
| /* nothing to do, return OK */ |
| return BCM_ERR_OK; |
| } |
| if(rv != BCM_E_NONE) |
| { |
| return BCM_ERR_INTERNAL; |
| } |
| |
| rv = bcm_vlan_translate_action_create(unit, ingport, bcmVlanTranslateKeyPortOuter, BCM_VLAN_INVALID, BCM_VLAN_NONE, &action); |
| if( rv ) |
| { |
| return BCM_ERR_INTERNAL; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Perform ingress vlan translation w %s action on gport 0x%x\n", |
| (p_flow->data.classifier.pkt_tag_type == BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG)? "DT": "ST", |
| ingport); |
| return BCM_ERR_OK; |
| } |
| } |
| /** |
| * @brief The egress vlan translation function modified the packet VLAN tag |
| * before sending it out |
| * |
| * @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 |
| * @param egport the gport the translation will be applied |
| * @return error code |
| */ |
| static bcmos_errno bal_sw_util_dpp_evlanx(int unit, bcmbal_flow_cfg *p_flow, uint32_t egport) |
| { |
| int rv = 0; |
| bcm_vlan_action_set_t action; |
| bcm_vlan_action_set_t_init(&action); |
| |
| if(BCMBAL_ACTION_CMD_ID_IS_SET( &(p_flow->data.action), BCMBAL_ACTION_CMD_ID_ADD_OUTER_TAG)) |
| { |
| action.new_outer_vlan = p_flow->data.action.o_vid; |
| action.priority = 0; |
| switch(p_flow->data.classifier.pkt_tag_type) |
| { |
| case BCMBAL_PKT_TAG_TYPE_UNTAGGED: |
| action.ut_outer = bcmVlanActionAdd; |
| action.ut_outer_pkt_prio = bcmVlanActionAdd; |
| break; |
| case BCMBAL_PKT_TAG_TYPE_SINGLE_TAG: |
| action.ot_outer = bcmVlanActionAdd; |
| action.ot_outer_pkt_prio = bcmVlanActionAdd; |
| break; |
| default: |
| action.new_outer_vlan = 0; |
| action.ot_outer = bcmVlanActionNone; |
| break; |
| } |
| |
| if( p_flow->data.action.o_tpid ) |
| { |
| action.outer_tpid_action = bcmVlanTpidActionModify; |
| action.outer_tpid = p_flow->data.action.o_tpid; |
| } |
| |
| } |
| else if(BCMBAL_ACTION_CMD_ID_IS_SET( &(p_flow->data.action), BCMBAL_ACTION_CMD_ID_REMOVE_OUTER_TAG)) |
| { |
| switch(p_flow->data.classifier.pkt_tag_type) |
| { |
| case BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG: |
| /* should be action.dt_outer = bcmVlanActionDelete, |
| but it does not seems to work. Using ot_outer seems to also work for double tagged packets */ |
| action.ot_outer = bcmVlanActionDelete; |
| break; |
| case BCMBAL_PKT_TAG_TYPE_SINGLE_TAG: |
| action.ot_outer = bcmVlanActionDelete; |
| break; |
| default: |
| action.ut_outer = bcmVlanActionNone; |
| break; |
| } |
| } |
| else if (BCMBAL_ACTION_CMD_ID_IS_SET( &(p_flow->data.action), BCMBAL_ACTION_CMD_ID_XLATE_OUTER_TAG)) |
| { |
| action.new_outer_vlan = p_flow->data.action.o_vid; |
| switch(p_flow->data.classifier.pkt_tag_type) |
| { |
| case BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG: |
| /* should be action.dt_outer = bcmVlanActionReplace, |
| but it does not seems to work. Using ot_outer seems to also work for double tagged packets */ |
| action.ot_outer = bcmVlanActionReplace; |
| break; |
| case BCMBAL_PKT_TAG_TYPE_SINGLE_TAG: |
| action.ot_outer = bcmVlanActionReplace; |
| break; |
| default: |
| action.ut_outer = bcmVlanActionNone; |
| break; |
| } |
| |
| /** @todo tpid is not translated for now */ |
| /** @note the vid translated from is passed as a parameter to the api call */ |
| |
| } |
| else |
| { |
| /* nothing to do, return OK */ |
| return BCM_ERR_OK; |
| } |
| |
| rv = bcm_vlan_translate_egress_action_add(unit, egport, BCM_VLAN_NONE, BCM_VLAN_NONE, &action); |
| if( rv ) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "bcm_vlan_translate_egress_action_add failed %d on gport 0x%x\n", rv, egport); |
| return BCM_ERR_INTERNAL; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Perform egress vlan translation on gport 0x%x\n", egport); |
| return BCM_ERR_OK; |
| } |
| } |
| /** |
| * @brief Multicast flow add function forward the specified multicast packets to a multicast group. |
| * The multicast group has to be created prio to the mc_flow add operation |
| * |
| * @param p_flow a pointer to the flow definition the created rule will be based on |
| * @param service_type id this multicast flow is for multicast or downstream N:1 service |
| * @return error code |
| */ |
| static bcmos_errno bal_sw_util_dpp_mc_flow_add(bcmbal_flow_cfg *p_flow, uint32_t service_type) |
| { |
| bcmos_errno ret = BCM_ERR_OK; |
| bcm_gport_t nni_gport; |
| int ii, unit, rv; |
| int nni; |
| bcm_vlan_t vsi; |
| bal_sw_flow *p_flow_elm; |
| bal_sw_flow flow_elm; |
| bal_sw_vsi_service *p_vsi_svc; |
| bal_sw_group_list *p_group_list; |
| uint32_t svc_tag_indx; |
| |
| /* get a pointer to the specified group instance */ |
| p_group_list = bal_sw_util_dpp_group_list_get_by_id(p_flow->data.group_id); |
| |
| if ( p_group_list == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: group %d not found\n", p_flow->data.group_id); |
| return BCM_ERR_INTERNAL; |
| } |
| |
| unit = p_group_list->device; |
| |
| /* make sure this flow id does not already exist */ |
| p_flow_elm = bal_sw_util_flow_list_get_by_id(p_flow->key.flow_id); |
| if (p_flow_elm) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: flow id %d type %d already exist\n",p_flow->key.flow_id, p_flow->key.flow_type); |
| return BCM_ERR_INTERNAL; |
| } |
| |
| do |
| { |
| /* initialize link list flow element */ |
| memset(&flow_elm, 0, sizeof (bal_sw_flow)); |
| /* fill in the basic info */ |
| flow_elm.id = p_flow->key.flow_id; |
| flow_elm.device = unit; |
| flow_elm.group_id = p_flow->data.group_id; |
| flow_elm.flow_cookie = p_flow->data.cookie; |
| |
| /* packet tag type are required fields when create NNI LIF */ |
| /* Here make sure it is the supported type */ |
| if(p_flow->data.classifier.pkt_tag_type != BCMBAL_PKT_TAG_TYPE_UNTAGGED && |
| p_flow->data.classifier.pkt_tag_type != BCMBAL_PKT_TAG_TYPE_SINGLE_TAG && |
| p_flow->data.classifier.pkt_tag_type != BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: MC classifier packet tag type 0x%x is invalid\n", p_flow->data.classifier.pkt_tag_type); |
| ret = BCM_ERR_PARM; |
| break; |
| } |
| |
| /* retrieve the vswitch instance */ |
| p_vsi_svc = p_group_list->p_vsi; |
| if(p_vsi_svc == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, MC: vsi not created\n"); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| vsi = p_vsi_svc->vswitch; |
| |
| /* add flow to the vsi service tag list */ |
| ret = bal_sw_util_dpp_vsi_service_tag_add(unit, p_vsi_svc, p_flow, &svc_tag_indx); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to add flow to vsi service tag list\n"); |
| break; |
| } |
| |
| ii = 0; /* Start with the first NNI logical interface */ |
| /* loop through all nni port, add them to vswitch, -1 indicate end of table */ |
| /* Configure the switch nni LIF based on classifier in the flow. */ |
| while(-1 != (nni = bal_bcm_net_inf_pbm_get(ii))) |
| { |
| /* skip nni port that is not in the same device or not valid */ |
| if ( bal_bcm_net_inf_dev_get(ii) != unit || |
| BCMOS_FALSE == bcm_topo_nni_is_valid(ii)) |
| { |
| ii++; |
| continue; |
| } |
| |
| /* if nni port is specified in the flow, skip the other nni ports */ |
| if ( BCMBAL_CFG_PROP_IS_SET(p_flow, flow, network_int_id) && |
| ii != p_flow->data.network_int_id |
| ) |
| { |
| ii++; |
| continue; |
| } |
| |
| /* create gport based on nni physical port number */ |
| ret = bal_sw_util_dpp_vsi_service_port_add(unit, p_vsi_svc, svc_tag_indx, nni, &nni_gport); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to add nni %d to vsi\n", nni); |
| break; |
| } |
| |
| /* if DMAC is set, create a L2 entry, otherwise flood unknown to the group */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, dst_mac)) |
| { |
| /* add static MC 01:00:5E:00:00:start_mc to the dowstream MC group */ |
| bcm_l2_addr_t l2addr; |
| bcm_mac_t mc_mac; |
| |
| /* Add mc mac address */ |
| memcpy(&mc_mac, &(p_flow->data.classifier.dst_mac), sizeof(bcm_mac_t)); |
| |
| bcm_l2_addr_t_init(&l2addr, mc_mac, vsi); |
| l2addr.flags = BCM_L2_STATIC | BCM_L2_MCAST; |
| /* direct output to the MC group */ |
| l2addr.l2mc_group = p_group_list->l2_grp_id; |
| |
| rv = bcm_l2_addr_add(unit, &l2addr); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util,"Error: bcm_l2_addr_add MC returned %s \n", |
| bcm_errmsg(rv)); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| } |
| else |
| { |
| rv = bcm_port_control_set(unit, nni_gport, bcmPortControlFloodUnknownMcastGroup, BAL_DPP_MC_OFFSET); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, MC: bcm_port_control_set for nni failed %d\n", rv); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| } |
| |
| flow_elm.num_net++; |
| flow_elm.net_port[ii] = nni_gport; |
| |
| ii++; /* Next NNI */ |
| } |
| /* skip the rest if anything goes wrong */ |
| if( ret != BCM_ERR_OK) |
| { |
| break; |
| } |
| /* perform vlan translation on nni port */ |
| if (BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, action)) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, "Warning, action for multicast flow not supported\n"); |
| } |
| |
| } while(0); |
| |
| if( ret == BCM_ERR_OK) |
| { |
| /* increase the group reference count */ |
| p_group_list->use_count++; |
| /* increase the vsi reference count */ |
| p_vsi_svc->use_count++; |
| /* add the flow info to the flow link list */ |
| flow_elm.p_vsi_svc = p_vsi_svc; |
| flow_elm.num_pon = 0; |
| flow_elm.type = service_type; |
| /* nni ports are done in the nni loop */ |
| flow_elm.valid = 1; |
| bal_sw_util_flow_list_insert(flow_elm); |
| |
| BCM_LOG(INFO, log_id_sw_util, "Add flow_elm %d type %d, vswitch 0x%x, group_id %d Success\n", flow_elm.id, flow_elm.type, p_vsi_svc->vswitch, flow_elm.group_id); |
| } |
| return ret; |
| } |
| |
| /* HW limitation on max burst rate - bps */ |
| #define BAL_MAX_BURST_RATE (1 << 16) |
| |
| /** |
| * @brief The flow add function program DPP to forward packets that have |
| * specified attributes to the designated ports. |
| * The packets is modified before egress |
| * In case IWF is in DIRECT MODE, |
| * On the downstream, a tunnel id (outer vlan tag) is added to the packets |
| * On the upstream, outer vlan tag (tunnel id) is removed from the packets |
| * In case IWF is in PER_FLOW_MODE, currently not supported |
| * |
| * @note a flow can be created which does VID translation between V & U (refer to TR156) interfaces. |
| * |
| * @note This routine adds flow for both upstream and downstream direction i.e. create PON & NNI LIFs |
| * for both directions in a same call to this routine - and all this as part of a single call to this function |
| * from the application module or CLI. |
| * It also programs the egress vlan translation modules for both upstream and downstream. |
| * |
| * @note The general sequence is: |
| * \li create PON LIF with matching ingress tunnel id (and vlan id, if upstream pkts are vlan tagged) |
| * \li next, configure egress vlan translation on PON side (for downstream pkts) |
| * \li create NNI LIF with matching ingress vlan id (currently by default it assumes downstream packets are tagged) |
| * \li next, configure egress vlan translation on NNI side (for upstream pkts) |
| * |
| * @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_dpp_flow_add(bcmbal_iwf_mode iwf_mode, bcmbal_flow_cfg *p_flow) |
| { |
| bcmos_errno ret = BCM_ERR_OK; |
| bcm_gport_t pon_gport, nni_gport; |
| bcm_vlan_t tunnel_id; |
| int ii, unit, rv; |
| bcm_vlan_port_t vp; |
| int pon, nni, sys_pon; |
| bcm_vlan_t vsi; |
| int pon_encap_id; |
| bal_sw_flow *p_flow_elm; |
| uint32_t flow_type, max_burst, svc_tag_indx; |
| bal_sw_flow flow_elm; |
| bal_sw_dpp_qos_service_cfg *p_service_cfg = NULL; |
| bcmbal_flow_cfg flow_rev; /* used for configuration in opposite direction */ |
| bcmbal_flow_cfg *p_us_flow, *p_ds_flow; |
| bal_sw_vsi_service *p_vsi_svc; |
| |
| BCM_LOG(INFO, log_id_sw_util, |
| " Got an DPP flow request - iwf_mode=%d flow_id=%d flow_type=%d, sub_port=%d svc_id=%d\n", |
| iwf_mode, |
| p_flow->key.flow_id, p_flow->key.flow_type, p_flow->data.access_int_id, p_flow->data.svc_port_id); |
| BCM_LOG(DEBUG, log_id_sw_util, |
| " classifier - mask=0x%x otpid=%x itpid=%x ovid=%x ivid=%x\n", |
| 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); |
| |
| |
| if ( iwf_mode != BCMBAL_IWF_MODE_DIRECT_MAPPING) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: not supported IWF mode - %d\n", iwf_mode); |
| return BCM_ERR_NOT_SUPPORTED; |
| } |
| |
| if (BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, action)) |
| { |
| BCM_LOG(DEBUG, log_id_sw_util, |
| " action.params: .cmds_bitmask=0x%x, .input_pkt_tag_type=0x%x\n", |
| p_flow->data.action.cmds_bitmask, |
| p_flow->data.classifier.pkt_tag_type); |
| } |
| |
| if (p_flow->key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM && |
| BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, sla) && |
| p_flow->data.sla.max_rate < p_flow->data.sla.min_rate) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: Max rate %d kbps is not >= Min rate %d kbps\n", p_flow->data.sla.max_rate, p_flow->data.sla.min_rate ); |
| return BCM_ERR_NOT_SUPPORTED; |
| } |
| |
| switch(p_flow->key.flow_type) |
| { |
| case BCMBAL_FLOW_TYPE_DOWNSTREAM: |
| flow_type = BAL_SW_FLOW_TYPE_DOWNSTREAM; |
| /* for downstream N:1 service, same flood settings as multicast flow */ |
| if(BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, group_id)) |
| { |
| return bal_sw_util_dpp_mc_flow_add(p_flow, BAL_SW_FLOW_TYPE_DOWNSTREAM); |
| } |
| break; |
| case BCMBAL_FLOW_TYPE_UPSTREAM: |
| flow_type = BAL_SW_FLOW_TYPE_UPSTREAM; |
| break; |
| case BCMBAL_FLOW_TYPE_MULTICAST: |
| return bal_sw_util_dpp_mc_flow_add(p_flow, BCMBAL_FLOW_TYPE_MULTICAST); |
| default: |
| return BCM_ERR_NOT_SUPPORTED; |
| } |
| |
| unit = bal_bcm_pon_inf_dev_get(p_flow->data.access_int_id); |
| /* We program a flow with 3 switch components, inLIF, vSwitch and outLIF. |
| LIF is always bi-directional operation, so for uni-directional flow we programmed an ingress or egress DROP |
| on PON based on the BAL flow direction. Later, when the BAL flow from the other direction is SET, we |
| remove the DROP from the PON. |
| This imply that two BAL flows with same id must have symmetry vlan manipulation operations. |
| */ |
| p_flow_elm = bal_sw_util_flow_list_get_by_id(p_flow->key.flow_id); |
| if (p_flow_elm) |
| { |
| /* Check if flow with same id and type exist or not. |
| If downstream exist, remove the DISCARD_INGRESS from PON port by set it to DISCARD_NONE. |
| If upstream exist, remove the DISCARD_EGRESS from PON port by set it to DISCARD_NONE. |
| Asymmetry operations need two flows with different ID - each flow in uni-direction */ |
| if(!(p_flow_elm->type & flow_type)) |
| { |
| bcmbal_flow_cfg *p_ref_flow = NULL; |
| bcmbal_flow_key key; |
| /* retrieve the classifier on the other flow direction */ |
| key.flow_id = p_flow->key.flow_id; |
| key.flow_type = (p_flow->key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM)? BCMBAL_FLOW_TYPE_UPSTREAM : BCMBAL_FLOW_TYPE_DOWNSTREAM; |
| p_ref_flow = flow_get_current_info_by_key(key); |
| if(NULL == p_ref_flow) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: Can't locate flow %d type %d info from main DB\n", key.flow_id, key.flow_type); |
| return BCM_ERR_INTERNAL; |
| } |
| /* check the symmetrical properties between two flows */ |
| if (BCMOS_FALSE == bal_sw_util_is_symmetry_flows(p_flow, p_ref_flow)) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: Flow %d does not have symmetry classifiers\n", key.flow_id); |
| return BCM_ERR_INTERNAL; |
| } |
| |
| /* if there is SLA in downstream, remove the US flow ( LIFs & VSWITCH) and build a new bi-direction flow. |
| This is because LIF is always bi-directional and when the US flow is created the PON LIF has no downstream VOQ. |
| One needs to create a new PON LIF with VOQ for SLA. There is no easy way to add VOQ to an existing LIF. */ |
| if( p_flow->key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM && |
| BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, sla) ) |
| { |
| /* remove the existing US flow */ |
| ret = bal_sw_util_dpp_flow_remove_int(p_flow_elm); |
| if(ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: fail to remove flow %d from the list\n", p_flow->key.flow_id); |
| |
| return BCM_ERR_INTERNAL; |
| } |
| flow_type = BAL_SW_FLOW_TYPE_DOWNSTREAM | BAL_SW_FLOW_TYPE_UPSTREAM; |
| /* goto regular path to create bi-directional flow */ |
| } |
| /* if no SLA and the classifier is more than just port + VLAN tags, add ACL rule to re-direct packet to egress port. |
| This bypass the ingress LIF as LIF check VID only. |
| Upstream classification is expected to be done in ONU (onu add GEM based on classifiers for upstream), |
| return success after complete as there is no other changes needed for existing flow. |
| */ |
| else |
| { |
| int i; |
| bcmos_bool is_nni_set = BCMBAL_CFG_PROP_IS_SET(p_flow, flow, network_int_id); |
| |
| if( p_flow->key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM && |
| bal_sw_util_flow_ds_acl_cls_chk(p_flow) == BCMOS_TRUE ) |
| { |
| for(i=0; i < p_flow_elm->num_pon; i++) |
| { |
| for(ii=0; ii < p_flow_elm->num_net; ii++) |
| { |
| /*first get the ING logical port from gport */ |
| bcm_vlan_port_t tmp_vp; |
| bcm_vlan_port_t_init(&tmp_vp); |
| tmp_vp.vlan_port_id = p_flow_elm->net_port[ii]; |
| ret = bcm_vlan_port_find(unit, &tmp_vp); |
| if(ret != BCM_ERR_OK) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, |
| " Warning: fail to retrieve logical port from gport 0x%x\n", p_flow_elm->net_port[ii]); |
| continue; |
| } |
| /* if nni port is specified in the flow, skip the other nni ports */ |
| if ( BCMOS_TRUE == is_nni_set && tmp_vp.port != bal_bcm_net_inf_pbm_get(p_flow->data.network_int_id)) |
| { |
| ii++; |
| /* if nni is set, it has to be in the nni list of the opposit FLOW */ |
| /* set the return to BCM_ERR_INTERNAL, in case it reach the end of for-loop. */ |
| ret = BCM_ERR_INTERNAL; |
| continue; |
| } |
| /* Rule need to match the ingress port, otherwise, it will apply to all ports - nni and pon */ |
| if ( BCMOS_TRUE != is_nni_set) |
| { |
| BCMBAL_CFG_PROP_SET(p_flow, flow, network_int_id, bal_bcm_net_inf_idx_get(tmp_vp.port)); |
| } |
| ret = bal_sw_util_dpp_acl_add(unit, p_flow, p_flow_elm, p_flow_elm->net_port[ii], p_flow_elm->pon_port[i]); |
| if ( BCMOS_TRUE != is_nni_set) |
| { |
| BCMBAL_CFG_PROP_CLEAR(p_flow, flow, network_int_id); |
| } |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to add forwarding acl on nni %d, ret = %d\n", ret, ii); |
| break; |
| } |
| /* add a dft drop rule (priority 0) on the LIF to drop packets that match VID but no match for other fields */ |
| ret = bal_sw_util_dpp_dft_acl_add(unit, p_flow, p_flow_elm, p_flow_elm->net_port[ii]); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to add default acl on nni=0x%x, ret = %d\n", p_flow_elm->net_port[ii], ret); |
| break; |
| } |
| } |
| if (ret != BCM_ERR_OK) |
| { |
| break; |
| } |
| } |
| |
| if(ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: fail to add DS ACL to nni_%d in flow id %d from the list\n", ii,p_flow->key.flow_id); |
| |
| return BCM_ERR_INTERNAL; |
| } |
| } |
| /* If the US and DS has asymmetrical nature, we only program common part (VID) when create the LIF |
| We need an ACL rule to perform upstream specific classification and forwarding. |
| In addition, ACL allow upstream flows to be prioritized based on priority field.*/ |
| if( p_flow->key.flow_type == BCMBAL_FLOW_TYPE_UPSTREAM && |
| bal_sw_util_flow_us_acl_cls_chk(p_flow) == BCMOS_TRUE && |
| /* there are issues using traditional API for PON application when egress vlan translation and ingress ACL are both active. |
| Enable the ACL only when there is no vlan action - TBD */ |
| BCMOS_FALSE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, action)) |
| { |
| if ( BCMOS_FALSE == is_nni_set) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: Upstream classifier in addition to VID without setting NNI is not supported\n"); |
| return BCM_ERR_INTERNAL; |
| } |
| |
| for(i=0; i < p_flow_elm->num_pon; i++) |
| { |
| ret = bal_sw_util_dpp_acl_add(unit, p_flow, p_flow_elm, p_flow_elm->pon_port[i], p_flow_elm->net_port[p_flow->data.network_int_id]); |
| |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to add forwarding acl on pon %d, ret = %d\n", ret, i); |
| break; |
| } |
| /* add a default drop rule (priority 0) on the LIF to drop packets that match VID but no match for other fields */ |
| ret = bal_sw_util_dpp_dft_acl_add(unit, p_flow, p_flow_elm, p_flow_elm->pon_port[i]); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to add default acl on pon=0x%x, ret = %d\n", p_flow_elm->pon_port[i], ret); |
| break; |
| } |
| } |
| |
| if(ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: fail to add US ACL to pon_%d in flow id %d from the list\n", i, p_flow->key.flow_id); |
| return BCM_ERR_INTERNAL; |
| } |
| } |
| |
| BCM_LOG(INFO, log_id_sw_util, |
| " remove packet discard function from PON in flow id %d \n",p_flow->key.flow_id); |
| /* remove the PON ports from DISCARD_XXX mode */ |
| for(ii=0; ii<p_flow_elm->num_pon; ii++) |
| { |
| if(p_flow_elm->pon_port[ii] == 0) |
| { |
| /* if PON LIF is deleted, then continue to the next one */ |
| continue; |
| } |
| ret = bcm_port_discard_set(p_flow_elm->device, p_flow_elm->pon_port[ii], BCM_PORT_DISCARD_NONE); |
| if (ret != BCM_ERR_OK) |
| { |
| break; |
| } |
| } |
| if(ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: fail to remove packet discard from pon_%d in flow id %d from the list\n", ii,p_flow->key.flow_id); |
| |
| ret = BCM_ERR_INTERNAL; |
| } |
| else |
| { |
| /* add flow type to the exist list */ |
| p_flow_elm->type |= flow_type; |
| ret = BCM_ERR_OK; |
| } |
| return ret; |
| } |
| } |
| else /* same id and type is an error - we do not check if contents are the same or not */ |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: flow id %d type %d already exist\n",p_flow->key.flow_id, p_flow->key.flow_type); |
| return BCM_ERR_INTERNAL; |
| } |
| } |
| |
| /* |
| * 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 no local PON . |
| */ |
| 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; |
| } |
| |
| /* initialize link list flow element */ |
| memset(&flow_elm, 0, sizeof (bal_sw_flow)); |
| /* fill in the basic info */ |
| flow_elm.id = p_flow->key.flow_id; |
| flow_elm.device = unit; |
| flow_elm.type = flow_type; |
| flow_elm.svc_port = p_flow->data.svc_port_id; |
| flow_elm.flow_cookie = p_flow->data.cookie; |
| |
| tunnel_id = (bcm_vlan_t)p_flow->data.svc_port_id; |
| pon = bal_bcm_pon_inf_pbm_get(p_flow->data.access_int_id); |
| |
| /* Map the tunnel ID to a PON channel OTM port */ |
| rv = bcm_port_pon_tunnel_map_set(unit, |
| pon, |
| tunnel_id, |
| pon); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| "Error, bcm_port_pon_tunnel_map_set on pon %d failed %d" |
| " (have you chosen the correct intf_maptable?)\n", pon, rv); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| |
| /* For TRAP to HOST action, it can be either upstream or downsteram, |
| There is no packet manipulation, just insert an ACL and return. |
| For flow action equal TRAP_TO_HOST, DS flow has to use different flow id even if the classifier is the same as the US. |
| This is to simplify the REMOVE/DELETE operation in the switch. |
| */ |
| if ((BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, action)) && |
| (p_flow->data.action.cmds_bitmask & BCMBAL_ACTION_CMD_ID_TRAP_TO_HOST) |
| ) |
| { |
| /* For US, create an in LIF for matching, this allow trapping of different tunnel_id on the same PON. |
| Tunnel_id is stripped before packets are forwarding to the host */ |
| if(p_flow->key.flow_type == BCMBAL_FLOW_TYPE_UPSTREAM) |
| { |
| if(BCMOS_FALSE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, pkt_tag_type)) |
| { |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow->data.classifier, classifier, pkt_tag_type, BCMBAL_PKT_TAG_TYPE_UNTAGGED); |
| BCM_LOG(WARNING, log_id_sw_util, "Tag Type for Upstream Packet Trapping is not set, default to UNTAGGED\n"); |
| } |
| |
| bcm_vlan_port_t_init(&vp); |
| |
| /* preserve any incoming packet vlan tags */ |
| vp.flags = BCM_VLAN_PORT_OUTER_VLAN_PRESERVE | BCM_VLAN_PORT_INNER_VLAN_PRESERVE | BCM_VLAN_PORT_FORWARD_GROUP; |
| |
| switch(p_flow->data.classifier.pkt_tag_type) |
| { |
| case BCMBAL_PKT_TAG_TYPE_SINGLE_TAG: |
| /* match both tunnel and outer tag on ingress PON */ |
| vp.criteria = BCM_VLAN_PORT_MATCH_PORT_TUNNEL_VLAN; |
| break; |
| case BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG: |
| /* match tunnel and both outer and inner tags on ingress PON */ |
| vp.criteria = BCM_VLAN_PORT_MATCH_PORT_TUNNEL_VLAN_STACKED; |
| break; |
| case BCMBAL_PKT_TAG_TYPE_UNTAGGED: |
| default: /* just to make compiler happy */ |
| /* match tunnel tag on ingress PON */ |
| vp.criteria = BCM_VLAN_PORT_MATCH_PORT_TUNNEL; |
| break; |
| } |
| |
| vp.port = pon; |
| vp.match_tunnel_value = tunnel_id; |
| vp.egress_tunnel_value = tunnel_id; |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, o_vid)) |
| { |
| vp.match_vlan = p_flow->data.classifier.o_vid; |
| } |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, i_vid)) |
| { |
| vp.match_inner_vlan = p_flow->data.classifier.i_vid; |
| } |
| vp.vsi = 0; /* will be populated when the gport is added to service, using vswitch_port_add */ |
| |
| /* Create the vlan port (i.e., PON LIF) */ |
| rv = bcm_vlan_port_create(unit, &vp); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_vlan_port_create pon %d failed %d\n", pon, rv); |
| return BCM_ERR_INTERNAL; |
| } |
| ret = bal_sw_util_dpp_acl_add(unit, p_flow, &flow_elm, vp.vlan_port_id, 0); |
| } |
| else |
| { |
| ret = bal_sw_util_dpp_acl_add(unit, p_flow, &flow_elm, 0, 0); |
| } |
| |
| if(BCM_ERR_OK != ret) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, " ERROR: fail to add %s acl rule on trap\n", (p_flow->key.flow_type == BCMBAL_FLOW_TYPE_UPSTREAM)? "US":"DS" ); |
| } |
| else |
| { |
| ret = bal_sw_util_flow_list_insert(flow_elm); |
| BCM_LOG(INFO, log_id_sw_util, "Add flow_elm %d type %d, trap_code 0x%x, eid=%d Success\n", flow_elm.id, flow_elm.type, flow_elm.trap_code, flow_elm.field_entry_id[0]); |
| } |
| return ret; |
| } /* endif TRAP_TO_HOST ACTION */ |
| |
| /* For TR-156 1:1, single tagged on V and R/S interface. |
| The vswitch will push a tunnel tag on the down stream packets. |
| The vswitch will pop the tunnel tag from the upstream packets. |
| |
| TBD For TR-156 1:1, double tagged on V interface and single tagged on R/S interface. |
| The vswitch will replace the outer tag with tunnel tag on the down stream packets. |
| The vswitch will replace the tunnel tag with outer vid on upstream packets. |
| |
| For TR-156 N:1, single tagged on V and R/S interface. |
| The vswitch will learn the upstream source MAC for downstream forwarding |
| */ |
| |
| /* packet tag type and service port id are required fields when there is no action - checked in validation stage */ |
| /* Here make sure it is the supported type */ |
| if(p_flow->data.classifier.pkt_tag_type != BCMBAL_PKT_TAG_TYPE_UNTAGGED && |
| p_flow->data.classifier.pkt_tag_type != BCMBAL_PKT_TAG_TYPE_SINGLE_TAG && |
| p_flow->data.classifier.pkt_tag_type != BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: classifier packet tag type 0x%x is invalid\n", p_flow->data.classifier.pkt_tag_type); |
| ret = BCM_ERR_PARM; |
| break; |
| } |
| |
| /* create a local reverse flow for symmetry configurations */ |
| ret = bal_sw_util_reverse_flow_create(p_flow, &flow_rev); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, (internal failure): fail to reverse flow\n"); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| |
| if(p_flow->key.flow_type == BCMBAL_FLOW_TYPE_UPSTREAM) |
| { |
| p_us_flow = p_flow; |
| p_ds_flow = &flow_rev; |
| } |
| else |
| { |
| p_us_flow = &flow_rev; |
| p_ds_flow = p_flow; |
| } |
| |
| |
| /* create the vswitch */ |
| /* if flow is associated with a GROUP, use the vswitch from the GROUP */ |
| if(BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, group_id)) |
| { |
| bal_sw_group_list *p_group_list; |
| /* get a pointer to the specified group instance */ |
| p_group_list = bal_sw_util_dpp_group_list_get_by_id(p_flow->data.group_id); |
| |
| if ( p_group_list == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, |
| " ERROR: group %d not found\n", p_flow->data.group_id); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| |
| /* retrieve the vswitch instance */ |
| p_vsi_svc = p_group_list->p_vsi; |
| vsi = (p_group_list->p_vsi)->vswitch; |
| /* add flow to the vsi service tag list */ |
| ret = bal_sw_util_dpp_vsi_service_tag_add(unit, p_vsi_svc, p_flow, &svc_tag_indx); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, flow fail to add service to vsi service tag list\n"); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| |
| /* set the mac learning distribution list */ |
| if(BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, resolve_mac) && |
| BCMOS_TRUE == p_flow->data.resolve_mac) |
| { |
| bcm_l2_addr_distribute_t dist; |
| |
| bcm_l2_addr_distribute_t_init(&dist); |
| dist.vid = vsi; |
| dist.flags = BCM_L2_ADDR_DIST_SET_CPU_DMA_DISTRIBUTER | BCM_L2_ADDR_DIST_SET_LEARN_DISTRIBUTER; |
| dist.flags |= BCM_L2_ADDR_DIST_LEARN_EVENT | BCM_L2_ADDR_DIST_AGED_OUT_EVENT | BCM_L2_ADDR_DIST_STATION_MOVE_EVENT; |
| rv = bcm_l2_addr_msg_distribute_set(unit, &dist); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_l2_addr_msg_distribute_set"); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| } |
| } |
| else |
| { |
| /* vsi service needs down stream info for in LIF VLAN info */ |
| p_vsi_svc = bal_sw_util_dpp_vsi_service_create(unit, p_ds_flow, &svc_tag_indx); |
| if(p_vsi_svc == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, vsi create failed\n"); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| vsi = p_vsi_svc->vswitch; |
| } |
| /* === create a PON LIF === */ |
| |
| /* if DS SLA is set, create a VOQ id, otherwise use local port id */ |
| /** @note QoS from the Downstream flow config is taken to configure SLA in the switch. Upstream is done in MAC. |
| */ |
| if ((p_flow->key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM) && (BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, sla))) |
| { |
| p_service_cfg = bcmos_calloc(sizeof(bal_sw_dpp_qos_service_cfg)); |
| if (p_service_cfg == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to alloc a bal_sw_dpp_qos_service_cfg_t\n"); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| |
| p_service_cfg->service_type = BAL_BCM_SVC_TYPE_IP; |
| p_service_cfg->bal_flow_id = p_flow->key.flow_id; |
| p_service_cfg->pon_port = p_flow->data.access_int_id; |
| p_service_cfg->tunnel_id = tunnel_id; |
| /* TBD - channelize port config */ |
| p_service_cfg->ds_qos.pon_chan = 0; |
| p_service_cfg->ds_qos.min.rate = p_flow->data.sla.min_rate; |
| /* burst - set it to twice the rate */ |
| max_burst = ( (2*p_service_cfg->ds_qos.min.rate) < BAL_MAX_BURST_RATE )? (2*p_service_cfg->ds_qos.min.rate): BAL_MAX_BURST_RATE; |
| p_service_cfg->ds_qos.min.burst = max_burst; |
| /* TBD priority */ |
| p_service_cfg->ds_qos.min.traffic_priority = 2; |
| |
| p_service_cfg->ds_qos.max.rate = p_flow->data.sla.max_rate - p_flow->data.sla.min_rate; |
| /* burst - set it to twice the rate */ |
| max_burst = ( (2*p_service_cfg->ds_qos.max.rate) < BAL_MAX_BURST_RATE )? (2*p_service_cfg->ds_qos.max.rate): BAL_MAX_BURST_RATE; |
| p_service_cfg->ds_qos.max.burst = max_burst; |
| /* TBD priority */ |
| p_service_cfg->ds_qos.max.traffic_priority = 2; |
| ret = bal_sw_dpp_llid_qos_config(unit, p_service_cfg); |
| /* Check for errors */ |
| if (ret != BCM_ERR_OK) |
| { |
| /* Failure */ |
| BCM_LOG(WARNING, log_id_sw_util, |
| "Downstream QoS configuration failed for pon %u tid %u\n", |
| p_flow->data.access_int_id, tunnel_id); |
| break; |
| } |
| BCM_GPORT_UNICAST_QUEUE_GROUP_SET(pon, p_service_cfg->ds_qos.voq_flow_id); |
| sys_pon = p_service_cfg->ds_qos.voq_gport; |
| BCM_LOG(INFO, log_id_sw_util, " use voq_id 0x%x queue group 0x%x, voq_gport 0x%x to create PON LIFT\n", p_service_cfg->ds_qos.voq_flow_id, pon, sys_pon); |
| |
| /* clear the VOQ stats here */ |
| bal_sw_dpp_qos_clear_voq_stats(unit, sys_pon); |
| } |
| else |
| { |
| sys_pon = pon; |
| BCM_LOG(INFO, log_id_sw_util, " use pp port 0x%x to create PON LIFT\n", pon); |
| } |
| |
| /* Configure the switch pon LIF based on classifier in the flow. |
| The pon LIF filter the ingress packets from PON, so use the classifier in the US direction. |
| This means, if the original flow configuration is for Downstream, use the reverse flow that locally created */ |
| |
| bcm_vlan_port_t_init(&vp); |
| |
| /* preserve any incoming packet vlan tags, if vlan actions are required, do it using egress translation */ |
| vp.flags = BCM_VLAN_PORT_OUTER_VLAN_PRESERVE | BCM_VLAN_PORT_INNER_VLAN_PRESERVE; |
| /* It is required to set the FEC flag so that the egress redirection action from the DS classification can |
| select the correct PON LIF to forward the packet */ |
| vp.flags |= BCM_VLAN_PORT_FORWARD_GROUP; |
| |
| |
| switch(p_us_flow->data.classifier.pkt_tag_type) |
| { |
| case BCMBAL_PKT_TAG_TYPE_SINGLE_TAG: |
| /* match both tunnel and outer tag on ingress PON */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_us_flow->data.classifier, classifier, o_pbits)) |
| { |
| vp.criteria = BCM_VLAN_PORT_MATCH_PORT_TUNNEL_PCP_VLAN; |
| vp.match_pcp = p_us_flow->data.classifier.o_pbits; |
| } |
| else |
| { |
| vp.criteria = BCM_VLAN_PORT_MATCH_PORT_TUNNEL_VLAN; |
| } |
| break; |
| case BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG: |
| /* Otherwise match both tunnel and both outer and inner tags on ingress PON */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_us_flow->data.classifier, classifier, o_pbits)) |
| { |
| vp.criteria = BCM_VLAN_PORT_MATCH_PORT_TUNNEL_PCP_VLAN_STACKED; |
| vp.match_pcp = p_us_flow->data.classifier.o_pbits; |
| } |
| else |
| { |
| vp.criteria = BCM_VLAN_PORT_MATCH_PORT_TUNNEL_VLAN_STACKED; |
| } |
| break; |
| case BCMBAL_PKT_TAG_TYPE_UNTAGGED: |
| /* Otherwise match tunnel tag on ingress PON */ |
| { |
| vp.criteria = BCM_VLAN_PORT_MATCH_PORT_TUNNEL; |
| } |
| break; |
| default: |
| /* should not reach here */ |
| BCM_LOG(ERROR, log_id_sw_util, "Error, Unsupported packet type %d for pon LIF\n",p_us_flow->data.classifier.pkt_tag_type ); |
| return BCM_ERR_INTERNAL; |
| } |
| |
| |
| vp.port = pon; |
| vp.match_tunnel_value = tunnel_id; |
| vp.egress_tunnel_value = tunnel_id; |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_us_flow->data.classifier, classifier, o_vid)) |
| { |
| vp.match_vlan = p_us_flow->data.classifier.o_vid; |
| } |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_us_flow->data.classifier, classifier, i_vid)) |
| { |
| vp.match_inner_vlan = p_us_flow->data.classifier.i_vid; |
| } |
| vp.vsi = 0; /* will be populated when the gport is added to service, using vswitch_port_add */ |
| |
| /* Create the vlan port (i.e., PON LIF) */ |
| rv = bcm_vlan_port_create(unit, &vp); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_vlan_port_create pon %d failed %d\n", pon, rv); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| |
| pon_gport = vp.vlan_port_id; |
| /* set pon gport to first element of the array */ |
| flow_elm.pon_port[0] = pon_gport; |
| |
| /* set PON port DISCARD mode to reflect the flow direction. When two BAL flows with same id and different directions |
| are configured, the DISCARD mode will be set back to NONE */ |
| if (flow_elm.type == BAL_SW_FLOW_TYPE_DOWNSTREAM) |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Info, bcm_port_discard_set pon 0x%x to DISCARD_INGRESS\n", pon_gport); |
| rv = bcm_port_discard_set(unit, pon_gport, BCM_PORT_DISCARD_INGRESS); |
| } |
| else if (flow_elm.type == BAL_SW_FLOW_TYPE_UPSTREAM) |
| { |
| /* allow bi-direction for N:1 service */ |
| if( BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_us_flow, flow, group_id) && |
| BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_us_flow, flow, resolve_mac) && |
| BCMOS_TRUE == p_us_flow->data.resolve_mac) |
| { |
| rv = BCM_E_NONE; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Info, bcm_port_discard_set pon 0x%x to DISCARD_EGRESS\n", pon_gport); |
| rv = bcm_port_discard_set(unit, pon_gport, BCM_PORT_DISCARD_EGRESS); |
| } |
| } |
| else |
| { |
| rv = BCM_E_NONE; |
| } |
| |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_port_discard_set failed %d\n", rv); |
| /* ARAD does not support the DISCARD function as of 6.5.4 */ |
| #ifdef QAX_SWITCH |
| ret = BCM_ERR_INTERNAL; |
| break; |
| #endif |
| } |
| |
| // add pon gport to vswitch |
| rv = bcm_vswitch_port_add(unit, vsi, pon_gport); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_vswitch_port_add for pon 0x%x failed %d\n", pon, rv); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| else |
| { |
| BCM_LOG(INFO, log_id_sw_util, "Info, bcm_vswitch_port_add for pon 0x%x, gport 0x%x\n", pon, pon_gport); |
| } |
| |
| if(BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, group_id) && |
| BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, resolve_mac) && |
| BCMOS_TRUE == p_flow->data.resolve_mac) |
| { |
| /* for flows that need to resolve mac, don't forward unknown traffics */ |
| BCM_LOG(DEBUG, log_id_sw_util, "pon %d:0x%x bypass downstream MC group join\n", pon, pon_gport); |
| } |
| else |
| { |
| // obtain encapsulation ID for legacy reason, used in ADD API */ |
| rv = bcm_multicast_vlan_encap_get(unit, p_vsi_svc->ds_flood_grp_id, sys_pon, pon_gport, &pon_encap_id); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_multicast_vlan_encap_get for pon failed %d\n", rv); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| /* join the downstream multicast group as a member of replication list */ |
| rv = bcm_multicast_ingress_add(unit, p_vsi_svc->ds_flood_grp_id, sys_pon, pon_encap_id); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_multicast_ingress_add for pon failed %d\n", rv); |
| ret = BCM_ERR_INTERNAL; |
| break; |
| } |
| /* now set the type of packets that goes to the upstream flooding group (offset 0) */ |
| rv = bcm_port_control_set(unit, pon_gport, bcmPortControlFloodUnknownUcastGroup, BAL_DPP_US_FLOOD_OFFSET); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_port_control_set ucast for pon failed %d\n", rv); |
| } |
| } |
| rv = bcm_port_control_set(unit, pon_gport, bcmPortControlFloodUnknownMcastGroup, BAL_DPP_US_FLOOD_OFFSET); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_port_control_set mcast for pon failed %d\n", rv); |
| break; |
| } |
| rv = bcm_port_control_set(unit, pon_gport, bcmPortControlFloodBroadcastGroup, BAL_DPP_US_FLOOD_OFFSET); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_port_control_set bcast for pon failed %d\n", rv); |
| break; |
| } |
| /* perform vlan translation on pon port, pon egress info is in the Downstream configuration */ |
| if (BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_ds_flow, flow, action)) |
| { |
| ret = bal_sw_util_dpp_invlanx(unit, p_us_flow, pon_gport); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to perform ingress vlan translation on pon %d\n", pon); |
| break; |
| } |
| ret = bal_sw_util_dpp_evlanx(unit, p_ds_flow, pon_gport); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to perform egress vlan translation on pon %d\n", pon); |
| break; |
| } |
| } |
| /* === PON LIF created === */ |
| |
| ii = 0; /* Start with the first NNI logical interface */ |
| /* loop through all nni port, add them to vswitch, -1 indicate end of table */ |
| /* Configure the switch nni LIF based on classifier in the flow. |
| The nni LIF filter the ingress packets from Network, so use the classifier in the DS direction. |
| This means, if the original flow configuration is for Upstream, use the reverse flow that locally created */ |
| while(-1 != (nni = bal_bcm_net_inf_pbm_get(ii))) |
| { |
| /* skip nni port that is not in the same device or not valid */ |
| if ( bal_bcm_net_inf_dev_get(ii) != unit || |
| BCMOS_FALSE == bcm_topo_nni_is_valid(ii)) |
| { |
| ii++; |
| continue; |
| } |
| |
| /* if nni port is specified in the flow, skip the other nni ports */ |
| if ( BCMBAL_CFG_PROP_IS_SET(p_ds_flow, flow, network_int_id) && |
| ii != p_ds_flow->data.network_int_id |
| ) |
| { |
| ii++; |
| continue; |
| } |
| |
| /* create gport based on nni physical port number */ |
| |
| ret = bal_sw_util_dpp_vsi_service_port_add(unit, p_vsi_svc, svc_tag_indx, nni, &nni_gport); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to add nni %d to vsi\n", nni); |
| break; |
| } |
| |
| flow_elm.net_port[flow_elm.num_net] = nni_gport; |
| flow_elm.num_net++; |
| |
| /* perform vlan translation on nni port, nni egress info is in the Upstream configuration */ |
| if (BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_us_flow, flow, action)) |
| { |
| rv = bcm_vlan_control_port_set(unit, nni_gport, bcmVlanPortTranslateKeyFirst, bcmVlanTranslateKeyPortOuter); |
| if (rv != BCM_E_NONE) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to perform vlan control port set on nni %d\n", nni); |
| break; |
| } |
| ret = bal_sw_util_dpp_invlanx(unit, p_ds_flow, nni_gport); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to perform ingress vlan translation on nni %d\n", nni); |
| break; |
| } |
| |
| ret = bal_sw_util_dpp_evlanx(unit, p_us_flow, nni_gport); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to perfrom egress vlan translation on nni %d\n", nni); |
| break; |
| } |
| } |
| /* if classifier is more than just port + VLAN tags, add ACL rule to re-direct packet to egress port. |
| This bypass the ingress LIF as LIF check VID only. |
| Upstream classification is expected to be done in ONU (onu add GEM based on classifiers for upstream) */ |
| if( p_flow->key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM && |
| bal_sw_util_flow_ds_acl_cls_chk(p_ds_flow) == BCMOS_TRUE ) |
| { |
| /* add re-direct rule */ |
| if( BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_ds_flow, flow, network_int_id)) |
| { |
| ret = bal_sw_util_dpp_acl_add(unit, p_ds_flow, &flow_elm, nni_gport, pon_gport); |
| } |
| else /* Rule need to match the network port, otherwise, it will apply to all ports - nni and pon */ |
| { |
| BCMBAL_CFG_PROP_SET(p_ds_flow, flow, network_int_id, ii); |
| ret = bal_sw_util_dpp_acl_add(unit, p_ds_flow, &flow_elm, nni_gport, pon_gport); |
| BCMBAL_CFG_PROP_CLEAR(p_ds_flow, flow, network_int_id); |
| } |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to add forwarding acl on nni %d, ret = %d\n", ret, ii); |
| break; |
| } |
| /* add a dft drop rule (priority 0) on the LIF to drop packets that match VID but no match for other fields */ |
| ret = bal_sw_util_dpp_dft_acl_add(unit, p_ds_flow, &flow_elm, nni_gport); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to add default acl on nni=0x%x, ret = %d\n", nni_gport, ret); |
| break; |
| } |
| } |
| /* If the user needs to perfrom upstream classification (in addition to VID), |
| Add an ACL rule to re-direct packets. The ACL also allow upstream flows to be prioritized based on priority field */ |
| if( p_flow->key.flow_type == BCMBAL_FLOW_TYPE_UPSTREAM && |
| bal_sw_util_flow_us_acl_cls_chk(p_us_flow) == BCMOS_TRUE && |
| /* there are issues using traditional API for PON application when egress vlan translation and ingress ACL are both active. |
| Enable the ACL only when there is no vlan action - TBD */ |
| BCMOS_FALSE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, action)) |
| { |
| /* ACL requires an action, currently support trap or re-direct the flow. |
| TRAP has been handle in other path, here we require user to specify which port to re-direct. |
| Other options will be adding multiple ACL to cover all possible egress ports - TBD */ |
| if( BCMOS_FALSE == BCMBAL_CFG_PROP_IS_SET(p_us_flow, flow, network_int_id)) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, Upstream classifier in addition to VID without NNI port set is not suppotred\n"); |
| break; |
| } |
| else |
| { |
| ret = bal_sw_util_dpp_acl_add(unit, p_us_flow, &flow_elm, pon_gport, nni_gport); |
| } |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to add forwarding acl on pon %d, ret = %d\n", ret, pon); |
| break; |
| } |
| /* add a default drop rule (priority 0) on the LIF to drop packets that match VID but no match for other fields */ |
| ret = bal_sw_util_dpp_dft_acl_add(unit, p_us_flow, &flow_elm, pon_gport); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to add default acl on pon=0x%x, ret = %d\n", pon_gport, ret); |
| break; |
| } |
| } |
| |
| ii++; /* Next NNI */ |
| } |
| /* skip the rest if anything goes wrong */ |
| if( ret != BCM_ERR_OK) |
| { |
| break; |
| } |
| /* Configure PCP/Cos to Traffic Class mapping. This has to be done after the NNI LIFs are created */ |
| if (p_flow->key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM && BCMOS_TRUE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, sla)) |
| { |
| p_service_cfg->num_nni_gport = flow_elm.num_net; |
| for( ii = 0; ii < p_service_cfg->num_nni_gport; ii++) |
| { |
| p_service_cfg->nni_gport[ii] = flow_elm.net_port[ii]; |
| } |
| ret = bal_sw_dpp_llid_set_qos_port_map(unit, p_service_cfg); |
| if (ret != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, "Error, fail to perfrom nni qos port map, ret = %d\n", ret); |
| break; |
| } |
| } |
| |
| } while(0); |
| |
| if( ret == BCM_ERR_OK) |
| { |
| /* add the flow info to the link list */ |
| flow_elm.p_vsi_svc = p_vsi_svc; |
| flow_elm.vsi_svc_indx = svc_tag_indx; |
| flow_elm.num_pon = 1; |
| flow_elm.p_service_cfg = p_service_cfg; |
| |
| /* nni ports are done in the nni loop */ |
| flow_elm.valid = 1; |
| bal_sw_util_flow_list_insert(flow_elm); |
| |
| BCM_LOG(INFO, log_id_sw_util, "Add flow_elm %d type %d, vswitch 0x%x, Success\n", flow_elm.id, flow_elm.type, p_vsi_svc->vswitch); |
| } |
| return ret; |
| } |
| |
| |
| /** |
| * @brief The internal flow remove function program DPP to release resource that |
| * were allocated during the ADD operation |
| * |
| * @param p_flow_elm a pointer to the internal flow list |
| * |
| * @return error code |
| */ |
| bcmos_errno bal_sw_util_dpp_flow_remove_int(bal_sw_flow *p_flow_elm) |
| { |
| bcmos_errno ret = BCM_ERR_OK; |
| int rv, i, flow_id; |
| bal_sw_vsi_service *p_vsi_svc; |
| bal_sw_group_list *p_group_list; |
| |
| if(p_flow_elm == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, " invalid pointer to the internal flow list\n"); |
| return BCM_ERR_INTERNAL; |
| } |
| p_vsi_svc = (bal_sw_vsi_service *) p_flow_elm->p_vsi_svc; |
| /* if anything go wrong, log a warning and continue release resources */ |
| for(i = 0; i <p_flow_elm->num_pon; i++) |
| { |
| rv = bcm_vswitch_port_delete(p_flow_elm->device, p_vsi_svc->vswitch, p_flow_elm->pon_port[i]); |
| if(rv) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, " vswitch pon port 0x%x delete failed %d\n", p_flow_elm->pon_port[i], rv); |
| } |
| rv = bcm_vlan_port_destroy(p_flow_elm->device, p_flow_elm->pon_port[i]); |
| if(rv) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, " vswitch pon port 0x%x destroy failed %d\n", p_flow_elm->pon_port[i], rv); |
| } |
| p_flow_elm->pon_port[i] = 0; |
| } |
| p_flow_elm->num_pon = 0; |
| |
| for(i = 0; i <p_flow_elm->num_net; i++) |
| { |
| ret = bal_sw_util_dpp_vsi_service_port_rem(p_flow_elm->device, p_flow_elm->p_vsi_svc, p_flow_elm->vsi_svc_indx, p_flow_elm->net_port[i]); |
| if(ret != BCM_ERR_OK) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, " vsi service port 0x%x remove failed %d\n", p_flow_elm->net_port[i], ret); |
| } |
| |
| p_flow_elm->net_port[i] = 0; |
| } |
| p_flow_elm->num_net = 0; |
| |
| /* release vswitch */ |
| if (p_vsi_svc) |
| { |
| /* remove the vsi if no more service, destroy function will do the counting */ |
| ret = bal_sw_util_dpp_vsi_service_destroy(p_flow_elm->device, p_vsi_svc); |
| if(ret != BCM_ERR_OK) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, " vsi service destroy failed %d\n", ret); |
| } |
| } |
| /* decrement the group reference counter in the group list */ |
| if (p_flow_elm->group_id) |
| { |
| p_group_list = bal_sw_util_dpp_group_list_get_by_id(p_flow_elm->group_id); |
| if ( p_group_list == NULL) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, |
| " WARNING: MC flow remove can't find the mc group %d in the list\n", p_flow_elm->group_id); |
| } |
| else |
| { |
| if(p_group_list->use_count) |
| { |
| p_group_list->use_count--; |
| } |
| else |
| { |
| BCM_LOG(WARNING, log_id_sw_util, |
| " WARNING: MC flow remove find the mc group %d in the list has no use_count\n", p_flow_elm->group_id); |
| } |
| } |
| } |
| |
| /* relese trap */ |
| if (p_flow_elm->trap_code) |
| { |
| rv = bcm_rx_trap_type_destroy(p_flow_elm->device, p_flow_elm->trap_code); |
| if(rv) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, " rx trap %d destroy failed %d\n", p_flow_elm->trap_code, rv); |
| } |
| p_flow_elm->trap_code = 0; |
| p_flow_elm->trap_port = 0; |
| } |
| |
| /* release acl, if any */ |
| for(i = 0; i <p_flow_elm->num_eid; i++) |
| { |
| if (p_flow_elm->field_entry_id[i]) |
| { |
| rv = bcm_field_entry_remove(p_flow_elm->device, p_flow_elm->field_entry_id[i]); |
| if(rv) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, " field entry %d remove failed %d\n", p_flow_elm->field_entry_id[i], rv); |
| } |
| rv = bcm_field_entry_destroy(p_flow_elm->device, p_flow_elm->field_entry_id[i]); |
| if(rv) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, " field entry %d destroy failed %d\n", p_flow_elm->field_entry_id[i], rv); |
| } |
| p_flow_elm->field_entry_id[i] = 0; |
| } |
| } |
| p_flow_elm->num_eid = 0; |
| |
| /* release llid qos, if any */ |
| if (p_flow_elm->p_service_cfg) |
| { |
| /* any error during cleanup will be reported, but continue anyway */ |
| bal_sw_dpp_llid_qos_cleanup(p_flow_elm->device, (bal_sw_dpp_qos_service_cfg *)p_flow_elm->p_service_cfg); |
| bcmos_free(p_flow_elm->p_service_cfg); |
| p_flow_elm->p_service_cfg = NULL; |
| } |
| |
| /* remove from the internal link list */ |
| flow_id = p_flow_elm->id; |
| ret = bal_sw_util_flow_list_remove(p_flow_elm); |
| if(ret == BCM_ERR_OK) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " flow %d is removed\n", flow_id); |
| ret = BCM_ERR_OK; |
| } |
| else |
| { |
| BCM_LOG(WARNING, log_id_sw_util, " fail to remove flow %d w ret = %d\n", flow_id, ret); |
| ret = BCM_ERR_INTERNAL; |
| } |
| return ret; |
| } |
| |
| /** |
| * @brief The flow remove function program DPP to release resource that |
| * were allocated during the ADD operation |
| * |
| * @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_dpp_flow_remove(bcmbal_iwf_mode iwf_mode, bcmbal_flow_cfg *p_flow) |
| { |
| bcmos_errno ret = BCM_ERR_OK; |
| bal_sw_flow *p_flow_elm; |
| int ii, rv; |
| bcm_port_discard_t discard_type = BCM_PORT_DISCARD_NONE; |
| |
| if(p_flow == NULL) |
| { |
| BCM_LOG(ERROR, log_id_sw_util, " invalid pointer to the bal flow\n"); |
| return BCM_ERR_INTERNAL; |
| } |
| |
| /* make sure the flow id is in the link list, if not return success anyway (probably clearing a DOWN flow) */ |
| p_flow_elm = bal_sw_util_flow_list_get_by_id (p_flow->key.flow_id); |
| if(p_flow_elm == NULL) |
| { |
| BCM_LOG(WARNING, log_id_sw_util, " flow %d not exist the link list\n", p_flow->key.flow_id); |
| return BCM_ERR_OK; |
| } |
| |
| /* if flow_elm has the requested direction, set switch to discard packets from the requested direction. */ |
| if( (p_flow_elm->type & BAL_SW_FLOW_TYPE_DOWNSTREAM) && |
| (p_flow->key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM) |
| ) |
| { |
| discard_type = BCM_PORT_DISCARD_EGRESS; |
| } |
| else if ((p_flow_elm->type & BAL_SW_FLOW_TYPE_UPSTREAM) && |
| (p_flow->key.flow_type == BCMBAL_FLOW_TYPE_UPSTREAM) |
| ) |
| { |
| discard_type = BCM_PORT_DISCARD_INGRESS; |
| } |
| else if (p_flow_elm->type & BAL_SW_FLOW_TYPE_MULTICAST) |
| { |
| /* clear rhe type so that it can remove the flow below */ |
| p_flow_elm->type &= ~BAL_SW_FLOW_TYPE_MULTICAST; |
| } |
| |
| if (discard_type != BCM_PORT_DISCARD_NONE) |
| { |
| for(ii=0; ii<p_flow_elm->num_pon; ii++) |
| { |
| if(p_flow_elm->pon_port[ii] == 0) |
| { |
| /* if PON LIF is deleted, then continue to the next pon */ |
| continue; |
| } |
| rv = bcm_port_discard_set(p_flow_elm->device, p_flow_elm->pon_port[ii], discard_type); |
| if (rv != BCM_E_NONE) |
| { |
| /* mark an error, but continue */ |
| BCM_LOG(ERROR, log_id_sw_util, "Error, bcm_port_discard_set failed %d on pon 0x%x\n", rv, p_flow_elm->pon_port[ii]); |
| ret = BCM_ERR_INTERNAL; |
| } |
| } |
| if (BCM_ERR_OK == ret) |
| { |
| if( p_flow->key.flow_type == BCMBAL_FLOW_TYPE_DOWNSTREAM) |
| { |
| p_flow_elm->type &= ~BAL_SW_FLOW_TYPE_DOWNSTREAM; |
| } |
| else if (p_flow->key.flow_type == BCMBAL_FLOW_TYPE_UPSTREAM) |
| { |
| p_flow_elm->type &= ~BAL_SW_FLOW_TYPE_UPSTREAM; |
| } |
| BCM_LOG(INFO, log_id_sw_util, " Success remove flow %d with type %d\n", p_flow->key.flow_id, p_flow->key.flow_type); |
| } |
| } |
| /* remove the flow from the list if everything is cleanup */ |
| if(BAL_SW_FLOW_TYPE_NONE == p_flow_elm->type) |
| { |
| ret = bal_sw_util_dpp_flow_remove_int(p_flow_elm); |
| } |
| |
| return ret; |
| } |
| |
| |
| |
| /******************************* |
| * Helper routines below |
| *******************************/ |
| /** |
| * @brief Helper routine to reverse a flow i.e. an upstream flow is made a downstream flow |
| * and vice versa |
| * |
| * @param p_flow A pointer to the original flow |
| * @param p_flow_rev A pointer to the reversed flow |
| * @return error code |
| * |
| * @note for now it assumes an untagged or single tagged vlan flow with single vlan tag classificiation |
| */ |
| static bcmos_errno bal_sw_util_reverse_flow_create(bcmbal_flow_cfg *p_flow, bcmbal_flow_cfg *p_flow_rev) |
| { |
| bcmos_errno ret = BCM_ERR_OK; |
| |
| if ((p_flow == NULL) || (p_flow_rev == NULL)) |
| { |
| return BCM_ERR_INTERNAL; |
| } |
| |
| /* reverse parameters in flow */ |
| memcpy(p_flow_rev, p_flow, sizeof(bcmbal_flow_cfg)); |
| |
| p_flow_rev->key.flow_type = (p_flow->key.flow_type == BCMBAL_FLOW_TYPE_UPSTREAM ? |
| BCMBAL_FLOW_TYPE_DOWNSTREAM : BCMBAL_FLOW_TYPE_UPSTREAM); |
| |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, dst_mac)) |
| { |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow_rev->data.classifier, classifier, src_mac, p_flow->data.classifier.dst_mac); |
| } |
| else /* clear the src_mac presence mask in the reverse flow */ |
| { |
| BCMBAL_ATTRIBUTE_PROP_CLEAR(&p_flow_rev->data.classifier, classifier, src_mac); |
| } |
| |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, src_mac)) |
| { |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow_rev->data.classifier, classifier, dst_mac, p_flow->data.classifier.src_mac); |
| } |
| else /* clear the dst_mac presence mask in the reverse flow */ |
| { |
| BCMBAL_ATTRIBUTE_PROP_CLEAR(&p_flow_rev->data.classifier, classifier, dst_mac); |
| } |
| |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, dst_port)) |
| { |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow_rev->data.classifier, classifier, src_port, p_flow->data.classifier.dst_port); |
| } |
| else /* clear the src_port presence mask in the reverse flow */ |
| { |
| BCMBAL_ATTRIBUTE_PROP_CLEAR(&p_flow_rev->data.classifier, classifier, src_port); |
| } |
| |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, src_port)) |
| { |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow_rev->data.classifier, classifier, dst_port, p_flow->data.classifier.src_port); |
| } |
| else /* clear the dst_port presence mask in the reverse flow */ |
| { |
| BCMBAL_ATTRIBUTE_PROP_CLEAR(&p_flow_rev->data.classifier, classifier, dst_port); |
| } |
| |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, dst_ip)) |
| { |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow_rev->data.classifier, classifier, src_ip, p_flow->data.classifier.dst_ip); |
| } |
| else /* clear the src_ip presence mask in the reverse flow */ |
| { |
| BCMBAL_ATTRIBUTE_PROP_CLEAR(&p_flow_rev->data.classifier, classifier, src_ip); |
| } |
| |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, src_ip)) |
| { |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow_rev->data.classifier, classifier, dst_ip, p_flow->data.classifier.src_ip); |
| } |
| else /* clear the dst_ip presence mask in the reverse flow */ |
| { |
| BCMBAL_ATTRIBUTE_PROP_CLEAR(&p_flow_rev->data.classifier, classifier, dst_ip); |
| } |
| |
| |
| if (BCMBAL_ACTION_CMD_ID_IS_SET(&(p_flow->data.action), BCMBAL_ACTION_CMD_ID_ADD_OUTER_TAG)) |
| { |
| BCMBAL_ACTION_CMD_ID_CLEAR(&(p_flow_rev->data.action), BCMBAL_ACTION_CMD_ID_ADD_OUTER_TAG); |
| BCMBAL_ACTION_CMD_ID_SET(&(p_flow_rev->data.action), BCMBAL_ACTION_CMD_ID_REMOVE_OUTER_TAG); |
| |
| /* remove o_vid from action */ |
| BCMBAL_ATTRIBUTE_PROP_CLEAR(&p_flow_rev->data.action, action, o_vid); |
| |
| switch(p_flow->data.classifier.pkt_tag_type) |
| { |
| case BCMBAL_PKT_TAG_TYPE_UNTAGGED: |
| /* ADD on untagged packet result to single tagged on revert direction */ |
| p_flow_rev->data.classifier.pkt_tag_type = BCMBAL_PKT_TAG_TYPE_SINGLE_TAG; |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow_rev->data.classifier, classifier, o_vid, p_flow->data.action.o_vid); |
| break; |
| case BCMBAL_PKT_TAG_TYPE_SINGLE_TAG: |
| /* ADD on single tagged packet result to double tagged on revert direction */ |
| p_flow_rev->data.classifier.pkt_tag_type = BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG; |
| p_flow_rev->data.classifier.o_vid = p_flow->data.action.o_vid; |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow_rev->data.classifier, classifier, i_vid, p_flow->data.classifier.o_vid); |
| break; |
| case BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG: |
| default: |
| /* should not reach here */ |
| break; |
| } |
| } |
| else if (BCMBAL_ACTION_CMD_ID_IS_SET(&(p_flow->data.action), BCMBAL_ACTION_CMD_ID_REMOVE_OUTER_TAG)) |
| { |
| BCMBAL_ACTION_CMD_ID_CLEAR(&(p_flow_rev->data.action), BCMBAL_ACTION_CMD_ID_REMOVE_OUTER_TAG); |
| BCMBAL_ACTION_CMD_ID_SET(&(p_flow_rev->data.action), BCMBAL_ACTION_CMD_ID_ADD_OUTER_TAG); |
| |
| /* add o_vid to action */ |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow_rev->data.action, action, o_vid, p_flow->data.classifier.o_vid); |
| switch(p_flow->data.classifier.pkt_tag_type) |
| { |
| case BCMBAL_PKT_TAG_TYPE_DOUBLE_TAG: |
| /* REMOVE on double tagged packet result to single tagged on revert direction */ |
| p_flow_rev->data.classifier.pkt_tag_type = BCMBAL_PKT_TAG_TYPE_SINGLE_TAG; |
| p_flow_rev->data.classifier.o_vid = p_flow->data.classifier.i_vid; |
| BCMBAL_ATTRIBUTE_PROP_CLEAR(&p_flow_rev->data.classifier, classifier, i_vid); |
| break; |
| case BCMBAL_PKT_TAG_TYPE_SINGLE_TAG: |
| /* REMOVE on single tagged packet result to untagged on revert direction */ |
| p_flow_rev->data.classifier.pkt_tag_type = BCMBAL_PKT_TAG_TYPE_UNTAGGED; |
| BCMBAL_ATTRIBUTE_PROP_CLEAR(&p_flow_rev->data.classifier, classifier, o_vid); |
| break; |
| case BCMBAL_PKT_TAG_TYPE_UNTAGGED: |
| default: |
| /* should not reach here */ |
| break; |
| } |
| /* if the remove classifier has tpid attribute, set it when ADD in reverse direction */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, o_tpid)) |
| { |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow_rev->data.action, action, o_tpid, p_flow->data.classifier.o_tpid); |
| BCMBAL_ATTRIBUTE_PROP_CLEAR(&p_flow_rev->data.classifier, classifier, o_tpid); |
| } |
| } |
| else if (BCMBAL_ACTION_CMD_ID_IS_SET(&(p_flow->data.action), BCMBAL_ACTION_CMD_ID_XLATE_OUTER_TAG)) |
| { |
| /* swap output vid and classifier parameters */ |
| p_flow_rev->data.action.o_vid = p_flow->data.classifier.o_vid; |
| p_flow_rev->data.classifier.o_vid = p_flow->data.action.o_vid; |
| } |
| |
| if (BCMBAL_ACTION_CMD_ID_IS_SET(&(p_flow->data.action), BCMBAL_ACTION_CMD_ID_REMARK_PBITS)) |
| { |
| /* swap output pbits and classifier parameters */ |
| if(BCMOS_TRUE == BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, o_pbits)) |
| { |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow_rev->data.action, action, o_pbits, p_flow->data.classifier.o_pbits); |
| } |
| else |
| { |
| /* if the pbits remark is for packets with any o_pibts, then there is no clear REVERSE operation. |
| Set the reverse to NO pbits action. i.e pbits is a passthrough |
| */ |
| BCMBAL_ATTRIBUTE_PROP_CLEAR(&p_flow_rev->data.action, action, o_pbits); |
| BCMBAL_ACTION_CMD_ID_CLEAR(&(p_flow_rev->data.action), BCMBAL_ACTION_CMD_ID_REMARK_PBITS); |
| } |
| |
| BCMBAL_ATTRIBUTE_PROP_SET(&p_flow_rev->data.classifier, classifier, o_pbits, p_flow->data.action.o_pbits); |
| } |
| //else tbd |
| |
| |
| return ret; |
| } |
| |
| /** |
| * @brief Helper routine to compare two flows for symmetry |
| * |
| * @param p_flow A pointer to the original flow |
| * @param p_ref_flow A pointer to the reference flow |
| * @return TRUE or FALSE |
| */ |
| bcmos_bool bal_sw_util_is_symmetry_flows(bcmbal_flow_cfg *p_flow, bcmbal_flow_cfg *p_ref_flow) |
| { |
| bcmos_bool ret = BCMOS_TRUE; |
| |
| /* compare the access interface */ |
| if( BCMBAL_CFG_PROP_IS_SET(p_flow, flow, access_int_id) != |
| BCMBAL_CFG_PROP_IS_SET(p_ref_flow, flow, access_int_id) ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " access interface SET not the same for flow %d\n", p_flow->key.flow_id); |
| ret = BCMOS_FALSE; |
| } |
| else |
| { |
| if( (BCMBAL_CFG_PROP_IS_SET(p_flow, flow, access_int_id) || BCMBAL_CFG_PROP_IS_SET(p_ref_flow, flow, access_int_id)) && |
| (p_flow->data.access_int_id != p_ref_flow->data.access_int_id) ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " flow %d downstream/upstream access interface %d != %d \n", |
| p_flow->key.flow_id, p_flow->data.access_int_id, p_ref_flow->data.access_int_id ); |
| ret = BCMOS_FALSE; |
| } |
| } |
| /* compare the network interface */ |
| if( BCMBAL_CFG_PROP_IS_SET(p_flow, flow, network_int_id) != |
| BCMBAL_CFG_PROP_IS_SET(p_ref_flow, flow, network_int_id) ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " network interface SET not the same for flow %d\n", p_flow->key.flow_id); |
| ret = BCMOS_FALSE; |
| } |
| else |
| { |
| if( (BCMBAL_CFG_PROP_IS_SET(p_flow, flow, network_int_id) ||BCMBAL_CFG_PROP_IS_SET(p_ref_flow, flow, network_int_id)) && |
| (p_flow->data.network_int_id != p_ref_flow->data.network_int_id) ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " flow %d downstream/upstream access interface %d != %d \n", |
| p_flow->key.flow_id, p_flow->data.network_int_id, p_ref_flow->data.network_int_id ); |
| ret = BCMOS_FALSE; |
| } |
| } |
| |
| /* if there is no action for the flow, packet type and VIDs should be the same */ |
| /* compare the IPv4 addresses */ |
| if(BCMOS_FALSE == BCMBAL_CFG_PROP_IS_SET(p_flow, flow, action) && |
| BCMOS_FALSE == BCMBAL_CFG_PROP_IS_SET(p_ref_flow, flow, action)) |
| { |
| /* check packet type */ |
| if( BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, pkt_tag_type) != |
| BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_ref_flow->data.classifier, classifier, pkt_tag_type) ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " packet type SET not the same for flow %d\n", p_flow->key.flow_id); |
| ret = BCMOS_FALSE; |
| } |
| else |
| { |
| if( BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, pkt_tag_type) && |
| (p_flow->data.classifier.pkt_tag_type != p_ref_flow->data.classifier.pkt_tag_type) ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " flow %d downstream/upstream packet type %d != %d \n", |
| p_flow->key.flow_id, p_flow->data.classifier.pkt_tag_type, p_ref_flow->data.classifier.pkt_tag_type ); |
| ret = BCMOS_FALSE; |
| } |
| } |
| |
| /* check the outer VID */ |
| if( BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, o_vid) != |
| BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_ref_flow->data.classifier, classifier, o_vid) ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " outer vid SET not the same for flow %d\n", p_flow->key.flow_id); |
| ret = BCMOS_FALSE; |
| } |
| else |
| { |
| if( BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, o_vid) && |
| (p_flow->data.classifier.o_vid != p_ref_flow->data.classifier.o_vid) ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " flow %d downstream/upstream outer vid %d != %d \n", |
| p_flow->key.flow_id, p_flow->data.classifier.o_vid, p_ref_flow->data.classifier.o_vid ); |
| ret = BCMOS_FALSE; |
| } |
| } |
| /* check the inner VID */ |
| if( BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, i_vid) != |
| BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_ref_flow->data.classifier, classifier, i_vid) ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " inner vid SET not the same for flow %d\n", p_flow->key.flow_id); |
| ret = BCMOS_FALSE; |
| } |
| else |
| { |
| if( BCMBAL_ATTRIBUTE_PROP_IS_SET(&p_flow->data.classifier, classifier, i_vid) && |
| (p_flow->data.classifier.i_vid != p_ref_flow->data.classifier.i_vid) ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " flow %d downstream/upstream inner vid %d != %d \n", |
| p_flow->key.flow_id, p_flow->data.classifier.i_vid, p_ref_flow->data.classifier.i_vid ); |
| ret = BCMOS_FALSE; |
| } |
| } |
| |
| } |
| else /* if there is an action - action for VID must be symmetrical */ |
| { |
| if(BCMOS_TRUE == BCMBAL_ACTION_CMD_ID_IS_SET(&(p_flow->data.action), BCMBAL_ACTION_CMD_ID_ADD_OUTER_TAG) && |
| BCMOS_FALSE == BCMBAL_ACTION_CMD_ID_IS_SET(&(p_ref_flow->data.action), BCMBAL_ACTION_CMD_ID_REMOVE_OUTER_TAG) ) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " flow %d downstream/upstream outer vlan action not symmetrical \n", p_flow->key.flow_id ); |
| ret = BCMOS_FALSE; |
| } |
| if(BCMOS_TRUE == BCMBAL_ACTION_CMD_ID_IS_SET(&(p_flow->data.action), BCMBAL_ACTION_CMD_ID_REMOVE_OUTER_TAG) && |
| BCMOS_FALSE == BCMBAL_ACTION_CMD_ID_IS_SET(&(p_ref_flow->data.action), BCMBAL_ACTION_CMD_ID_ADD_OUTER_TAG)) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " flow %d upstream/downstream outer vlan action not symmetrical \n", p_flow->key.flow_id ); |
| ret = BCMOS_FALSE; |
| } |
| if(BCMBAL_ACTION_CMD_ID_IS_SET(&(p_flow->data.action), BCMBAL_ACTION_CMD_ID_XLATE_OUTER_TAG) != |
| BCMBAL_ACTION_CMD_ID_IS_SET(&(p_ref_flow->data.action), BCMBAL_ACTION_CMD_ID_XLATE_OUTER_TAG)) |
| { |
| BCM_LOG(INFO, log_id_sw_util, " flow %d upstream/downstream outer vlan translation not symmetrical \n", p_flow->key.flow_id ); |
| ret = BCMOS_FALSE; |
| } |
| |
| } |
| |
| return ret; |
| } |
| |
| #endif /* #ifndef TEST_SW_UTIL_LOOPBACK */ |
| |
| /*@}*/ |
| |