blob: bf8067cc1ce23f44e1a82f7a10299f43fa1b95a5 [file] [log] [blame]
/******************************************************************************
*
* <: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 */
/*@}*/