/* 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 #include #include #include #include #include #include "ipnet.h" #include "ipnet_h.h" #include "ipnet_eth.h" #include "ipnet_svlan.h" #ifdef IPCOM_USE_FORWARDER_VLAN_STACKING #include #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 **************************************************************************** */