Files
m241Neelima/components/ip_net2-6.9/ipnet2/src/ipnet_svlan.c
T
2026-07-30 11:19:44 +05:30

2438 lines
82 KiB
C

/* ipnet_svlan.c - 802.1ad VLAN stacking */
/*
* Copyright (c) 2010 Wind River Systems, Inc.
*
* The right to copy, distribute or otherwise make use of this software
* may be licensed only pursuant to the terms of an applicable Wind River
* license agreement. No license to Wind River intellectual property rights
* is granted herein. All rights not licensed by Wind River are reserved
* by Wind River.
*/
/*
modification history
--------------------
01f,15dec10,tlu Reduce show routine data
01e,24nov10,tlu Set C-VLAN priority to 0 when S-VLAN pkts with only S-VLAN
tag exit provider network
01d,28oct10,tlu Pass IP_FALSE to pkt clone function and update fast fwd
entry only when reset ingress/egess rule
01c,09jul10,tlu Reset port type after clearing S-VLAN configuration on NAE
01b,19feb10,tlu Add fast forwarding on NAE
01a,10sep09,tlu Created.
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
* Processing of 802.1ad VLAN stacking.
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#ifndef IPCOM_KERNEL
#define IPCOM_KERNEL
#endif
#include "ipnet_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#ifdef IPNET_USE_VLAN_STACKING
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_clib.h>
#include <ipcom_cstyle.h>
#include <ipcom_err.h>
#include <ipcom_hash.h>
#include <ipcom_type.h>
#include <ipcom_syslog.h>
#include "ipnet.h"
#include "ipnet_h.h"
#include "ipnet_eth.h"
#include "ipnet_svlan.h"
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
#include <ipcom_forwarder.h>
#endif
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/* The compile flag, turning on it will build debug message into image */
/* #define SVLAN_DEBUG */
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
IP_EXTERN int ipcom_drv_eth_set_stpid(Ipcom_netif *netif, Ip_u16 stpid);
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
* Source MAC addresses learned/seen from this interface, which means
* a station owning the MAC address is on the link, used to segregate
* and optimize the VLAN forwarding traffic.
*/
Ipcom_hash *vlan_stations;
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
IP_STATIC int ipnet_vlan_del_fwd(Ipnet_netif *netif, Ip_u16 vid);
/*
*===========================================================================
* ipnet_vlan_fwd_obj_hash
*===========================================================================
* Description: Computes a hash value for the VLAN forwarding table.
* Parameters: netif - A vlan interface.
* Returns: The hash value.
*
*/
IP_STATIC unsigned
ipnet_vlan_fwd_obj_hash(Ipnet_vlan_fwd *vlan_fwd)
{
return ipcom_hash_update(&vlan_fwd->vid, sizeof(vlan_fwd->vid), 0);
}
/*
*===========================================================================
* ipnet_vlan_fwd_key_hash
*===========================================================================
* Description: Computes a hash value for the specified interface index.
* Parameters: ifindex - A virtual interface index.
* Returns: The hash value.
*
*/
IP_STATIC unsigned
ipnet_vlan_fwd_key_hash(Ip_u16 *vid)
{
return ipcom_hash_update(vid, sizeof(*vid), 0);
}
/*
*===========================================================================
* ipnet_vlan_fwd_hash_cmp
*===========================================================================
* Description: Compares if the vlan interface and the interface index matches.
* Parameters: netif - An vlan interface
* ifindex - A pointer to a interface index.
* Returns: IP_TRUE if the interface matches the virtual interface.
*
*/
IP_STATIC Ip_bool
ipnet_vlan_fwd_hash_cmp(Ipnet_vlan_fwd *vlan_fwd, Ip_u16 *vid)
{
return vlan_fwd->vid == *vid;
}
/*
*===========================================================================
* ipnet_vlan_scfwd_obj_hash
*===========================================================================
* Description: Computes a hash value for the S-VLAN to C-VLAN forwarding table.
* Parameters: sc_fwd - A S-VLAN to C-VLAN forwarding entry.
* Returns: The hash value.
*
*/
IP_STATIC unsigned
ipnet_vlan_scfwd_obj_hash(Ipnet_vlan_scfwd *sc_fwd)
{
return ipcom_hash_update(&sc_fwd->vid, sizeof(sc_fwd->vid), 0);
}
/*
*===========================================================================
* ipnet_vlan_scfwd_key_hash
*===========================================================================
* Description: Computes a hash value for the specified customer VID.
* Parameters: vid - A customer VID.
* Returns: The hash value.
*
*/
IP_STATIC unsigned
ipnet_vlan_scfwd_key_hash(Ip_u16 *cvid)
{
return ipcom_hash_update(cvid, sizeof(*cvid), 0);
}
/*
*===========================================================================
* ipnet_vlan_scfwd_hash_cmp
*===========================================================================
* Description: Compares if the S-to-C fwd entry and the customer VID matches.
* Parameters: sc_fwd - A S-VLAN to C-VLAN forwarding entry
* cvid - A pointer to a customer VID .
* Returns: IP_TRUE if the forwarding entry matches the C-VID.
*
*/
IP_STATIC Ip_bool
ipnet_vlan_scfwd_hash_cmp(Ipnet_vlan_scfwd *sc_fwd, Ip_u16 *cvid)
{
return sc_fwd->vid == *cvid;
}
/*
*===========================================================================
* ipnet_vlan_mac_obj_hash
*===========================================================================
* Description: Computes a hash value for the VLAN mac station.
* Parameters: station - A vlan station structure.
* Returns: The hash value.
*
*/
IP_STATIC unsigned
ipnet_vlan_mac_obj_hash(Ipnet_vlan_station_cache *station)
{
Ip_u8 key[8];
/* form the hash key: mac address + svid */
ipcom_memcpy(key, station->macAddr, 6);
*(Ip_u16 *)(&(key[6])) = station->svid;
return ipcom_hash_update(key, 8, 0);
}
/*
*===========================================================================
* ipnet_vlan_mac_key_hash
*===========================================================================
* Description: Computes a hash value for the specified mac address.
* Parameters: key - MAC address + S-VID.
* Returns: The hash value.
*
*/
IP_STATIC unsigned
ipnet_vlan_mac_key_hash(Ip_u8 *key)
{
return ipcom_hash_update(key, 8, 0);
}
/*
*===========================================================================
* ipnet_vlan_mac_hash_cmp
*===========================================================================
* Description: Compares if the vlan MAC and the MAC matches.
* Parameters: station - An MAC station
* key - MAC address + S-VID.
* Returns: IP_TRUE if the station matches.
*
*/
IP_STATIC Ip_bool
ipnet_vlan_mac_hash_cmp(Ipnet_vlan_station_cache *station, Ip_u8 *key)
{
return (IPNET_ARE_ETHADDR_EQUAL(station->macAddr, key) &&
(*(Ip_u16 *)(&(key[6])) == station->svid));
}
/*
*===========================================================================
* ipnet_vlan_find_fwd_port
*===========================================================================
* Description: Search a VLAN forwarding port list to find a matching port.
* Parameters: fwd_port_list - The VLAN forwarding port list
* ifindex - The interface index
* Returns: matching port, IP_NULL if not found
*
*/
IP_STATIC Ipnet_vlan_fwd_port *
ipnet_vlan_find_fwd_port(Ipcom_list *fwd_port_list, Ip_u32 ifindex)
{
Ipnet_vlan_fwd_port *port;
for (port = IPCOM_LIST_FIRST(fwd_port_list);
port != IP_NULL;
port = IPCOM_LIST_NEXT(&port->next))
{
if (port->netif->ipcom.ifindex == ifindex)
return port;
}
return IP_NULL;
}
/*
*===========================================================================
* ipnet_vlan_mac_type
*===========================================================================
* Description: Identify a MAC address type (unicast/multicast/broadcast)
* Parameters: mac_addr - a MAC address
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC Ipnet_vlan_addr_type
ipnet_vlan_mac_type(Ip_u8 *mac_addr)
{
if (IPNET_IS_ETHADDR_MCAST(mac_addr))
{
if (IPNET_ARE_ETHADDR_EQUAL(mac_addr, ipnet_eth_addr_broadcast))
return IPNET_VLAN_ADDR_BROADCAST;
else
return IPNET_VLAN_ADDR_MULTICAST;
}
return IPNET_VLAN_ADDR_UNICAST;
}
/*
*===========================================================================
* ipnet_vlan_find_mac
*===========================================================================
* Description: Search the station list to find whether it contains the
* same MAC address and S-VID.
* Parameters: netif - The VLAN port
* mac_addr - The MAC address
* svid - S-VID
* if_lkup - Whether on the same interface
* Returns: The station entry if found, IP_NULL if not found
*
*/
IP_STATIC Ipnet_vlan_station_cache *
ipnet_vlan_find_mac(Ipnet_netif *netif, Ip_u8 *mac_addr, Ip_u16 svid,
Ip_bool if_lkup)
{
Ipnet_vlan_station_cache *station;
Ip_time_t current_time = ipcom_time(0);
Ip_u8 key[8];
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_find_mac, netif %s, MAC %x:%x:%x:%x:%x:%x, svid %d, if_lkup %d\n",
netif->ipcom.name, *mac_addr, *(mac_addr+1), *(mac_addr+2),
*(mac_addr+3), *(mac_addr+4), *(mac_addr+5), svid, if_lkup);
#endif
/* form the hash key: mac address + svid */
ipcom_memcpy(key, mac_addr, 6);
*(Ip_u16 *)(&(key[6])) = svid;
station = ipcom_hash_get(vlan_stations, key);
if (station)
{
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_find_mac, found station->netif %s\n",
station->netif->ipcom.name);
#endif
/* found the station */
/* Update the last used time, interface */
station->last_used_time = current_time;
if (!if_lkup && netif) station->netif = netif;
if (if_lkup && netif &&
ipcom_strcmp(station->netif->ipcom.name, netif->ipcom.name))
return IP_NULL;
else
return station;
}
return IP_NULL;
}
/*
*===========================================================================
* ipnet_vlan_find_svid
*===========================================================================
* Description: Search a customer edge/network port to find whether a S-VID
* is configured for the port, and return its corresponding C-VID
* Parameters: netif - The customer edge/network port
* svid - The service VID
* Returns: corresponding C-VID, 0 if not found
*
*/
IP_STATIC Ip_u16
ipnet_vlan_find_svid(Ipnet_netif *netif, Ip_u16 svid)
{
Ip_u16 i, j, vid;
Ipnet_vlan_fwd *cs_fwd = IP_NULL;
for (i = 0; i < 128; i++)
{
for (j = 0; j < 32; j++)
if (netif->eth->vlan_param.vids[i] & (1 << j))
{
vid = (Ip_u16)(i * 32 + j);
if (netif->eth->vlan_param.vlan_fwd)
cs_fwd = ipcom_hash_get(netif->eth->vlan_param.vlan_fwd,
&vid);
if (cs_fwd && (cs_fwd->svid == svid))
return vid;
}
}
return 0;
}
/*
*===========================================================================
* ipnet_vlan_find_next_svid
*===========================================================================
* Description: Search a customer edge/network port to find whether a S-VID
* is configured for the port, and return the next C-VID after
* the passed in C-VID
* Parameters: netif - The customer edge/network port
* cvid - The customer VID
* svid - The service VID
* Returns: next C-VID, 0 if not found
*
*/
IP_STATIC Ip_u16
ipnet_vlan_find_next_svid(Ipnet_netif *netif, Ip_u16 cvid, Ip_u16 svid)
{
Ip_u16 i, j, vid;
Ipnet_vlan_fwd *cs_fwd = IP_NULL;
Ip_bool found_cvid = IP_FALSE;
for (i = 0; i < 128; i++)
{
for (j = 0; j < 32; j++)
if (netif->eth->vlan_param.vids[i] & (1 << j))
{
vid = (Ip_u16)(i * 32 + j);
if (vid < cvid) continue;
if (netif->eth->vlan_param.vlan_fwd)
cs_fwd = ipcom_hash_get(netif->eth->vlan_param.vlan_fwd,
&vid);
if (cs_fwd && (cs_fwd->vid == cvid))
{
found_cvid = IP_TRUE;
continue;
}
else if (found_cvid && cs_fwd && (cs_fwd->svid == svid))
return vid;
}
}
return 0;
}
/*
*===========================================================================
* ipnet_vlan_add_cs_fwd
*===========================================================================
* Description: Add a provider network port to a customer edge/network port's
* forwarding port list
* Parameters: netif - The customer edge/network port
* vid - The customer VID
* svid - The service VID
* snetif - The provider network port
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_vlan_add_cs_fwd(Ipnet_netif *netif, Ip_u16 vid, Ip_u16 svid,
Ipnet_netif *snetif)
{
Ipnet_vlan_fwd *vlan_fwd = IP_NULL;
Ipnet_vlan_fwd_port *vlan_fwd_port = IP_NULL;
Ip_u8 i;
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_add_cs_fwd, netif %s, vid %d, svid %d, snetif %s\n",
netif->ipcom.name, vid, svid, snetif?snetif->ipcom.name:"NULL");
#endif
if (netif->eth->vlan_param.vlan_fwd == IP_NULL)
{
/* Create VLAN fwd hash list */
netif->eth->vlan_param.vlan_fwd = ipcom_hash_new(
(Ipcom_hash_obj_func) ipnet_vlan_fwd_obj_hash,
(Ipcom_hash_key_func) ipnet_vlan_fwd_key_hash,
(Ipcom_hash_cmp_func) ipnet_vlan_fwd_hash_cmp);
if (netif->eth->vlan_param.vlan_fwd == IP_NULL)
{
IPCOM_LOG1(DEBUG, "ipnet_vlan_add_cs_fwd: %s fail to alloc VLAN fwd hash list",
netif->ipcom.name);
return -IP_ERRNO_ENOMEM;
}
}
/* Get the VLAN fwd entry corresponding to the vid */
vlan_fwd = ipcom_hash_get(netif->eth->vlan_param.vlan_fwd, &vid);
if (!vlan_fwd)
{
/* Create a VLAN fwd port list entry */
vlan_fwd = ipcom_malloc(sizeof(Ipnet_vlan_fwd));
if (vlan_fwd == IP_NULL)
{
IPCOM_LOG1(DEBUG, "ipnet_vlan_add_cs_fwd: %s fail to alloc VLAN fwd port list",
netif->ipcom.name);
return -IP_ERRNO_ENOMEM;
}
/* init fields */
ipcom_memset(vlan_fwd, 0, sizeof(Ipnet_vlan_fwd));
vlan_fwd->vid = vid;
vlan_fwd->svid = svid;
vlan_fwd->ports.port_list = ipcom_malloc(sizeof(Ipcom_list));
if (!vlan_fwd->ports.port_list)
{
IPCOM_LOG0(DEBUG, "ipnet_vlan_add_cs_fwd: fail to alloc port list");
ipcom_free(vlan_fwd);
return -IP_ERRNO_ENOMEM;
}
ipcom_list_init(vlan_fwd->ports.port_list);
for (i = 0; i < 8; i++) vlan_fwd->pri_mapping[i] = i;
/* add vlan fwd port list entry */
if (ipcom_hash_add(netif->eth->vlan_param.vlan_fwd,
vlan_fwd) != IPCOM_SUCCESS)
{
IPCOM_LOG2(DEBUG, "Fail to add %s to VID (%d) fwd list",
netif->ipcom.name, vid);
ipcom_free(vlan_fwd->ports.port_list);
ipcom_free(vlan_fwd);
return -IP_ERRNO_ENOMEM;
}
}
if ((snetif == IP_NULL) ||
ipnet_vlan_find_fwd_port(vlan_fwd->ports.port_list,
snetif->ipcom.ifindex))
/* No port, or port already in the forwarding list */
return 0;
/* Add port vnetif to the VLAN fwd port list entry */
vlan_fwd_port = ipcom_malloc(sizeof(Ipnet_vlan_fwd_port));
if (!vlan_fwd_port)
{
IPCOM_LOG0(DEBUG, "ipnet_vlan_add_cs_fwd: fail to alloc VLAN fwd port list");
return -IP_ERRNO_ENOMEM;
}
ipcom_memset(vlan_fwd_port, 0, sizeof(Ipnet_vlan_fwd_port));
vlan_fwd_port->netif = snetif;
ipcom_list_insert_last(vlan_fwd->ports.port_list, &vlan_fwd_port->next);
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
/* Add to fast path S-VLAN fwd cache */
ipcom_forwarder_svlan_add_cache_entry(netif, vid, svid,
vlan_fwd->pri_mapping, snetif);
#endif
return 0;
}
/*
*===========================================================================
* ipnet_vlan_add_sc_fwd
*===========================================================================
* Description: Add a customer edge/network port to a provider network port's
* forwarding port list
* Parameters: netif - The provider network port
* vid - The customer VID
* svid - The service VID
* cnetif - The customer edge/network port
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_vlan_add_sc_fwd(Ipnet_netif *netif, Ip_u16 vid, Ip_u16 svid,
Ipnet_netif *cnetif)
{
Ipnet_vlan_fwd *vlan_fwd = IP_NULL;
Ipnet_vlan_scfwd *vlan_fwd_ports = IP_NULL;
Ipnet_vlan_fwd_port *vlan_fwd_port = IP_NULL;
if (netif->eth->vlan_param.vlan_fwd == IP_NULL)
{
/* Create VLAN fwd hash list */
netif->eth->vlan_param.vlan_fwd = ipcom_hash_new(
(Ipcom_hash_obj_func) ipnet_vlan_fwd_obj_hash,
(Ipcom_hash_key_func) ipnet_vlan_fwd_key_hash,
(Ipcom_hash_cmp_func) ipnet_vlan_fwd_hash_cmp);
if (netif->eth->vlan_param.vlan_fwd == IP_NULL)
{
IPCOM_LOG1(DEBUG, "ipnet_vlan_add_sc_fwd: %s fail to alloc VLAN fwd hash list",
netif->ipcom.name);
return -IP_ERRNO_ENOMEM;
}
}
/* Get the VLAN fwd entry corresponding to the svid */
vlan_fwd = ipcom_hash_get(netif->eth->vlan_param.vlan_fwd, &svid);
if (!vlan_fwd)
{
/* Create a VLAN fwd port list entry */
vlan_fwd = ipcom_malloc(sizeof(Ipnet_vlan_fwd));
if (vlan_fwd == IP_NULL)
{
IPCOM_LOG1(DEBUG, "ipnet_vlan_add_sc_fwd: %s fail to alloc VLAN fwd port list",
netif->ipcom.name);
return -IP_ERRNO_ENOMEM;
}
/* init fields */
ipcom_memset(vlan_fwd, 0, sizeof(Ipnet_vlan_fwd));
vlan_fwd->vid = svid;
vlan_fwd->ports.c_ports = ipcom_hash_new(
(Ipcom_hash_obj_func) ipnet_vlan_scfwd_obj_hash,
(Ipcom_hash_key_func) ipnet_vlan_scfwd_key_hash,
(Ipcom_hash_cmp_func) ipnet_vlan_scfwd_hash_cmp);
if (vlan_fwd->ports.c_ports == IP_NULL)
{
IPCOM_LOG1(DEBUG, "ipnet_vlan_add_sc_fwd: %s fail to alloc C-VLAN fwd hash list",
netif->ipcom.name);
ipcom_free(vlan_fwd);
return -IP_ERRNO_ENOMEM;
}
/* add vlan fwd port list entry */
if (ipcom_hash_add(netif->eth->vlan_param.vlan_fwd,
vlan_fwd) != IPCOM_SUCCESS)
{
IPCOM_LOG2(DEBUG, "Fail to add %s to S-VID (%d) fwd list",
netif->ipcom.name, svid);
ipcom_hash_delete(vlan_fwd->ports.c_ports);
ipcom_free(vlan_fwd);
return -IP_ERRNO_ENOMEM;
}
}
/* Get the C-VLAN fwd entry corresponding to the c-vid */
vlan_fwd_ports = ipcom_hash_get(vlan_fwd->ports.c_ports, &vid);
if (!vlan_fwd_ports)
{
/* Create a VLAN fwd port list entry */
vlan_fwd_ports = ipcom_malloc(sizeof(Ipnet_vlan_scfwd));
if (vlan_fwd_ports == IP_NULL)
{
IPCOM_LOG1(DEBUG, "ipnet_vlan_add_sc_fwd: %s fail to alloc C-VLAN fwd port list",
netif->ipcom.name);
return -IP_ERRNO_ENOMEM;
}
/* init fields */
ipcom_memset(vlan_fwd_ports, 0, sizeof(Ipnet_vlan_scfwd));
vlan_fwd_ports->vid = vid;
vlan_fwd_ports->port_list = ipcom_malloc(sizeof(Ipcom_list));
if (!vlan_fwd_ports->port_list)
{
IPCOM_LOG0(DEBUG, "ipnet_vlan_add_sc_fwd: fail to alloc port list");
ipcom_free(vlan_fwd_ports);
return -IP_ERRNO_ENOMEM;
}
ipcom_list_init(vlan_fwd_ports->port_list);
/* Add C-VLAN fwd port list entry */
if (ipcom_hash_add(vlan_fwd->ports.c_ports,
vlan_fwd_ports) != IPCOM_SUCCESS)
{
IPCOM_LOG2(DEBUG, "Fail to add %s to C-VID (%d) fwd list",
netif->ipcom.name, vid);
ipcom_free(vlan_fwd_ports->port_list);
ipcom_free(vlan_fwd_ports);
return -IP_ERRNO_ENOMEM;
}
}
if (ipnet_vlan_find_fwd_port(vlan_fwd_ports->port_list,
cnetif->ipcom.ifindex))
/* Port already in the forwarding list */
return 0;
/* Add port cnetif to the C-VLAN fwd port list entry */
vlan_fwd_port = ipcom_malloc(sizeof(Ipnet_vlan_fwd_port));
if (!vlan_fwd_port)
{
IPCOM_LOG1(DEBUG, "ipnet_vlan_add_sc_fwd: %s fail to alloc C-VLAN fwd port list",
netif->ipcom.name);
return -IP_ERRNO_ENOMEM;
}
ipcom_memset(vlan_fwd_port, 0, sizeof(Ipnet_vlan_fwd_port));
vlan_fwd_port->netif = cnetif;
ipcom_list_insert_last(vlan_fwd_ports->port_list, &vlan_fwd_port->next);
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
/* Add to fast path S-VLAN fwd cache */
ipcom_forwarder_svlan_add_cache_entry(netif, vid, svid, IP_NULL, cnetif);
#endif
return 0;
}
/*
*===========================================================================
* ipnet_vlan_del_cs_fwd
*===========================================================================
* Description: Delete a provider network port from a customer edge/network
* port's forwarding port list
* Parameters: netif - The customer edge/network port
* vid - The customer VID
* snetif - The provider network port
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_vlan_del_cs_fwd(Ipnet_netif *netif, Ip_u16 vid, Ipnet_netif *snetif)
{
Ipnet_vlan_fwd *vlan_fwd = IP_NULL;
Ipnet_vlan_fwd_port *vlan_fwd_port;
#ifdef SVLAN_DEBUG
ipcom_printf("Enter ipnet_vlan_del_cs_fwd, netif %s, vid %d, snetif %s\n",
netif->ipcom.name, vid, snetif->ipcom.name);
#endif
/* Get the VLAN fwd entry corresponding to the vid */
if (netif->eth->vlan_param.vlan_fwd)
vlan_fwd = ipcom_hash_get(netif->eth->vlan_param.vlan_fwd, &vid);
if (!vlan_fwd) return 0;
vlan_fwd_port = ipnet_vlan_find_fwd_port(vlan_fwd->ports.port_list,
snetif->ipcom.ifindex);
/* Remove and free the port */
if (vlan_fwd_port)
{
ipcom_list_remove(&vlan_fwd_port->next);
ipcom_free(vlan_fwd_port);
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
/* Remove fast path S-VLAN fwd cache entries */
ipcom_forwarder_svlan_del_cache_entry(netif, vid, vlan_fwd->svid,
snetif);
ipcom_forwarder_svlan_del_cache_entry(snetif, vid, vlan_fwd->svid,
netif);
#endif
}
return 0;
}
/*
*===========================================================================
* ipnet_vlan_del_sc_fwd
*===========================================================================
* Description: Delete a customer edge/network port from a provider network
* port's forwarding port list
* Parameters: netif - The provider network port
* vid - The service VID
* cvid - The customer VID
* cnetif - The customer edge/network port
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_vlan_del_sc_fwd(Ipnet_netif *netif, Ip_u16 vid, Ip_u16 cvid,
Ipnet_netif *cnetif)
{
Ipnet_vlan_fwd *vlan_fwd = IP_NULL;
Ipnet_vlan_scfwd *vlan_scfwd = IP_NULL;
Ipnet_vlan_fwd_port *vlan_fwd_port;
#ifdef SVLAN_DEBUG
ipcom_printf("Enter ipnet_vlan_del_sc_fwd, netif %s, vid %d, cvid %d, cnetif %s\n",
netif->ipcom.name, vid, cvid, cnetif->ipcom.name);
#endif
/* Get the VLAN fwd entry corresponding to the svid */
if (netif->eth->vlan_param.vlan_fwd)
vlan_fwd = ipcom_hash_get(netif->eth->vlan_param.vlan_fwd, &vid);
if (!vlan_fwd) return 0;
/* Get the C-VLAN fwd entry corresponding to the cvid */
if (vlan_fwd->ports.c_ports)
vlan_scfwd = ipcom_hash_get(vlan_fwd->ports.c_ports, &cvid);
if (!vlan_scfwd) return 0;
vlan_fwd_port = ipnet_vlan_find_fwd_port(vlan_scfwd->port_list,
cnetif->ipcom.ifindex);
/* Remove and free the port */
if (vlan_fwd_port)
{
ipcom_list_remove(&vlan_fwd_port->next);
ipcom_free(vlan_fwd_port);
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
/* Remove fast path S-VLAN fwd cache entres */
ipcom_forwarder_svlan_del_cache_entry(netif, cvid, vid, cnetif);
ipcom_forwarder_svlan_del_cache_entry(cnetif, cvid, vid, netif);
#endif
}
if (vlan_scfwd->port_list->size == 0)
{
/* No more port, remove and free the vlan_scfwd */
ipcom_hash_remove(vlan_fwd->ports.c_ports, vlan_scfwd);
ipcom_free(vlan_scfwd->port_list);
ipcom_free(vlan_scfwd);
}
if (vlan_fwd->ports.c_ports->elem == 0)
{
/* No more port, remove and free the vlan_fwd */
ipcom_hash_delete(vlan_fwd->ports.c_ports);
ipcom_hash_remove(netif->eth->vlan_param.vlan_fwd, vlan_fwd);
ipcom_free(vlan_fwd);
}
return 0;
}
/*
*===========================================================================
* ipnet_vlan_fill_conf_ports
*===========================================================================
* Description: Fill in ports info for ifconfig show
* Parameters: port_list - The VLAN forwarding port list
* ifindex - The interface index
* Returns: matching port, IP_NULL if not found
*
*/
IP_STATIC void *
ipnet_vlan_fill_conf_ports(Ipcom_list *port_list, struct Ip_vlanreq *vlanreq,
Ip_u8 vid_index, Ip_u8 cvid_index)
{
Ipnet_vlan_fwd_port *port;
Ip_u8 n_port = 0;
for (port = IPCOM_LIST_FIRST(port_list);
port != IP_NULL && n_port < 3;
port = IPCOM_LIST_NEXT(&port->next))
{
/* Record the port */
ipcom_strncpy(
vlanreq->pvlan_fwd[vid_index].port_list[cvid_index].fwd_ports[n_port],
port->netif->ipcom.name, IP_IFNAMSIZ);
vlanreq->pvlan_fwd[vid_index].port_list[cvid_index].fwd_ports[n_port][IP_IFNAMSIZ-1] = '\0';
n_port++;
}
vlanreq->pvlan_fwd[vid_index].port_list[cvid_index].num_fwd_ports = n_port;
return IP_NULL;
}
/*
*===========================================================================
* ipnet_vlan_add_mac
*===========================================================================
* Description: Add a MAC address type to station list
* Parameters: netif - The VLAN interface
* mac_addr - a MAC address
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC void
ipnet_vlan_add_mac(Ipnet_netif *netif, Ip_u8 *mac, Ip_u16 svid)
{
Ip_time_t current_time = ipcom_time(0);
Ipnet_vlan_station_cache *station;
/* Add a new MAC address to the station list */
station = ipcom_malloc(sizeof(Ipnet_vlan_station_cache));
if (!station)
{
IPCOM_LOG1(DEBUG, "Fail to alloc station for %s", netif->ipcom.name);
return;
}
/* Record the last used time */
station->last_used_time = current_time;
/* Record the source MAC address, netif, svid */
ipcom_memcpy(station->macAddr, mac, 6);
station->netif = netif;
station->svid = svid;
/* Add the entry to the station list */
if (ipcom_hash_add(vlan_stations, station) != IPCOM_SUCCESS)
{
IPCOM_LOG1(DEBUG, "Fail to add a MAC station to %s", netif->ipcom.name);
ipcom_free(station);
}
}
/*
*===========================================================================
* ipnet_vlan_output_one_port
*===========================================================================
* Description: Forward an Ethernet VLAN frame out on an VLAN interface
* Parameters: netif - The VLAN interface on which the packet should
* be sent.
* pkt - The ethernet VLAN frame to send
* frame_type - The frame type of the pkt.
* vid - The VID extracted from the received frame.
* svid - The S-VID extracted or mapped from the received C-VID.
* cvid - The C-VID extracted from the received frame.
* vpri - VLAN priority extracted from the received frame.
* svpri - VLAN priority mapped from the received C-VLAN priority.
* link_hdr_size - The link header size.
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int ipnet_vlan_output_one_port(Ipnet_netif *netif,
Ipcom_pkt *pkt,
int frame_type,
Ip_u16 vid,
Ip_u16 svid,
Ip_u16 cvid,
Ip_u8 vpri,
Ip_u8 svpri,
Ip_u16 link_hdr_size)
{
int ret = 0;
Ipnet_pkt_eth *eth;
Ip_u16 tag;
Ip_u8 link_hdr[30];
/* Record the link start pointer and link hdr from the receiving frame */
ipcom_memcpy(link_hdr, &(pkt->data[pkt->start]), link_hdr_size);
#ifdef SVLAN_DEBUG
ipcom_printf("Enter ipnet_vlan_output_one_port, type %d, pkt->start %d\n",
netif->eth->vlan_param.vlan_type, pkt->start);
#endif
switch (netif->eth->vlan_param.vlan_type)
{
case IPNET_VLAN_S_VLAN:
/* provider network port on S-VLAN node: no change in pkt, send out */
goto transmit;
case IPNET_VLAN_SC_VLAN:
/* customer edge/network port forwarding to provider network port */
/* Add S-VLAN tag */
/* Move the start pointer to point to IP data */
pkt->start += link_hdr_size;
if (netif->eth->vlan_param.egress_rule == IPNET_VLAN_EGRESS_TAGGED)
{
/* Insert C-VLAN tag */
tag = (Ip_u16)((vid & 0x0fff) | ((vpri << 13) & 0xe000));
pkt->start -= (int)(IPNET_ETH_HDR_SIZE + IPNET_VLAN_TAG_SIZE);
eth = (Ipnet_pkt_eth *) &pkt->data[pkt->start];
eth->d.vlan.tag_type = IPNET_ETHFRAME_8021Q_VLAN;
eth->d.vlan.pri_cif_vid = ip_htons(tag);
eth->d.vlan.type = (Ip_u16)frame_type;
/* Move back start pointer */
pkt->start += IPNET_ETH_HDR_SIZE;
}
/* Format S-VLAN tag */
tag = (Ip_u16)((svid & 0x0fff) | ((svpri << 13) & 0xe000));
/*
* Hardware does not support inserting svlan to the
* ethernet header. Do software s-vlan tagging
*/
IP_BIT_CLR(pkt->flags, IPCOM_PKT_FLAG_VLAN_TAG);
pkt->start -= (int)(IPNET_ETH_HDR_SIZE + IPNET_VLAN_TAG_SIZE);
eth = (Ipnet_pkt_eth *) &pkt->data[pkt->start];
eth->d.vlan.tag_type = ipnet->vlan_s_tpid_net;
eth->d.vlan.pri_cif_vid = ip_htons(tag);
if (netif->eth->vlan_param.egress_rule == IPNET_VLAN_EGRESS_UNTAGGED)
/* No C-VLAN, set frame type */
eth->d.vlan.type = (Ip_u16)frame_type;
break;
case IPNET_VLAN_CS_VLAN:
/* provider network port forwarding to customer edge/network port */
/* Remove S-VLAN tag -- move start pointer to skip S-VLAN tag */
if (netif->eth->vlan_param.egress_rule == IPNET_VLAN_EGRESS_UNTAGGED)
/* remove C-VLAN tag if there is one */
/* move start pointer to skip S-VLAN and C-VLAN */
pkt->start += (link_hdr_size - IPNET_ETH_HDR_SIZE);
else
{
/* Insert C-VLAN tag */
/* Move the start pointer to point to IP data */
pkt->start += link_hdr_size;
tag = (Ip_u16)((cvid & 0x0fff) | ((vpri << 13) & 0xe000));
pkt->start -= (int)(IPNET_ETH_HDR_SIZE + IPNET_VLAN_TAG_SIZE);
eth = (Ipnet_pkt_eth *) &pkt->data[pkt->start];
#ifdef IPTESTENGINE
eth->d.vlan.tag_type = ip_htons(0x8200);
#else
eth->d.vlan.tag_type = IPNET_ETHFRAME_8021Q_VLAN;
#endif /*IPTESTENGINE */
eth->d.vlan.pri_cif_vid = ip_htons(tag);
eth->d.vlan.type = (Ip_u16)frame_type;
}
break;
default:
/* never happen case */
return -IP_ERRNO_EINVAL;
}
/* Set dst/src ethernet header */
ipcom_memcpy(&(pkt->data[pkt->start]), link_hdr, 12);
transmit:
#ifdef SVLAN_DEBUG
{
int i;
ipcom_printf("ipnet_vlan_output_one_port(tx pkt): if %s, start %d, end %d, length %d\n",
netif->ipcom.name, pkt->start, pkt->end, pkt->end - pkt->start);
for (i = pkt->start; i < pkt->end; i++)
ipcom_printf("%x ", pkt->data[i]);
ipcom_printf("\n");
}
#endif
IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_LINK_VLAN);
pkt->ifindex = netif->ipcom.ifindex;
ret = ipnet_if_output(netif, pkt);
if (IP_UNLIKELY(ret != 0))
{
IP_BIT_CLR(pkt->flags, IPCOM_PKT_FLAG_LINK_VLAN);
IPNET_STATS(eth_output_vlan_drverr++);
}
return ret;
}
/*
*===========================================================================
* ipnet_vlan_common_output
*===========================================================================
* Description: Common code to forward an Ethernet VLAN frame out on an
* VLAN interface
* Parameters: netif - The packet receiving interface
* pkt - The ethernet VLAN frame to send
* frame_type - The frame type of the pkt.
* vid - The VID extracted from the received frame.
* svid - The S-VID extracted or mapped from the received C-VID.
* cvid - The C-VID extracted from the received frame.
* vpri - VLAN priority extracted from the received frame.
* svpri - VLAN priority mapped from the received C-VLAN priority.
* link_hdr_size - The link header size.
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_vlan_common_output(Ipnet_netif *netif,
void *fwd,
Ipcom_pkt *pkt,
int frame_type,
Ip_u16 vid,
Ip_u16 svid,
Ip_u16 cvid,
Ip_u8 vpri,
Ip_u8 svpri,
Ip_u16 link_hdr_size)
{
int ret = 0;
Ipnet_vlan_fwd_port *fwd_port = IP_NULL, *nxt_fwd_port = IP_NULL;
Ipcom_list *port_list;
Ipnet_pkt_eth *eth = (Ipnet_pkt_eth *)&pkt->data[pkt->start];
Ipnet_vlan_addr_type dst_type;
Ipnet_vlan_station_cache *station;
#ifdef SVLAN_DEBUG
ipcom_printf("Enter ipnet_vlan_common_output, if %s, vid %d, svid %d, vpri %d, svpri %d\n",
netif->ipcom.name, vid, svid, vpri, svpri);
#endif
/* check for dst MAC address type */
dst_type = ipnet_vlan_mac_type(eth->dst);
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_common_output, dst_type %d\n", dst_type);
#endif
if (netif->eth->vlan_param.vlan_type != IPNET_VLAN_SC_VLAN)
port_list = ((Ipnet_vlan_fwd *)fwd)->ports.port_list;
else
port_list = ((Ipnet_vlan_scfwd *)fwd)->port_list;
if (((netif->eth->vlan_param.vlan_type != IPNET_VLAN_S_VLAN) &&
port_list->size) ||
(port_list->size > 1))
IPNET_STATS(eth_output_vlan++);
/* Check for the destination MAC address on the fwd port list */
if (dst_type == IPNET_VLAN_ADDR_UNICAST)
{
station = ipnet_vlan_find_mac(IP_NULL, eth->dst, svid, IP_FALSE);
if (station &&
ipnet_vlan_find_fwd_port(port_list, station->netif->ipcom.ifindex))
{
/* Forward on fwd_port only */
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_common_output (one port), fwd_port %s, pkt %x\n",
station->netif->ipcom.name, pkt);
#endif
ret = ipnet_vlan_output_one_port(station->netif, pkt,
frame_type, vid, svid, cvid, vpri, svpri,
link_hdr_size);
return ret;
}
}
for (fwd_port = IPCOM_LIST_FIRST(port_list);
fwd_port != IP_NULL;
fwd_port = nxt_fwd_port)
{
Ipcom_pkt *tx_pkt;
nxt_fwd_port = IPCOM_LIST_NEXT(&fwd_port->next);
if (!ipcom_strcmp(netif->ipcom.name, fwd_port->netif->ipcom.name))
/* don't send out on the same receiving interface */
continue;
if (nxt_fwd_port || (dst_type != IPNET_VLAN_ADDR_UNICAST))
{
/* duplicate the original pkt */
tx_pkt = ipnet_pkt_clone(pkt, IP_FALSE);
if (tx_pkt == IP_NULL)
{
IPCOM_LOG0(WARNING, "ipnet_vlan_common_output: fail to"
"allocate memory to duplicate the packet");
/* tx the original pkt */
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_common_output (tx orig), fwd_port %s, pkt %x\n",
fwd_port->netif->ipcom.name, pkt);
#endif
return ipnet_vlan_output_one_port(fwd_port->netif, pkt,
frame_type, vid, svid, cvid, vpri, svpri,
link_hdr_size);
}
}
else
tx_pkt = pkt;
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_common_output (tx multi), fwd_port %s, tx_pkt %x\n",
fwd_port->netif->ipcom.name, tx_pkt);
#endif
ret = ipnet_vlan_output_one_port(fwd_port->netif, tx_pkt,
frame_type, vid, svid, cvid, vpri, svpri,
link_hdr_size);
}
return ret;
}
/*
*===========================================================================
* ipnet_vlan_add_fwd
*===========================================================================
* Description: add a VLAN forwarding port
* Parameters:
* Returns:
*
*/
IP_STATIC int
ipnet_vlan_add_fwd(Ipnet_netif *netif, Ip_u16 vid, Ip_u16 svid)
{
Ipnet_netif *vnetif;
Ipnet_vlan_fwd *vlan_fwd = IP_NULL;
Ipnet_vlan_fwd_port *vlan_fwd_port = IP_NULL;
Ip_u32 i;
int ret = -IP_ERRNO_EFAULT;
Ip_bool found_sc_port = IP_FALSE;
#ifdef SVLAN_DEBUG
ipcom_printf("Enter ipnet_vlan_add_fwd, netif %s, vid %d, svid %d\n",
netif->ipcom.name, vid, svid);
#endif
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
if (!netif->eth->vlan_param.id)
{
/* Set input hash tag configuration for input port and VID only */
ret = ipcom_forwarder_if_set_vlan_stacking(netif);
if (ret < 0)
{
IPCOM_LOG1(DEBUG, "ipnet_vlan_add_fwd: %s fail to set NAE port",
netif->ipcom.name);
return -IP_ERRNO_ENOMEM;
}
}
#endif
/* Search all the interfaces for the same VLAN configuration */
/*
* IPNET_NETIF_FOR_EACH_ON_VR sets netif to NULL after the for loop,
* this is a performance optimized defination
*/
/* coverity[result_independent_of_operands] */
IPNET_NETIF_FOR_EACH_ON_VR(vnetif, netif->vr_index, i)
{
if ((vnetif->ipcom.type == IP_IFT_ETHER) &&
ipcom_strcmp(vnetif->ipcom.name, netif->ipcom.name))
{
if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_S_VLAN)
{
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_add_fwd, S-VLAN, vnetif %s\n", vnetif->ipcom.name);
#endif
/* A C-VLAN port or a S-VLAN port */
if ((netif->eth->vlan_param.vlan_type !=
vnetif->eth->vlan_param.vlan_type) ||
!(vnetif->eth->vlan_param.vids[svid>>5] &
(1 << (svid&0x1f))))
continue;
/* the vnetif has the same VLAN configuration as netif */
if (vnetif->eth->vlan_param.vlan_fwd == IP_NULL)
{
/* Create VLAN fwd hash list */
vnetif->eth->vlan_param.vlan_fwd = ipcom_hash_new(
(Ipcom_hash_obj_func) ipnet_vlan_fwd_obj_hash,
(Ipcom_hash_key_func) ipnet_vlan_fwd_key_hash,
(Ipcom_hash_cmp_func) ipnet_vlan_fwd_hash_cmp);
if (vnetif->eth->vlan_param.vlan_fwd == IP_NULL)
{
IPCOM_LOG1(DEBUG, "%s fail to alloc VLAN fwd hash list",
vnetif->ipcom.name);
return -IP_ERRNO_ENOMEM;
}
}
if (netif->eth->vlan_param.vlan_fwd == IP_NULL)
{
/* Create VLAN fwd hash list */
netif->eth->vlan_param.vlan_fwd = ipcom_hash_new(
(Ipcom_hash_obj_func) ipnet_vlan_fwd_obj_hash,
(Ipcom_hash_key_func) ipnet_vlan_fwd_key_hash,
(Ipcom_hash_cmp_func) ipnet_vlan_fwd_hash_cmp);
if (netif->eth->vlan_param.vlan_fwd == IP_NULL)
{
IPCOM_LOG1(DEBUG, "%s fail to alloc VLAN fwd hash list", netif->ipcom.name);
ret = -IP_ERRNO_ENOMEM;
goto errout;
}
}
/* Get the VLAN fwd entry corresponding to the vid */
vlan_fwd = ipcom_hash_get(vnetif->eth->vlan_param.vlan_fwd,
&svid);
if (!vlan_fwd)
{
/* Create a VLAN fwd port list entry */
vlan_fwd = ipcom_malloc(sizeof(Ipnet_vlan_fwd));
if (vlan_fwd == IP_NULL)
{
IPCOM_LOG1(DEBUG, "%s fail to alloc VLAN fwd port list",
netif->ipcom.name);
ret = -IP_ERRNO_ENOMEM;
goto errout;
}
/* init fields */
ipcom_memset(vlan_fwd, 0, sizeof(Ipnet_vlan_fwd));
vlan_fwd->vid = svid;
vlan_fwd->ports.port_list=ipcom_malloc(sizeof(Ipcom_list));
if (!vlan_fwd->ports.port_list)
{
IPCOM_LOG0(DEBUG, "ipnet_vlan_add_fwd: fail to alloc port list");
ipcom_free(vlan_fwd);
ret = -IP_ERRNO_ENOMEM;
goto errout;
}
ipcom_list_init(vlan_fwd->ports.port_list);
/* Add port vnetif to the VLAN fwd port list entry */
vlan_fwd_port = ipcom_malloc(sizeof(Ipnet_vlan_fwd_port));
if (!vlan_fwd_port)
{
IPCOM_LOG0(DEBUG, "ipnet_vlan_add_fwd: fail to alloc VLAN fwd port list");
ipcom_free(vlan_fwd->ports.port_list);
ipcom_free(vlan_fwd);
ret = -IP_ERRNO_ENOMEM;
goto errout;
}
ipcom_memset(vlan_fwd_port,0,sizeof(Ipnet_vlan_fwd_port));
vlan_fwd_port->netif = vnetif;
ipcom_list_insert_last(vlan_fwd->ports.port_list,
&vlan_fwd_port->next);
/* add vlan fwd port list entry */
if (ipcom_hash_add(vnetif->eth->vlan_param.vlan_fwd,
vlan_fwd) != IPCOM_SUCCESS)
{
IPCOM_LOG2(DEBUG, "Fail to add %s to VID (%d) fwd list",
vnetif->ipcom.name, svid);
ipcom_free(vlan_fwd);
ipcom_free(vlan_fwd_port);
ret = -IP_ERRNO_ENOMEM;
goto errout;
}
}
/* Add port netif to the VLAN fwd port list entry */
vlan_fwd_port = ipcom_malloc(sizeof(Ipnet_vlan_fwd_port));
if(IP_NULL == vlan_fwd_port)
goto errout;
ipcom_memset(vlan_fwd_port, 0, sizeof(Ipnet_vlan_fwd_port));
vlan_fwd_port->netif = netif;
ipcom_list_insert_last(vlan_fwd->ports.port_list,
&vlan_fwd_port->next);
/* add vlan fwd port list entry */
if (ipcom_hash_add(netif->eth->vlan_param.vlan_fwd,
vlan_fwd) != IPCOM_SUCCESS)
{
IPCOM_LOG2(DEBUG, "Fail to add %s to VID (%d) fwd list",
netif->ipcom.name, svid);
ipcom_list_remove(&vlan_fwd_port->next);
ipcom_free(vlan_fwd_port);
ret = -IP_ERRNO_ENOMEM;
goto errout;
}
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
/* Add to fast path S-VLAN fwd cache */
for (vlan_fwd_port = IPCOM_LIST_FIRST(vlan_fwd->ports.port_list);
vlan_fwd_port && ipcom_strcmp(netif->ipcom.name,
vlan_fwd_port->netif->ipcom.name);
vlan_fwd_port = IPCOM_LIST_NEXT(&vlan_fwd_port->next))
{
ipcom_forwarder_svlan_add_cache_entry(netif, 0,
svid, IP_NULL, vlan_fwd_port->netif);
ipcom_forwarder_svlan_add_cache_entry(vlan_fwd_port->netif,
0, svid, IP_NULL, netif);
}
#endif
/* already added ... break out loop */
break;
}
else if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_CS_VLAN)
{
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_add_fwd, CS-VLAN, vnetif %s\n",
vnetif->ipcom.name);
#endif
/* A customer edge/network port */
if ((vnetif->eth->vlan_param.vlan_type != IPNET_VLAN_SC_VLAN) ||
!(vnetif->eth->vlan_param.vids[svid>>5] &
(1 << (svid&0x1f))))
continue;
found_sc_port = IP_TRUE;
/* The vnetif is a forwarding port */
/* Add vnetif (provider network port) to netif's (customer
edge/network port) forwarding port list */
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_add_fwd(CS), netif %s, vid %d, svid %d, vnetif %s",
netif->ipcom.name, vid, svid, vnetif->ipcom.name);
#endif
ret = ipnet_vlan_add_cs_fwd(netif, vid, svid, vnetif);
/* Add netif to vnetif's forwarding port list */
if (!ret)
ret = ipnet_vlan_add_sc_fwd(vnetif, vid, svid, netif);
if (ret) return ret;
}
else if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_SC_VLAN)
{
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_add_fwd, SC-VLAN, vnetif %s, svid %d\n",
vnetif->ipcom.name, svid);
#endif
/* A provider network port on a provider edge node */
if (vnetif->eth->vlan_param.vlan_type != IPNET_VLAN_CS_VLAN)
continue;
/* Check whether the customer edge/network port has S-VID, */
/* and get the corresponding C-VID if it does. */
vid = ipnet_vlan_find_svid(vnetif, svid);
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_add_fwd(SC), vnetif %s, vid %d\n",
vnetif->ipcom.name, vid);
#endif
if (!vid)
continue;
/* The vnetif is a forwarding port */
do {
/* Add vnetif (customer edge/network port) to netif's
(provider network port) forwarding port list */
ret = ipnet_vlan_add_sc_fwd(netif, vid, svid, vnetif);
/* Add netif to vnetif's forwarding port list */
if (!ret)
ret = ipnet_vlan_add_cs_fwd(vnetif, vid, svid, netif);
vid = ipnet_vlan_find_next_svid(vnetif, vid, svid);
} while (vid);
if (ret) return ret;
}
}
}
if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_CS_VLAN)
{
if (!found_sc_port)
/* Create a fwd entry with no fwd port */
ret = ipnet_vlan_add_cs_fwd(netif, vid, svid, IP_NULL);
/* Mark the VLAN membership bit map */
netif->eth->vlan_param.vids[vid>>5] |= (1 << (vid&0x1f));
}
else
{
/* Mark the VLAN membership bit map */
netif->eth->vlan_param.vids[svid>>5] |= (1 << (svid&0x1f));
}
/* Set default port VID and promisc mode when port becomes VLAN-aware */
if (!netif->eth->vlan_param.id)
{
Ip_bool promisc = IP_TRUE;
netif->eth->vlan_param.id = IPNET_VLAN_DEFAULT_PVID;
if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_S_VLAN)
{
netif->ipcom.link_hdr_size += (Ip_u16)IPNET_VLAN_TAG_SIZE;
netif->eth->vlan_param.tag_required = IP_TRUE;
}
/* Record the current promisc mode */
netif->eth->vlan_param.promisc_count = (int)(netif->ipcom.flags &
IP_IFF_PROMISC);
if (!netif->eth->vlan_param.promisc_count)
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
if (ipcom_forwarder_is_master())
#endif
ipnet_if_drv_ioctl(netif, IP_SIOCXPROMISC, &promisc);
/* Init MAC station hash */
if (!vlan_stations)
vlan_stations = ipcom_hash_new(
(Ipcom_hash_obj_func) ipnet_vlan_mac_obj_hash,
(Ipcom_hash_key_func) ipnet_vlan_mac_key_hash,
(Ipcom_hash_cmp_func) ipnet_vlan_mac_hash_cmp);
if (vlan_stations == IP_NULL)
{
IPCOM_LOG1(DEBUG, "ipnet_vlan_add_fwd: %s fail to alloc MAC hash table",
netif->ipcom.name);
/* Delete the VLAN stacking configuration */
ipnet_vlan_del_fwd(netif, (Ip_u16)
(netif->eth->vlan_param.vlan_type==IPNET_VLAN_CS_VLAN)?vid:svid);
return -IP_ERRNO_ENOMEM;
}
}
return 0;
errout:
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
if (!netif->eth->vlan_param.id)
ipcom_forwarder_if_clr_vlan_stacking(netif);
#endif
return ret;
}
/*
*===========================================================================
* ipnet_vlan_del_fwd
*===========================================================================
* Description: delete a VLAN forwarding port
* Parameters:
* Returns:
*/
IP_STATIC int
ipnet_vlan_del_fwd(Ipnet_netif *netif, Ip_u16 vid)
{
Ipnet_vlan_fwd *vlan_fwd = IP_NULL;
Ipnet_vlan_fwd_port *vlan_fwd_port = IP_NULL, *n_vlan_fwd_port;
Ip_u16 i, num_stats, svid = vid;
Ip_bool no_vlan = IP_TRUE;
Ipnet_netif *vnetif;
Ipnet_vlan_station_cache *station;
#ifdef SVLAN_DEBUG
ipcom_printf("Enter ipnet_vlan_del_fwd, netif %s, vid %d\n",
netif->ipcom.name, vid);
#endif
/* Clear the bit map setting */
netif->eth->vlan_param.vids[vid >> 5] &= ~(1 << (vid & 0x1f));
/* Get the VLAN fwd entry corresponding to the vid */
if (netif->eth->vlan_param.vlan_fwd)
vlan_fwd = ipcom_hash_get(netif->eth->vlan_param.vlan_fwd, &vid);
if (!vlan_fwd)
{
if (vid == netif->eth->vlan_param.id)
/* No forwarding table for PVID, return */
return 0;
goto clear_other;
}
if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_S_VLAN)
{
vlan_fwd_port = ipnet_vlan_find_fwd_port(vlan_fwd->ports.port_list,
netif->ipcom.ifindex);
/* Remove and free the port */
if (vlan_fwd_port)
{
ipcom_list_remove(&vlan_fwd_port->next);
ipcom_free(vlan_fwd_port);
}
/* Remove the vlan fwd port list entry */
ipcom_hash_remove(netif->eth->vlan_param.vlan_fwd, vlan_fwd);
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
/* Remove fast path S-VLAN fwd cache entries */
for (n_vlan_fwd_port = IPCOM_LIST_FIRST(vlan_fwd->ports.port_list);
n_vlan_fwd_port &&
ipcom_strcmp(netif->ipcom.name,n_vlan_fwd_port->netif->ipcom.name);
n_vlan_fwd_port = IPCOM_LIST_NEXT(&n_vlan_fwd_port->next))
{
ipcom_forwarder_svlan_del_cache_entry(netif, 0, vid,
n_vlan_fwd_port->netif);
ipcom_forwarder_svlan_del_cache_entry(n_vlan_fwd_port->netif,
0, vid, netif);
}
#endif
if (vlan_fwd->ports.port_list->size == 0)
{
/* No more port, free the port list and vlan_fwd */
ipcom_free(vlan_fwd->ports.port_list);
ipcom_free(vlan_fwd);
}
}
else if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_CS_VLAN)
{
svid = vlan_fwd->svid;
/* Remove all ports */
for (vlan_fwd_port = IPCOM_LIST_FIRST(vlan_fwd->ports.port_list);
vlan_fwd_port != IP_NULL;
vlan_fwd_port = n_vlan_fwd_port)
{
n_vlan_fwd_port = IPCOM_LIST_NEXT(&vlan_fwd_port->next);
/* Record SC-VLAN port */
vnetif = vlan_fwd_port->netif;
/* Remove and free the port */
ipcom_list_remove(&vlan_fwd_port->next);
ipcom_free(vlan_fwd_port);
ipnet_vlan_del_sc_fwd(vnetif, vlan_fwd->svid, vlan_fwd->vid, netif);
}
/* Remove and free the vlan fwd port list entry */
ipcom_hash_remove(netif->eth->vlan_param.vlan_fwd, vlan_fwd);
ipcom_free(vlan_fwd->ports.port_list);
ipcom_free(vlan_fwd);
}
else if ((netif->eth->vlan_param.vlan_type == IPNET_VLAN_SC_VLAN) &&
vlan_fwd->ports.c_ports)
{
Ip_u16 j = 0;
Ipnet_vlan_scfwd *scfwd_ports;
Ipnet_vlan_scfwd *scfwd_ports_list[vlan_fwd->ports.c_ports->elem];
for (i = 0;
i < vlan_fwd->ports.c_ports->size &&
j < vlan_fwd->ports.c_ports->elem;
i++)
{
scfwd_ports = (Ipnet_vlan_scfwd *)
vlan_fwd->ports.c_ports->table[i];
if (scfwd_ports)
{
/* Remove all ports */
for (vlan_fwd_port=IPCOM_LIST_FIRST(scfwd_ports->port_list);
vlan_fwd_port != IP_NULL;
vlan_fwd_port = n_vlan_fwd_port)
{
n_vlan_fwd_port = IPCOM_LIST_NEXT(&vlan_fwd_port->next);
/* Record CS-VLAN port */
vnetif = vlan_fwd_port->netif;
/* Remove and free the port */
ipcom_list_remove(&vlan_fwd_port->next);
ipcom_free(vlan_fwd_port);
ipnet_vlan_del_cs_fwd(vnetif, scfwd_ports->vid, netif);
}
/* Record ports */
scfwd_ports_list[j] = scfwd_ports;
j++;
}
}
for (i = 0; i < j; i++)
{
/* Remove and free the vlan scfwd port list entry */
ipcom_free(scfwd_ports_list[i]->port_list);
ipcom_free(scfwd_ports_list[i]);
}
ipcom_hash_delete(vlan_fwd->ports.c_ports);
/* Remove and free the vlan fwd port list entry */
ipcom_hash_remove(netif->eth->vlan_param.vlan_fwd, vlan_fwd);
ipcom_free(vlan_fwd);
}
if (netif->eth->vlan_param.vlan_fwd->elem == 0)
{
/* Clear forwarding hash table */
ipcom_hash_delete(netif->eth->vlan_param.vlan_fwd);
netif->eth->vlan_param.vlan_fwd = IP_NULL;
}
/* Free the MAC station cache */
if (vlan_stations)
{
Ipnet_vlan_station_cache *station_list[vlan_stations->elem];
for (i = 0, num_stats = 0;
(i < vlan_stations->size)&&(num_stats < vlan_stations->elem);
i++)
{
station = (Ipnet_vlan_station_cache *)vlan_stations->table[i];
if (station &&
(station->svid == svid) &&
(!ipcom_strcmp(netif->ipcom.name,station->netif->ipcom.name)))
{
station_list[num_stats] = station;
num_stats ++;
}
}
/* free the MAC stations */
for (i = 0; i < num_stats; i++)
{
ipcom_hash_remove(vlan_stations, station_list[i]);
ipcom_free(station_list[i]);
}
}
clear_other:
/* Check whether there is still VLAN configuration */
for (i = 0; i < 128; i++)
if (netif->eth->vlan_param.vids[i])
{
no_vlan = IP_FALSE;
break;
}
/* re-set default PVID, port type, and promisc mode, free station list */
if (no_vlan)
{
Ip_bool promisc = IP_FALSE;
netif->eth->vlan_param.id = 0;
netif->eth->vlan_param.ingress_rule = 0;
netif->eth->vlan_param.egress_rule = 0;
if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_S_VLAN)
{
netif->ipcom.link_hdr_size -= (Ip_u16)IPNET_VLAN_TAG_SIZE;
netif->eth->vlan_param.tag_required = IP_FALSE;
}
if (!netif->eth->vlan_param.promisc_count)
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
if (ipcom_forwarder_is_master())
#endif
ipnet_if_drv_ioctl(netif, IP_SIOCXPROMISC, &promisc);
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
/* Clear/restore input hash tag configuration */
ipcom_forwarder_if_clr_vlan_stacking(netif);
#endif
/* reset VLAN type */
netif->eth->vlan_param.vlan_type = IPNET_VLAN_NOT_DEFINED;
}
return 0;
}
/*
*===========================================================================
* ipnet_vlan_pri_mapping
*===========================================================================
* Description: create a priority mapping from a receiving VLAN tag to
* the transmitting S-VLAN tag for a customer edge/network port
* on a provider node.
* Parameters:
* Returns:
*
*/
IP_STATIC int
ipnet_vlan_pri_mapping(Ipnet_netif *netif, Ip_u16 cvid, Ip_u8 pri, Ip_u8 spri)
{
Ipnet_vlan_fwd *cs_fwd = IP_NULL;
if (netif->eth->vlan_param.vlan_fwd)
cs_fwd = ipcom_hash_get(netif->eth->vlan_param.vlan_fwd, &cvid);
if (cs_fwd)
cs_fwd->pri_mapping[pri & 0x07] = (Ip_u8)(spri & 0x07);
return 0;
}
/*
*===========================================================================
* ipnet_vlan_stpid
*===========================================================================
* Description: Set/Get service TPID vaule
* Parameters:
* Returns: 0 = success, <0 error code.
*
*/
IP_STATIC int
ipnet_vlan_stpid(unsigned long request, struct Ip_vlanreq *vlanmsg)
{
int ret = 0;
Ip_u16 stpid = vlanmsg->vlr_stpid;
switch (request)
{
case IP_SIOCSETSTPID:
#if defined(IP_PORT_VXWORKS) && !defined(IPCOM_USE_FORWARDER_VLAN_STACKING)
{
Ipnet_netif *netif;
Ip_u32 i;
/*
* IPNET_NETIF_FOR_EACH sets netif to NULL after the for loop,
* this is a performance optimized defination
*/
/* coverity[result_independent_of_operands] */
IPNET_NETIF_FOR_EACH(netif, i)
{
if (netif->ipcom.type == IP_IFT_ETHER)
ret = ipcom_drv_eth_set_stpid(&netif->ipcom, stpid);
}
}
#endif /* IP_PORT_VXWORKS */
ipnet->vlan_s_tpid = stpid;
ipnet->vlan_s_tpid_net = ip_htons(ipnet->vlan_s_tpid);
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
vlanmsg->vlr_stpid = ipnet->vlan_s_tpid_net;
ipcom_forwader_svlan_update_config(request, vlanmsg, 0, IP_NULL);
#endif
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_stpid, set ipnet stpid %x\n", ipnet->vlan_s_tpid);
#endif
break;
case IP_SIOCGETSTPID:
vlanmsg->vlr_stpid = ipnet_get_svlan_stpid();
break;
default:
ret = -IP_ERRNO_EINVAL;
break;
}
return ret;
}
/*
*===========================================================================
* ipnet_vlan_get_pvlan
*===========================================================================
* Description: Get port VLAN configuration for ifconfig show commands.
* Parameters:
* Returns:
*
*/
IP_STATIC int
ipnet_vlan_get_pvlan(Ipnet_netif *netif, void *data)
{
struct Ip_vlanreq *vlanreq;
struct Ip_ifreq *ifreq;
Ipnet_vlan_fwd *vlan_fwd = IP_NULL;
Ip_u16 i, j, k, vid;
Ip_u8 num_vid = 0, num_cvid = 0;
ifreq = (struct Ip_ifreq *) data;
vlanreq = (struct Ip_vlanreq *) (ifreq->ip_ifr_data);
/* Get PVID, VLAN type, ingress rule, egress rule */
vlanreq->vlr_tag = (Ip_u16)netif->eth->vlan_param.id;
vlanreq->vlr_type = netif->eth->vlan_param.vlan_type;
vlanreq->vlr_inrule = netif->eth->vlan_param.ingress_rule;
vlanreq->vlr_outrule = netif->eth->vlan_param.egress_rule;
/* Get vid, fwd ports, and priority mapping, at most 10 VIDs */
for (i = 0; i < 128; i++)
{
for (j = 0, vlan_fwd = IP_NULL; j < 32 && num_vid < 10; j++)
if (netif->eth->vlan_param.vids[i] & (1 << j))
{
vid = (Ip_u16)(i * 32 + j);
vlanreq->pvlan_fwd[num_vid].vid = vid;
if (netif->eth->vlan_param.vlan_fwd)
vlan_fwd = ipcom_hash_get(netif->eth->vlan_param.vlan_fwd, &vid);
if (!vlan_fwd)
{
num_vid++;
continue;
}
if (netif->eth->vlan_param.vlan_type != IPNET_VLAN_SC_VLAN)
{
if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_CS_VLAN)
{
vlanreq->pvlan_fwd[num_vid].svid = vlan_fwd->svid;
for (k = 0; k < 8; k++)
vlanreq->pvlan_fwd[num_vid].priority[k] =
vlan_fwd->pri_mapping[k];
}
vlanreq->pvlan_fwd[num_vid].num_vlans = 1;
ipnet_vlan_fill_conf_ports(vlan_fwd->ports.port_list,
vlanreq, num_vid, 0);
}
else
{
/* SC VLAN port */
Ipnet_vlan_scfwd *vlan_scfwd_ports;
Ip_u16 l;
for (l = 0, num_cvid = 0;
l < vlan_fwd->ports.c_ports->size &&
num_cvid < vlan_fwd->ports.c_ports->elem &&
num_cvid < 5;
l++)
{
vlan_scfwd_ports = (Ipnet_vlan_scfwd *)
vlan_fwd->ports.c_ports->table[l];
if (vlan_scfwd_ports)
{
ipnet_vlan_fill_conf_ports(vlan_scfwd_ports->port_list,
vlanreq, num_vid, num_cvid);
vlanreq->pvlan_fwd[num_vid].port_list[num_cvid].cvid =
vlan_scfwd_ports->vid;
num_cvid ++;
}
}
vlanreq->pvlan_fwd[num_vid].num_vlans = num_cvid;
}
num_vid++;
}
}
vlanreq->num_vlans = num_vid;
return 0;
}
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_vlan_stacking_ioctl
*===========================================================================
* Description: VLAN stacking ioctl function
* Parameters:
* Returns: 0 = success, <0 error code
*
*/
IP_PUBLIC int
ipnet_vlan_stacking_ioctl(unsigned long request, void *req, Ipnet_netif *netif)
{
int ret = 0;
struct Ip_vlanreq *vlan_req;
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_stacking_ioctl, request %lu, netif %s\n",
request, netif?netif->ipcom.name:"NULL");
#endif
if ((request == IP_SIOCSETSTPID) || (request == IP_SIOCGETSTPID))
vlan_req = (struct Ip_vlanreq *)req;
else
vlan_req = (struct Ip_vlanreq *)(((struct Ip_ifreq *)req)->ip_ifr_data);
switch (request)
{
case IP_SIOCSETVLANPVID:
if ((netif->ipcom.type != IP_IFT_ETHER) ||
(netif->eth->vlan_param.vlan_type == IPNET_VLAN_NOT_DEFINED))
return -IP_ERRNO_EPERM;
netif->eth->vlan_param.id = vlan_req->vlr_tag;
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
ipcom_forwader_svlan_update_config(request, vlan_req, 0, netif);
#endif
break;
case IP_SIOCSETVLANP:
{
Ip_u16 vid = vlan_req->vlr_tag;
Ip_u16 svid = vlan_req->vlr_stag;
Ip_u16 vlr_type = vlan_req->vlr_type;
Ipnet_vlan_type old_type = netif->eth->vlan_param.vlan_type;
if (netif->ipcom.type != IP_IFT_ETHER || netif->eth->vlan.ifs)
return -IP_ERRNO_EPERM;
if ((netif->eth->vlan_param.vlan_type != IPNET_VLAN_NOT_DEFINED) &&
(netif->eth->vlan_param.vlan_type != vlr_type))
return -IP_ERRNO_EPERM;
netif->eth->vlan_param.vlan_type = vlr_type;
if ((((netif->eth->vlan_param.vlan_type == IPNET_VLAN_C_VLAN) ||
(netif->eth->vlan_param.vlan_type == IPNET_VLAN_CS_VLAN)) &&
(netif->eth->vlan_param.vids[vid>>5]&(1<<(vid&0x1f))))
|| (((netif->eth->vlan_param.vlan_type == IPNET_VLAN_S_VLAN) ||
(netif->eth->vlan_param.vlan_type == IPNET_VLAN_SC_VLAN)) &&
(netif->eth->vlan_param.vids[svid>>5]&(1<<(svid&0x1f)))))
return 0;
/* add to VLAN forwarding port list */
ret = ipnet_vlan_add_fwd(netif, vid, svid);
if (ret < 0)
{
/* Invalid configuration, recover */
netif->eth->vlan_param.vlan_type = old_type;
}
}
break;
case IP_SIOCDELVLANP:
{
Ip_u16 vid = vlan_req->vlr_tag;
if ((netif->ipcom.type != IP_IFT_ETHER) ||
(netif->eth->vlan_param.vlan_type == IPNET_VLAN_NOT_DEFINED))
return -IP_ERRNO_EPERM;
if (!(netif->eth->vlan_param.vids[vid>>5]&(1<<(vid&0x1f))))
return -IP_ERRNO_EPERM;
/* Remove from VLAN forwarding port list */
ret = ipnet_vlan_del_fwd(netif, vid);
}
break;
case IP_SIOCSETVLANPRIMAP:
{
Ip_u16 cvid = vlan_req->vlr_tag;
Ip_u8 pri = (Ip_u8)vlan_req->vlr_pri;
Ip_u8 spri = (Ip_u8)vlan_req->vlr_spri;
if (netif->ipcom.type != IP_IFT_ETHER)
return -IP_ERRNO_EPERM;
if (netif->eth->vlan_param.vlan_type != IPNET_VLAN_CS_VLAN)
return -IP_ERRNO_EPERM;
if (!cvid ||
!(netif->eth->vlan_param.vids[cvid>>5]&(1<<(cvid&0x1f))))
return -IP_ERRNO_EPERM;
/* Update priority mapping for a customer edge/network port */
ret = ipnet_vlan_pri_mapping(netif, cvid, pri, spri);
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
ipcom_forwader_svlan_update_config(request, vlan_req, 0, netif);
#endif
}
break;
case IP_SIOCSETVLANINRULE:
if (netif->ipcom.type != IP_IFT_ETHER)
return -IP_ERRNO_EPERM;
if ((netif->eth->vlan_param.vlan_type != IPNET_VLAN_CS_VLAN) &&
(netif->eth->vlan_param.vlan_type != IPNET_VLAN_SC_VLAN))
return -IP_ERRNO_EPERM;
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
if (netif->eth->vlan_param.ingress_rule != vlan_req->vlr_inrule)
ipcom_forwader_svlan_update_config(request, vlan_req,
netif->eth->vlan_param.ingress_rule, netif);
#endif
netif->eth->vlan_param.ingress_rule = vlan_req->vlr_inrule;
break;
case IP_SIOCSETVLANOUTRULE:
if (netif->ipcom.type != IP_IFT_ETHER)
return -IP_ERRNO_EPERM;
if ((netif->eth->vlan_param.vlan_type != IPNET_VLAN_CS_VLAN) &&
(netif->eth->vlan_param.vlan_type != IPNET_VLAN_SC_VLAN))
return -IP_ERRNO_EPERM;
#ifdef IPCOM_USE_FORWARDER_VLAN_STACKING
if (netif->eth->vlan_param.egress_rule != vlan_req->vlr_outrule)
ipcom_forwader_svlan_update_config(request, vlan_req,
netif->eth->vlan_param.egress_rule, netif);
#endif
netif->eth->vlan_param.egress_rule = vlan_req->vlr_outrule;
break;
case IP_SIOCGETPVLAN:
if (netif->ipcom.type != IP_IFT_ETHER)
return -IP_ERRNO_EPERM;
if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_NOT_DEFINED)
return -IP_ERRNO_EPERM;
/* Get port VLAN configuration info */
ret = ipnet_vlan_get_pvlan(netif, req);
break;
case IP_SIOCSETSTPID:
case IP_SIOCGETSTPID:
ret = ipnet_vlan_stpid(request, vlan_req);
default:
break;
}
return ret;
}
/*
*===========================================================================
* ipnet_get_svlan_stpid
*===========================================================================
* Description: Returns the value of service VLAN TPID.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_u16
ipnet_get_svlan_stpid(void)
{
return ipnet->vlan_s_tpid;
}
/*
*===========================================================================
* ipnet_get_svlan_stpid_net
*===========================================================================
* Description: Returns the value of service VLAN TPID in network order.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_u16
ipnet_get_svlan_stpid_net(void)
{
return ipnet->vlan_s_tpid_net;
}
/*
*===========================================================================
* ipnet_svlan_free_all
*===========================================================================
* Description: Free the VLAN forwarding port list and MAC station list on
* an interface
* Parameters: netif - The provider network port
* Returns: 0 = success, <0 = error code.
*
*/
IP_PUBLIC void
ipnet_svlan_free_all(Ipnet_netif *netif)
{
Ip_u16 i, j;
Ip_u16 vid;
/* Free the VLAN forwarding port list */
for (i = 0; i < 128; i++)
{
for (j = 0; j < 32; j++)
if (netif->eth->vlan_param.vids[i] & (1 << j))
{
vid = (Ip_u16)(i * 32 + j);
ipnet_vlan_del_fwd(netif, vid);
}
}
}
/*
*===========================================================================
* ipnet_svlan_ingress_filter
*===========================================================================
* Description: Perform VLAN ingress filtering
* Parameters:
* Returns: 0 = success, <0 error code
*
*/
IP_PUBLIC int
ipnet_svlan_ingress_filter(Ipnet_netif *netif,
Ip_u16 vid,
Ip_u16 svid)
{
/* Ingress classification and filtering */
if (netif->eth->vlan_param.ingress_rule == IPNET_VLAN_INGRESS_ADMIT_ALL)
{
/* Admit all frames */
if (vid &&
(vid != netif->eth->vlan_param.id) &&
!(netif->eth->vlan_param.vids[vid>>5] & (1<<(vid&0x1f))))
{
/* Discard the frame */
return -IP_ERRNO_EACCES;
}
}
else if (netif->eth->vlan_param.ingress_rule ==
IPNET_VLAN_INGRESS_ADMIT_TAG)
{
/* Admit only VLAN-tagged frames */
if ((vid == 0) ||
((vid != netif->eth->vlan_param.id) &&
!(netif->eth->vlan_param.vids[vid>>5] & (1<<(vid&0x1f)))))
{
/* Discard the frame */
return -IP_ERRNO_EACCES;
}
}
else if (netif->eth->vlan_param.ingress_rule ==
IPNET_VLAN_INGRESS_ADMIT_OTHER)
{
/* Admit only Untagged and Prioirty-tagged frames */
if (vid)
{
/* Discard the frame */
return -IP_ERRNO_EACCES;
}
}
/* Check for frame type and port type matching */
if ((netif->eth->vlan_param.vlan_type == IPNET_VLAN_CS_VLAN) && svid)
/* customer edge/network port with SVID */
/* or provider network port without SVID */
return -IP_ERRNO_EACCES;
return 0;
}
/*
*===========================================================================
* ipnet_svlan_to_forward
*===========================================================================
* Description: Determine whether to perform VLAN forwarding
* Parameters:
* Returns: IP_TURE if to perform VLAN forwarding, IP_FALSE if not
*
*/
IP_PUBLIC Ip_bool
ipnet_svlan_to_forward(Ipnet_netif *netif, Ipcom_pkt *pkt, int vid)
{
Ipnet_pkt_eth *eth = (Ipnet_pkt_eth *)&pkt->data[pkt->start];
return ((vid || (netif->eth->vlan_param.vlan_type == IPNET_VLAN_CS_VLAN))
&& !IPNET_ARE_ETHADDR_EQUAL(eth->dst, netif->ipcom.link_addr)
&& IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_LINK_VLAN));
}
/*
*===========================================================================
* ipnet_svlan_output
*===========================================================================
* Description: Perform VLAN forwarding
* Parameters:
* Returns: 0 = success, <0 error code
*
*/
IP_PUBLIC int
ipnet_svlan_output(Ipnet_netif *netif,
Ipcom_pkt *pkt,
int frame_type,
int tag,
int ctag,
Ip_u8 vpri,
Ip_u16 link_hdr_size)
{
Ipnet_vlan_fwd *vlan_fwd = IP_NULL;
int ret = -IP_ERRNO_EINVAL;
Ip_u16 vid = (Ip_u16)tag, cvid = (Ip_u16)ctag;
Ip_u16 svid = 0;
Ip_u8 svpri = 0, tvpri = vpri;
Ipnet_pkt_eth *eth = (Ipnet_pkt_eth *)&pkt->data[pkt->start];
Ipnet_vlan_addr_type dst_type;
#ifdef SVLAN_DEBUG
ipcom_printf("Enter ipnet_vlan_output, if %s, vid %d, cvid %d, vpri %d\n",
netif->ipcom.name, vid, cvid, vpri);
{
int i;
ipcom_printf("ipnet_vlan_output(rx pkt): if %s,start %d,end %d,length %d\n",
netif->ipcom.name, pkt->start, pkt->end, pkt->end - pkt->start);
for (i = pkt->start; i < pkt->end; i++)
ipcom_printf("%x ", pkt->data[i]);
ipcom_printf("\n");
}
#endif
if (cvid)
vpri = IPNET_ETH_VLAN_GET_VPRI(*(Ip_u16 *)(&pkt->data[pkt->start+18]));
/* Get a valid C-VID and C-VLAN priority for customer edge/network port */
if (!vid && (netif->eth->vlan_param.vlan_type == IPNET_VLAN_CS_VLAN))
{
if (cvid)
/* pkt has S-VLAN-tag(SVID:0) and C-VLAN-tag */
vid = cvid;
else
{
/* pkt has no VID, use the PVID */
vid = (Ip_u16)(netif->eth->vlan_param.id & 0x0fff);
if (eth->d.type == ipnet->vlan_s_tpid_net)
{
if (*(Ip_u16 *)(&pkt->data[pkt->start+16]) !=
IPNET_ETHFRAME_8021Q_VLAN)
vpri = 0;
else
vpri = IPNET_ETH_VLAN_GET_VPRI(*(Ip_u16 *)(&pkt->data[pkt->start+18]));
}
}
}
/* Get the VLAN fwd entry corresponding to the vid */
if (netif->eth->vlan_param.vlan_fwd)
vlan_fwd = ipcom_hash_get(netif->eth->vlan_param.vlan_fwd, &vid);
if (!vlan_fwd ||
((netif->eth->vlan_param.vlan_type == IPNET_VLAN_S_VLAN) &&
vlan_fwd->ports.port_list &&
(vlan_fwd->ports.port_list->size == 1)))
/* no forwarding table, go to cleanup */
goto cleanup;
/* get S-VID */
if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_CS_VLAN)
svid = vlan_fwd->svid;
else
svid = vid;
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_output, vid %d, cvid %d, svid %d\n",
vid, cvid, svid);
#endif
/* MAC processing: add the pkt's source MAC address to the interface's */
/* MAC station list if not recorded already. If the pkt's dest MAC */
/* address is in the interface's MAC station list, meaning the */
/* destination is on the same link, then don't forward the pkt to */
/* other ports. */
if (ipnet_vlan_find_mac(netif, eth->src, svid, IP_FALSE) == IP_NULL)
ipnet_vlan_add_mac(netif, eth->src, svid);
if ((ipnet_vlan_mac_type(eth->dst) == IPNET_VLAN_ADDR_UNICAST) &&
ipnet_vlan_find_mac(netif, eth->dst, svid, IP_TRUE))
/* The destination is on the same link, don't forward the pkt */
goto cleanup;
if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_S_VLAN)
{
ret = ipnet_vlan_common_output(netif, vlan_fwd, pkt, frame_type,
vid, svid, cvid, vpri, svpri, link_hdr_size);
}
else if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_CS_VLAN)
{
/* Map the priority */
svpri = vlan_fwd->pri_mapping[tvpri];
#ifdef SVLAN_DEBUG
ipcom_printf("ipnet_vlan_output(CS), svpri %d, svid %d\n", svpri, svid);
#endif
ret = ipnet_vlan_common_output(netif, vlan_fwd, pkt, frame_type,
vid, svid, cvid, vpri, svpri, link_hdr_size);
}
else if (netif->eth->vlan_param.vlan_type == IPNET_VLAN_SC_VLAN)
{
Ipnet_vlan_scfwd *vlan_scfwd = IP_NULL;
if (!cvid)
{
cvid = (Ip_u16)(netif->eth->vlan_param.id & 0x0fff);
vpri = 0;
}
if (vlan_fwd->ports.c_ports)
vlan_scfwd = ipcom_hash_get(vlan_fwd->ports.c_ports, &cvid);
if (!vlan_scfwd)
/* no forwarding table, drop the pkt */
goto cleanup;
ret = ipnet_vlan_common_output(netif, vlan_scfwd, pkt, frame_type,
vid, svid, cvid, vpri, svpri, link_hdr_size);
}
cleanup:
dst_type = ipnet_vlan_mac_type(eth->dst);
if (dst_type != IPNET_VLAN_ADDR_UNICAST)
{
/* Continue the boradcast and multicast pkt to the local stack */
return -IP_ERRNO_ERANGE;
}
if (ret == -IP_ERRNO_EINVAL)
ipcom_pkt_free(pkt);
return ret < 0 ? ret : 0;
}
/*
*===========================================================================
* ipnet_svlan_add_svtag
*===========================================================================
* Description: Add S-VLAN tag
* Parameters: netif - The network interface on which the packet should
* be sent.
* sock_vtag - The socket VLAN tag.
* eth_type - The type of data this ethernet frame contains.
* pkt - The ethernet frame.
* Returns: none.
*
*/
IP_PUBLIC Ipnet_pkt_eth *
ipnet_svlan_add_svtag(Ipnet_netif *netif,
Ip_u16 sock_vtag,
Ip_u16 eth_type,
Ipcom_pkt *pkt)
{
Ipnet_pkt_eth *eth;
Ip_u16 tag;
tag = (Ip_u16)(netif->eth->vlan_param.id & 0x0fff);
if (sock_vtag)
{
/* pkt->start has already been moved to point to eth data */
/* move back 4 bytes for S-VLAN tag */
eth = ipcom_pkt_push_front(pkt, IPNET_VLAN_TAG_SIZE);
}
else
{
/* move pkt->start points to eth data including S-VLAN tag */
eth = ipcom_pkt_push_front(pkt,
IPNET_ETH_HDR_SIZE + IPNET_VLAN_TAG_SIZE);
}
/* Insert S-VLAN tag */
eth->d.vlan.tag_type = ipnet->vlan_s_tpid_net;
eth->d.vlan.pri_cif_vid = ip_htons(tag);
eth->d.vlan.type = eth_type;
return eth;
}
#endif /* IPNET_USE_VLAN_STACKING */
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/