blob: 42a9e9588b770bb1775f67d7e5d7c06c51b24d0d [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.
*
* :>
*
*****************************************************************************/
#ifndef TEST_SW_UTIL_LOOPBACK
#include <bal_common.h>
#include <bcm_dev_log.h>
#include <bal_msg.h>
#include "bal_switch_util.h"
#include "bal_switch_acc_term.h"
#include "bal_dpp_acc_term.h"
#include "bal_dpp_qos.h"
#include "bal_dpp_qos_map.h"
#include <bcm/types.h>
#include <bcm/port.h>
#include <bcm/rx.h> /* for dpp rpc rx register callback */
#include <bcm/switch.h>
#include <bcm/l2.h>
/**
* @file bal_dpp_acc_term.c
* @brief BAL Switch util functions that handle access terminal requests on DUNE PACKET PROCESSOR
* @addtogroup sw_util
*
*/
/*@{*/
/* @brief L2 Table Operation Control
*
* This routine set the HW L2 learning and aging
*
* @param unit The device id
* @param flags Operation flags
* BCM_L2_LEARN_CPU 0x20
* BCM_L2_INGRESS_DIST 0x02
* BCM_L2_INGRESS_CENT 0x01
* @param age_seconds L2 entry age out time in seconds
* @return BCM error code
*/
static int sw_util_dpp_l2_entry_control_set(int unit, int flags, int age_seconds)
{
int rv = 0;
rv = bcm_switch_control_set(unit, bcmSwitchL2LearnMode, flags);
if (rv)
{
BCM_LOG(ERROR, log_id_sw_util, " bcm_switch_control_set failed ret = %d\n", rv);
return rv;
}
/* set aging time */
rv = bcm_l2_age_timer_set(unit, age_seconds);
if (rv)
{
BCM_LOG(ERROR, log_id_sw_util, " bcm_l2_age_timer_set failed ret = %d\n", rv);
return rv;
}
else
{
BCM_LOG(INFO, log_id_sw_util, " Set L2 table aging time to %d seconds\n", age_seconds);
}
return rv;
}
/**
* @brief L2 Table event handler
*
* This routine is a callback triggered by HW L2 Table events
*
* @param unit The device id
* @param p_l2addr Pointer to the L2 entry where the event is happen
* @param operation The type of event
* @param userdata Pointer to a user provided data when the handler is registered
*/
static void sw_util_dpp_l2_entry_event_handler(int unit, bcm_l2_addr_t *p_l2addr, int operation, void *userdata)
{
if (p_l2addr == NULL)
{
BCM_LOG(ERROR, log_id_sw_util, " L2 entry callback with NULL L2 address, op = %d\n", operation);
return;
}
if (operation == BCM_L2_CALLBACK_LEARN_EVENT)
{
BCM_LOG(DEBUG, log_id_sw_util, " BCM_L2_CALLBACK_LEARN_EVENT handler\n");
}
else if (operation == BCM_L2_CALLBACK_MOVE_EVENT)
{
BCM_LOG(DEBUG, log_id_sw_util, " BCM_L2_CALLBACK_MOVE_EVENT handler\n");
}
else if (operation == BCM_L2_CALLBACK_AGE_EVENT)
{
BCM_LOG(DEBUG, log_id_sw_util, " BCM_L2_CALLBACK_AGE_EVENT handler\n");
}
else
{
BCM_LOG(DEBUG, log_id_sw_util, " BCM_L2_CALLBACK_OPERATION %d handler\n", operation);
}
BCM_LOG(DEBUG, log_id_sw_util, " MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
(0xff & p_l2addr->mac[0]),
(0xff & p_l2addr->mac[1]),
(0xff & p_l2addr->mac[2]),
(0xff & p_l2addr->mac[3]),
(0xff & p_l2addr->mac[4]),
(0xff & p_l2addr->mac[5]) );
if(!(p_l2addr->flags & BCM_L2_MCAST))
{
BCM_LOG(DEBUG, log_id_sw_util, " UC VID=0x%x| PORT=0x%08x\n", p_l2addr->vid, p_l2addr->port);
/* print_gport_part - p_l2addr->port */
{
int a = -1, b = 0;
char* type = "";
if (a==-1){
a=BCM_GPORT_LOCAL_GET(p_l2addr->port);
type ="local";
}
if (a==-1){
a=BCM_GPORT_MODPORT_MODID_GET(p_l2addr->port);
b=BCM_GPORT_MODPORT_PORT_GET(p_l2addr->port);
type ="modport";
}
if (a==-1){
a=BCM_GPORT_TRUNK_GET(p_l2addr->port);
type ="trunk";
}
if (a==-1){
a=BCM_GPORT_MCAST_GET(p_l2addr->port);
type ="mact";
}
if (a==-1){
a=BCM_GPORT_MPLS_PORT_ID_GET(p_l2addr->port);
type ="mpls_port";
}
if (a==-1){
a=BCM_GPORT_VLAN_PORT_ID_GET(p_l2addr->port);
type ="vlan_port";
}
if (a==-1){
a=BCM_GPORT_SYSTEM_PORT_ID_GET(p_l2addr->port);
type ="sys_port";
}
if (a==-1){
a=BCM_GPORT_MIRROR_GET(p_l2addr->port);
}
BCM_LOG(DEBUG, log_id_sw_util, " GPORT %s <0x%x,%d>\n", type, a, b);
}
}
else
{
BCM_LOG(DEBUG, log_id_sw_util, " MC 0x%08x\n",p_l2addr->l2mc_group);
}
BCM_LOG(DEBUG, log_id_sw_util, " static %d|\n", (p_l2addr->flags & BCM_L2_STATIC)!=0 );
}
/**
* @brief Connect access terminal with DPP as part of the components
*
* This routine is called by sw_util_access_terminal_connect in the BAL core
* to execute DPP specific API for access_terminal_connect request
*
* @param p_net_map Pointer to the net ports mapping from logical numbrer to physical number
* @param p_pon_map Pointer to the pon ports mapping from logical numbrer to physical number
* @return bcmos_errno
*/
bcmos_errno sw_util_dpp_acc_term_connect(bal_swapp_port *p_net_map, bal_swapp_port *p_pon_map )
{
bcmos_errno ret = BCM_ERR_OK;
int rc = 0;
bal_swapp_port *port;
BCM_LOG(INFO, log_id_sw_util, " DPP - Got a access terminal CONNECT\n");
do
{
/* setup the device ID - This is very hardware specific */
port = p_net_map;
/* -1 indicate the end of table */
while(port->pbm_id != -1)
{
/* the default TPID is 0x8100, add 0x88a8 to the allow TPID */
rc = bcm_port_tpid_delete_all(port->device_id, port->pbm_id);
if (rc)
{
BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to clear the nni TPID list on interface %d\n", port->pbm_id);
ret = BCM_ERR_INTERNAL;
}
rc = bcm_port_tpid_add(port->device_id, port->pbm_id, 0x8100, 0);
if (rc)
{
BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to add 0x8100 to the nni TPID list on interface %d\n", port->pbm_id);
ret = BCM_ERR_INTERNAL;
}
rc = bcm_port_tpid_add(port->device_id, port->pbm_id, 0x88a8, 0);
if (rc)
{
BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to add 0x88a8 to the nni TPID list on interface %d\n", port->pbm_id);
ret = BCM_ERR_INTERNAL;
}
port++;
}
port = p_pon_map;
while(port->pbm_id != -1)
{
/* the default TPID is 0x8100, add 0x88a8 to the allow TPID */
rc = bcm_port_tpid_delete_all(port->device_id, port->pbm_id);
if (rc)
{
BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to clear the pon TPID list on interface %d\n", port->pbm_id);
ret = BCM_ERR_INTERNAL;
}
rc = bcm_port_tpid_add(port->device_id, port->pbm_id, 0x8100, 0);
if (rc)
{
BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to add 0x8100 to the pon TPID list on interface %d\n", port->pbm_id);
ret = BCM_ERR_INTERNAL;
}
rc = bcm_port_tpid_add(port->device_id, port->pbm_id, 0x88a8, 0);
if (rc)
{
BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to add 0x88a8 to the pon TPID list on interface %d\n", port->pbm_id);
ret = BCM_ERR_INTERNAL;
}
port++;
}
if(ret != BCM_ERR_OK)
{
/* exit if port init failed */
break;
}
/* Remove all ports from VLAN 1, so the L2 broadcast won't send to CPU port by default */
bcm_vlan_gport_delete_all(bal_bcm_dft_dev_get(), 1);
/* configure all qos map tables */
ret = bal_sw_dpp_pcp_remark_maps_init(bal_bcm_dft_dev_get());
if (ret)
{
BCM_LOG(WARNING, log_id_sw_util, " DPP - fail to init qos map tables\n");
break;
}
/* init the DS QOS hierarchical scheduling - ignore error if HW dos not support QOS */
ret =bal_sw_dpp_qos_init(bal_bcm_dft_dev_get(), bal_bcm_intf_maptable_get());
if (ret)
{
BCM_LOG(WARNING, log_id_sw_util, " DPP - fail to init qos HR scheduler\n");
break;
}
rc = bcm_l2_addr_register(bal_bcm_dft_dev_get(), sw_util_dpp_l2_entry_event_handler, NULL);
if (rc)
{
BCM_LOG(WARNING, log_id_sw_util, " DPP - fail to register l2_addr callback \n");
ret = BCM_ERR_INTERNAL;
break;
}
rc = sw_util_dpp_l2_entry_control_set(bal_bcm_dft_dev_get(), BCM_L2_LEARN_CPU|BCM_L2_INGRESS_DIST, bal_bcm_l2_age_time_get());
if (rc)
{
BCM_LOG(WARNING, log_id_sw_util, " DPP - fail to set l2_addr control \n");
ret = BCM_ERR_INTERNAL;
break;
}
}while(0);
return ret;
}
/* internal structure used by the trap receiving thread */
typedef struct
{
int udp_port;
pthread_t threadid;
dpp_rx_cb_f callback;
} trap_context;
static trap_context g_trap_ctx = {0};
/* the adjustment needed for ING trap packet header */
#define DEFAULT_SOP_ADJ 2
#define DEFAULT_REASON_ADJ 4
/* compiler restirct the total local variables size < 16384 */
#define DEFAULT_TRAP_BUF_SIZE (10 *1024)
/*
the listener thread that wait for the trap packet_in message from the switch
*/
static void *trap_receive(void *p_user_data)
{
int rc;
int sUDPSocket;
unsigned char cBuffer[DEFAULT_TRAP_BUF_SIZE];
int nBytesRecv = 0;
int nBufSize = DEFAULT_TRAP_BUF_SIZE;
socklen_t nReceiveAddrSize = 0;
int maxfd;
fd_set read_fds;
struct timeval tv;
uint16_t tmp_src_port, src_port;
trap_context *p_trap_ctx = (trap_context *)p_user_data;
uint32_t tmp_reason, reason;
/* Create a connectionless socket */
sUDPSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
/* Check to see if we have a valid socket */
if(sUDPSocket < 0)
{
BCM_LOG(ERROR, log_id_sw_util, " DPP - trap_receive:create socket failed\n");
return NULL;
}
// Setup a bind on the socket, telling us what port and
// adapter to receive datagrams on.
struct sockaddr_in sReceiveFromAddr;
memset(&sReceiveFromAddr, 0, sizeof(struct sockaddr_in));
sReceiveFromAddr.sin_family = AF_INET;
sReceiveFromAddr.sin_port = htons(p_trap_ctx->udp_port);
sReceiveFromAddr.sin_addr.s_addr = htonl(INADDR_ANY);
rc = bind(sUDPSocket, (struct sockaddr *)&sReceiveFromAddr,
sizeof(struct sockaddr_in));
if (rc < 0)
{
BCM_LOG(ERROR, log_id_sw_util, " DPP - trap_receive:bind failed\n");
return NULL;
}
BCM_LOG(INFO, log_id_sw_util, " DPP - trap_receive start listen at %d\n", p_trap_ctx->udp_port);
// Receive a datagram from another device
while(1)
{
FD_ZERO(&read_fds);
FD_SET(sUDPSocket, &read_fds);
maxfd = sUDPSocket;
/* Set the timeout */
tv.tv_sec = 3;
tv.tv_usec = 0; /* 3 seconds */
rc = select(maxfd + 1, &read_fds, NULL, NULL, &tv);
if (rc < 0)
{
BCM_LOG(ERROR, log_id_sw_util, " DPP - trap_receive:select failed err = %d\n", rc);
break;
}
if (rc == 0) /* timeout */
{
continue;
}
// Get the datagrama
nBytesRecv = recvfrom(sUDPSocket, cBuffer, nBufSize, 0,
(struct sockaddr *) &sReceiveFromAddr,
&nReceiveAddrSize);
BCM_LOG(INFO, log_id_sw_util, " DPP - Got %d bytes message \n", nBytesRecv);
/* the first 4 bytes are reason */
/* compiler generate cast-align warning -> p_reason = (uint32_t *)(cBuffer); */
memcpy(&tmp_reason, cBuffer, sizeof(uint32_t) );
reason = ntohl(tmp_reason);
/* the next 2 bytes are srouce port */
/* compiler generate cast-align warning -> p_src_port = (uint16 *)(cBuffer + DEFAULT_REASON_ADJ); */
memcpy(&tmp_src_port, cBuffer + DEFAULT_REASON_ADJ, sizeof(uint16_t));
src_port = ntohs(tmp_src_port);
/* call the register callback here - set unit to 0 as it is don't care */
if(p_trap_ctx->callback)
{
p_trap_ctx->callback(0, src_port, reason, cBuffer+DEFAULT_SOP_ADJ+DEFAULT_REASON_ADJ, nBytesRecv-DEFAULT_SOP_ADJ-DEFAULT_REASON_ADJ);
}
}
close(sUDPSocket);
return NULL;
}
/**
* @brief Start a receiving thread and add the callbck to process CPU trapped packets
*
* This routine is called by sw_util_access_terminal_connect in the BAL core
* to execute DPP specific API for process the trapping packets
*
* @param unit the switch device number the callback applied
* @param cb_f a callback function that process the trapped packets
* @return bcmos_errno
*/
bcmos_errno sw_util_dpp_rx_cb_register(uint32_t unit, dpp_rx_cb_f cb_f)
{
int ret;
/* if the receiving thread already started, return error */
if (g_trap_ctx.threadid)
{
BCM_LOG(ERROR, log_id_sw_util, " DPP - There is an existing trap receiving thread\n");
return BCM_ERR_INTERNAL;
}
g_trap_ctx.udp_port = bal_bcm_trap_rcv_port_get();
g_trap_ctx.callback = cb_f;
/* use default attribute to create the thread */
ret = pthread_create(&g_trap_ctx.threadid, NULL, &trap_receive, &g_trap_ctx);
if(ret)
{
BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to create trap receiving thread - %d\n", ret);
return BCM_ERR_INTERNAL;
}
return BCM_ERR_OK;
}
/* To make compiler happy when build with ESW switch only */
#ifdef ESW_SWITCH
void dpp_dft_rx_cb(int unit, int dst_port, int reason, unsigned char *payload, int payload_len)
{
return;
}
#else
extern void dpp_dft_rx_cb(int unit, int dst_port, int reason, unsigned char *payload, int payload_len);
#endif
#define L2_HEADER_SIZE (12) /* bytes */
#define TUNNEL_TAG_TPID_LEN (2) /* bytes - TPID (2 bytes) */
#define TUNNEL_TAG_PBITS_CFI_VLAN_ID_LEN (2) /* PBITS+CFI+VLAN ID (2 bytes) */
#define TUNNEL_TAG_SIZE (TUNNEL_TAG_TPID_LEN + TUNNEL_TAG_PBITS_CFI_VLAN_ID_LEN)
bcmos_errno sw_util_dpp_pkt_send(int target_port_id,
int reason,
unsigned char *p_user_pkt,
int user_pkt_len,
trap_target target_device,
int target_tunnel_id)
{
bcmos_errno ret = BCM_ERR_OK;
char *p_pktout_msg;
uint8_t dst_port_id;
int dst_device_id;
char *p_pktout_ptr;
uint32_t total_msg_size;
uint32_t uint32_num;
uint16_t uint16_num;
/*
* Allocate a message to be sent to the bcm.user instance. This will contain
* the packet to be sent out of the switch, as well has header metadata.
*/
p_pktout_msg = bcmos_calloc(user_pkt_len +
DEFAULT_SOP_ADJ +
DEFAULT_REASON_ADJ +
TUNNEL_TAG_SIZE);
if(NULL == p_pktout_msg)
{
return BCM_ERR_NOMEM;
}
/* translate the output port to the bcm.user's mapping */
switch(reason)
{
case REASON_SEND_TO_NNI:
dst_port_id = bal_bcm_net_inf_pbm_get(target_port_id);
dst_device_id = bal_bcm_net_inf_dev_get(target_port_id);
break;
case REASON_SEND_TO_PON:
dst_port_id = bal_bcm_pon_inf_pbm_get(target_port_id);
dst_device_id = bal_bcm_pon_inf_dev_get(target_port_id);
break;
default:
return BCM_ERR_PARM;
break;
}
/* Format of the message to the bcm.user instance is:
*
* reason code: 4 bytes
* dst_port: 2 bytes
* user packet: pkt_size bytes
*/
/* Insert the 4 bytes with reason code */
uint32_num = (htonl(reason));
memcpy(p_pktout_msg, &uint32_num, sizeof(uint32_num));
/* Replace 2 bytes with packet_out destination port info. This is the bcm.user's
* mapping of output port (NNI or PON) to BCM SDK port mapping */
uint16_num = htons(dst_port_id & 0xff); /* dst_port contents is actually only 1 byte */
memcpy(&p_pktout_msg[DEFAULT_REASON_ADJ], &uint16_num, sizeof(uint16_num));
/* Copy in the user packet to send to the bcm.user for transmission out of the switch.
* Remember to insert the proper tunnel tag in the message for out_ports that are
* associated with the PON
*/
p_pktout_ptr = p_pktout_msg + DEFAULT_REASON_ADJ + DEFAULT_SOP_ADJ;
total_msg_size = user_pkt_len + DEFAULT_REASON_ADJ + DEFAULT_SOP_ADJ;
if(reason == REASON_SEND_TO_NNI)
{
/* Copy in the entire packet as-is */
memcpy(p_pktout_ptr, p_user_pkt, user_pkt_len);
}
else
{
uint16_t pktout_offset = 0;
/* Copy in the L2 header (MAC DA/SA) */
memcpy(&p_pktout_ptr[pktout_offset], p_user_pkt, L2_HEADER_SIZE);
/* Point to the tunnel tag area in the outgoing message */
pktout_offset += L2_HEADER_SIZE;
/* Insert the TPID in the tag for the output destination */
uint16_num = htons(0x8100);
memcpy(&p_pktout_ptr[pktout_offset], &uint16_num, sizeof(uint16_num));
/* Point to the remainder of the outgoing message */
pktout_offset += TUNNEL_TAG_TPID_LEN;
/* Insert the tunnel tag vlan ID for the output destination */
uint16_num = htons(target_tunnel_id);
memcpy(&p_pktout_ptr[pktout_offset], &uint16_num, sizeof(uint16_num));
/* Point to the remainder of the outgoing message */
pktout_offset += TUNNEL_TAG_PBITS_CFI_VLAN_ID_LEN;
/* Copy in the rest of the message */
memcpy(&p_pktout_ptr[pktout_offset],
(p_user_pkt + L2_HEADER_SIZE),
(user_pkt_len - L2_HEADER_SIZE));
total_msg_size += TUNNEL_TAG_SIZE;
}
BCM_LOG(DEBUG, log_id_sw_util, "Packet send (user pkt_size:%d) destined for %s port %d\n",
user_pkt_len,
(reason == REASON_SEND_TO_PON) ? "PON" : "NNI",
dst_port_id);
if(reason == REASON_SEND_TO_PON)
{
BCM_LOG(DEBUG, log_id_sw_util, "Sending via GEM %d\n", target_tunnel_id);
}
if (bal_bcm_use_rpc_get())
{
/* On systems where BAL runs remotely from the switch hardware, we
* send the packet out message to the remote bcm.user process.
* That process then sends the attached user packet to the specified switch port.
*/
sendto(target_device.socket,
p_pktout_msg,
total_msg_size, 0,
(struct sockaddr *) &(target_device.addr),
sizeof(struct sockaddr_in));
}
else
{
/* On systems where BAL runs on the CPU co-located with the switch hardware,
* we send the attached user packet to the specified switch port directly.
*/
total_msg_size = total_msg_size - ( DEFAULT_REASON_ADJ + DEFAULT_SOP_ADJ );
dpp_dft_tx_cb(dst_device_id,
dst_port_id,
reason,
(unsigned char *)p_pktout_ptr,
total_msg_size);
}
bcmos_free(p_pktout_msg);
BCM_LOG(INFO, log_id_sw_util, "CPU packet msg sent to bcm.user for packet with msg size of %d (payload size: %d)\n",
total_msg_size, user_pkt_len);
return ret;
}
/*@}*/
#endif /* #ifndef TEST_SW_UTIL_LOOPBACK */