2438 lines
82 KiB
C
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
|
|
****************************************************************************
|
|
*/
|
|
|