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