/* ipcom_forward_svlan.h - VLAN stacking MNT head file */ /* * 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 -------------------- 01b,11jan11,tlu Added macro to remove VLAN tags 01a,17nov10,tlu Moved inline functions from ipcom_forwarder_svlan.c */ #ifndef IPCOM_FORWARDER_SVLAN_H #define IPCOM_FORWARDER_SVLAN_H /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipcom_config.h" #if defined(IPCOM_USE_FORWARDER_VLAN_STACKING) #include "ipcom_forwarder.h" #include "ipcom_nae.h" #ifdef IPCOM_USE_NEHALEM_FORWARDER #include "ipcom_vxworks_nehalem_svlan.h" #endif #ifdef IPCOM_USE_OCTEON_FORWARDER #include "ffsLib.h" #endif /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #ifdef __cplusplus extern "C" { #endif /* **************************************************************************** * 4 DEFINES **************************************************************************** */ #define FWD_SVLAN_RX(pkt, msg, ipprt, fw, ret) \ { \ switch (ipcom_fw->port_type[ipprt]) \ { \ case IPCOM_FWD_VLAN_PORT_CS: \ ret = ipcom_svlan_cs_rx(pkt, msg, ipprt, fw); \ break; \ case IPCOM_FWD_VLAN_PORT_SC: \ ret = ipcom_svlan_sc_rx(pkt, msg, ipprt, fw); \ break; \ case IPCOM_FWD_VLAN_PORT_S: \ ret = ipcom_svlan_s_rx(pkt, msg, ipprt, fw); \ break; \ } \ } #define FWD_SVLAN_FMT(ipprt, fw_msg, ix_num) \ { \ switch (ipcom_fw->port_type[ipprt]) \ { \ case IPCOM_FWD_VLAN_PORT_CS: \ ipcom_svlan_cs_fmt(fw_msg, ix_num); \ break; \ case IPCOM_FWD_VLAN_PORT_SC: \ ipcom_svlan_sc_fmt(fw_msg, ix_num); \ break; \ } \ } #define FWD_SVLAN_REMOVE_TAGS(fw_msg) \ { \ /* remove VLAN tags */ \ switch ((fw_msg->pkt_flags & IPCOM_FWD_VLAN_PKT_SVTAG) | \ (fw_msg->pkt_flags & IPCOM_FWD_VLAN_PKT_CVTAG)) \ { \ case 1: \ /* 01: S-VLAN tag */ \ case 2: \ /* 10: C-VLAN tag */ \ /* remove S-VLAN/C-VLAN tag */ \ *(Ip_u64 *)(fw_msg->packet+8) = *(Ip_u64 *)(fw_msg->packet+4); \ *(Ip_u32 *)(fw_msg->packet+4) = *(Ip_u32 *)(fw_msg->packet); \ fw_msg->packet += 4; \ fw_msg->len_mod = -4; \ break; \ case 3: \ /* 11: S-VLAN tag + C-VLAN tag */ \ /* remove S-VLAN + C-VLAN tags */ \ *(Ip_u64 *)(fw_msg->packet+12) = *(Ip_u64 *)(fw_msg->packet+4); \ *(Ip_u32 *)(fw_msg->packet+8) = *(Ip_u32 *)(fw_msg->packet); \ fw_msg->packet += 8; \ fw_msg->len_mod = -8; \ } \ } /* Define the following flag to build debug msg into image */ /* #define FW_VLAN_DEBUG */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ typedef struct fw_mac_station_s { Ip_u16 last_used_time; Ip_u8 macAddr_svid[8]; /* MAC address + S-VID (last 2 bytes) */ Ip_u8 ipprt; /* input physical port */ } fw_mac_station_t; IP_EXTERN fw_mac_station_t *mac_stations; IP_EXTERN Ip_time_t svlan_time; IP_EXTERN int (*ipcom_forwarder_vlan_hw_tx[MAX_FW_PORTS]) (fw_msg_t *fw_msg, void *fw_cookie); IP_INLINE unsigned ipcom_vlan_hash_update(const void *key_param, int key_len, unsigned initval, int type) { unsigned a , b; unsigned len; const Ip_u8 *key = key_param; /* Set up the internal state */ len = (unsigned) key_len; a = initval; b = len; while (len >= 4) { a += key[0] + (key[1] << 8) + (key[2] << 16) + (key[3] << 24); b += (a << 3); a += ~(b << 11); b ^= (a >> 16); key += 4; len -= 4; } /* All the case statements fall through */ switch (len) { case 3 : a += key[2] << 16; case 2 : b ^= key[1] << 8; case 1 : a += key[0]; default: break; } a ^= (b >> 6); b += ~(a << 15); a ^= (b >> 10); return a + b; } /* *=========================================================================== * ipcom_svlan_copy_pkt *=========================================================================== * Description: Copy a pkt, first 64 bits, then 32, then 16, last 8 * Parameters: copy_to : the pkt to copy to * copy_from: the pkt to copy from * len : the copy length in bytes */ IP_STATIC __inline__ void ipcom_svlan_copy_pkt(Ip_u8 *copy_to, Ip_u8 *copy_from, Ip_u16 len) { Ip_u16 num, i = len & 0x7; Ip_u16 rem = len - i; for (num = 0; num < rem; num += 8) *(Ip_u64 *)(&(copy_to[num])) = *(Ip_u64 *)(&(copy_from[num])); if (i >= 4) { *(Ip_u32 *)(&(copy_to[num])) = *(Ip_u32 *)(&(copy_from[num])); num += 4; i -= 4; } if (i >= 2) { *(Ip_u16 *)(&(copy_to[num])) = *(Ip_u16 *)(&(copy_from[num])); num += 2; i -= 2; } if (i >= 1) *(Ip_u8 *)(&(copy_to[num])) = *(Ip_u8 *)(&(copy_from[num])); } /* *=========================================================================== * ipcom_forwarder_mac_learning *=========================================================================== * Description: Add a MAC address to station list * Parameters: * Returns: * */ IP_INLINE void ipcom_forwarder_mac_learning(int port, Ip_u8 *mac, Ip_u16 svid) { fw_mac_station_t *station, *new_station = IP_NULL; Ip_u8 key[8], i; unsigned hash_val; Ip_u16 hash_bucket; Ip_bool not_found = IP_TRUE; #ifdef FW_VLAN_DEBUG ipcom_printf("mac_learning: port %d, mac: %x %x %x %x %x %x, svid %d\n", port, *mac, *(mac+1), *(mac+2), *(mac+3), *(mac+4), *(mac+5), svid); #endif /* form the hash key: mac address + svid */ *(Ip_u16 *)key = *(Ip_u16 *)mac; *(Ip_u32 *)(&(key[2])) = *(Ip_u32 *)(&(mac[2])); *(Ip_u16 *)(&(key[6])) = svid; /* calculate hash value */ hash_val = ipcom_vlan_hash_update(key, 8, 0, 0); /* record the MAC station */ hash_bucket = (hash_val & 0x3ff) << 2; station = &(mac_stations[hash_bucket]); for (i = 0; i < 4; i++, station++) { if (*(Ip_u64 *)key == *(Ip_u64 *)(station->macAddr_svid)) { /* station already recorded, update the last used time and port */ station->last_used_time = svlan_time; station->ipprt = port; #ifdef FW_VLAN_DEBUG ipcom_printf("mac_learning: station exist already\n"); #endif return; } else if ((*(Ip_u16 *)(&(station->macAddr_svid[6])) == 0) && not_found) { /* an open slot, to record the new MAC station */ new_station = station; not_found = IP_FALSE; } } if (new_station == IP_NULL) /* all slots are currently used, overwrite a random slot */ new_station = &(mac_stations[hash_bucket+(svlan_time & 0x3)]); /* Record the new MAC station info */ new_station->last_used_time = svlan_time; new_station->ipprt = port; *(Ip_u64 *)(new_station->macAddr_svid) = *(Ip_u64 *)key; #ifdef FW_VLAN_DEBUG ipcom_printf("mac_learning: added new station\n"); #endif } /* *=========================================================================== * ipcom_forwarder_get_mac *=========================================================================== * Description: Search a MAC address in station lists * Parameters: * Returns: * */ IP_STATIC __inline__ fw_mac_station_t * ipcom_forwarder_get_mac(fw_msg_t *msg) { fw_mac_station_t *station; Ip_u8 key[8], i; unsigned hash_val; #ifdef FW_VLAN_DEBUG ipcom_printf("ipcom_forwarder_get_mac, mac: %x %x %x %x %x %x, svid %d\n", msg->packet[0], msg->packet[1], msg->packet[2], msg->packet[3], msg->packet[4], msg->packet[5], msg->evlan->s_vlan_id); #endif /* form the hash key: mac address + svid */ *(Ip_u64 *)key = *(Ip_u64 *)msg->packet; *(Ip_u16 *)(&(key[6])) = msg->evlan->s_vlan_id; /* calculate hash value */ hash_val = ipcom_vlan_hash_update(key, 8, 0, 1); /* search the MAC station */ station = &(mac_stations[(hash_val & 0x3ff) << 2]); for (i = 0; i < 4; i++, station++) { if (*(Ip_u64 *)key == *(Ip_u64 *)(station->macAddr_svid)) return station; } return IP_NULL; } /* *=========================================================================== * ipcom_forwarder_lkup_mac *=========================================================================== * Description: Search a MAC address in station lists * Parameters: * Returns: * */ IP_STATIC __inline__ int ipcom_forwarder_lkup_mac(fw_msg_t *msg) { fw_mac_station_t *station; #ifdef FW_VLAN_DEBUG ipcom_printf("ipcom_forwarder_lkup_mac, mac: %x %x %x %x %x %x, svid %d\n", msg->packet[0], msg->packet[1], msg->packet[2], msg->packet[3], msg->packet[4], msg->packet[5], msg->evlan->s_vlan_id); #endif station = ipcom_forwarder_get_mac(msg); if (station) { /* found the station, update the last used time */ station->last_used_time = svlan_time; /* check whether the port is one of tx ports */ if (msg->evlan->tx_ports[station->ipprt>>5] & (1 << (station->ipprt&0x1f))) { #ifdef FW_VLAN_DEBUG ipcom_printf("ipcom_forwarder_lkup_mac, station rtn %d\n", station->ipprt); #endif return station->ipprt; } } #ifdef FW_VLAN_DEBUG ipcom_printf("ipcom_forwarder_lkup_mac, no station found, rtn -1\n"); #endif return -1; } /* *=========================================================================== * ipcom_svlan_cs_fmt *=========================================================================== * Context: * Description: Format output VLAN stacking frame from CS port to SC port. * Insert S-VLAN tag. * Parameters: * Returns: * */ IP_STATIC __inline__ void ipcom_svlan_cs_fmt(fw_msg_t *fw_msg, Ip_u8 ix) { Ip_u8 *pkt_ptr; #ifdef FW_VLAN_DEBUG ipcom_printf("Enter ipcom_svlan_cs_fmt, ix %d, fw_msg->packet %x\n", ix, fw_msg->packet); /* { int i; for (i = 0; i < 40; i++) printf("%x ", *((Ip_u8 *)(fw_msg->packet + i))); printf("\n"); } */ #endif /* Re-map the rx pkt's priority */ if (!(fw_msg->pkt_flags & IPCOM_FWD_VLAN_PKT_MAPED)) { fw_msg->pkt_flags |= IPCOM_FWD_VLAN_PKT_MAPED; if (fw_msg->stci) fw_msg->spri = fw_msg->evlan->pri_mapping[(fw_msg->stci&0xe000)>>13]; else if (fw_msg->ctci) fw_msg->spri = fw_msg->evlan->pri_mapping[(fw_msg->ctci&0xe000)>>13]; else fw_msg->spri = fw_msg->evlan->pri_mapping[0]; } /* Check on the VLAN tags */ switch ((fw_msg->pkt_flags & IPCOM_FWD_VLAN_PKT_SVTAG) | (fw_msg->pkt_flags & IPCOM_FWD_VLAN_PKT_CVTAG) | (ipcom_fw->rule_flags[ix] & IPCOM_FWD_VLAN_ADD_CVLAN)) { case 2: /* 010: C-VLAN + egress rule as remove C-VLAN */ /* modify C-VLAN to S-VLAN */ *(Ip_u32 *)(fw_msg->packet+LL_ETH_ADDRS_SZ) = fw_msg->evlan->s_vlan_tag + ip_htonl((fw_msg->spri << 13) & 0xe000); #ifdef IPCOM_USE_OCTEON_FORWARDER fw_msg->pkt_flags &= ~IPCOM_FWD_VLAN_PKT_CVTAG; fw_msg->pkt_flags |= IPCOM_FWD_VLAN_PKT_SVTAG; #endif return; case 4: /* 100: egress rule as add C-VLAN */ /* add two VLAN tags: S-VLAN + C-VLAN */ /* copy dst + src MAC addresses: add space for 2 VLAN tags */ *(Ip_u64 *)(fw_msg->packet-8) = *(Ip_u64 *)(fw_msg->packet); *(Ip_u32 *)(fw_msg->packet) = *(Ip_u32 *)(fw_msg->packet+8); /* Insert S-VLAN */ *(Ip_u32 *)(fw_msg->packet+4) = fw_msg->evlan->s_vlan_tag + ip_htonl((fw_msg->spri << 13) & 0xe000); /* insert C-VLAN tag */ *(Ip_u32 *)(fw_msg->packet+8) = fw_msg->evlan->c_vlan_tag; fw_msg->packet -= 8; fw_msg->len_mod = 8; #ifdef IPCOM_USE_OCTEON_FORWARDER fw_msg->pkt_flags|=(IPCOM_FWD_VLAN_PKT_SVTAG|IPCOM_FWD_VLAN_PKT_CVTAG); #endif return; case 0: /* 000: egress rule as no C-VLAN */ case 6: /* 110: C-VLAN + egress rule as keep C-VLAN */ /* add S-VLAN */ /* copy dst + src MAC addresses: add space for one VLAN tag */ *(Ip_u64 *)(fw_msg->packet-4) = *(Ip_u64 *)(fw_msg->packet); *(Ip_u32 *)(fw_msg->packet+4) = *(Ip_u32 *)(fw_msg->packet+8); /* Insert S-VLAN */ *(Ip_u32 *)(fw_msg->packet+8) = fw_msg->evlan->s_vlan_tag + ip_htonl((fw_msg->spri << 13) & 0xe000); fw_msg->packet -= 4; fw_msg->len_mod = 4; #ifdef IPCOM_USE_OCTEON_FORWARDER fw_msg->pkt_flags |= IPCOM_FWD_VLAN_PKT_SVTAG; #endif return; case 3: /* 011: S-VLAN + C-VLAN + egress rule as remove C-VLAN */ /* remove C-VLAN */ /* copy dst+src MAC addr + S-VLAN: remove space for one VLAN tag */ pkt_ptr = fw_msg->packet+LL_ETH_ADDRS_SZ; *(Ip_u64 *)pkt_ptr = *(Ip_u64 *)(pkt_ptr - 4); *(Ip_u64 *)(pkt_ptr-8) = *(Ip_u64 *)(pkt_ptr - 12); /* modify S-VLAN */ fw_msg->packet += 4; *(Ip_u32 *)(fw_msg->packet+LL_ETH_ADDRS_SZ) = fw_msg->evlan->s_vlan_tag + ip_htonl((fw_msg->spri << 13) & 0xe000); fw_msg->len_mod = -4; #ifdef IPCOM_USE_OCTEON_FORWARDER fw_msg->pkt_flags &= ~IPCOM_FWD_VLAN_PKT_CVTAG; #endif return; case 5: /* 101: S-VLAN + egress rule as add C-VLAN */ /* insert C-VLAN */ /* copy dst+src MAC addr + S-VLAN: add space for one VLAN tag */ *(Ip_u64 *)(fw_msg->packet-4) = *(Ip_u64 *)(fw_msg->packet); *(Ip_u64 *)(fw_msg->packet+4) = *(Ip_u64 *)(fw_msg->packet + 8); /* insert C-VLAN tag */ *(Ip_u32 *)(fw_msg->packet+12) = fw_msg->evlan->c_vlan_tag; /* modify S-VLAN */ fw_msg->packet -= 4; *(Ip_u32 *)(fw_msg->packet+LL_ETH_ADDRS_SZ) = fw_msg->evlan->s_vlan_tag + ip_htonl((fw_msg->spri << 13) & 0xe000); fw_msg->len_mod = 4; #ifdef IPCOM_USE_OCTEON_FORWARDER fw_msg->pkt_flags |= IPCOM_FWD_VLAN_PKT_CVTAG; #endif return; case 1: /* 001: S-VLAN + egress rule as no C-VLAN */ case 7: /* 111: S-VLAN + C-VLAN + egress rule as keep C-VLAN */ /* modify S-VLAN */ *(Ip_u32 *)(fw_msg->packet+LL_ETH_ADDRS_SZ) = fw_msg->evlan->s_vlan_tag + ip_htonl((fw_msg->spri << 13) & 0xe000); return; } } /* *=========================================================================== * ipcom_svlan_sc_fmt *=========================================================================== * Context: * Description: Format output VLAN stacking frame from SC port to CS port. * Remove S-VLAN tag. * Parameters: * Returns: * */ IP_STATIC __inline__ void ipcom_svlan_sc_fmt(fw_msg_t *fw_msg, Ip_u8 ix) { Ip_u8 *pkt_ptr; #ifdef FW_VLAN_DEBUG ipcom_printf("Enter ipcom_svlan_sc_fmt, ix %d, fw_msg->packet %x\n", ix, fw_msg->packet); /* { int i; for (i = 0; i < 40; i++) printf("%x ", *((Ip_u8 *)(fw_msg->packet + i))); printf("\n"); } */ #endif /* Check on the VLAN tags and egress rule */ switch ((fw_msg->pkt_flags & IPCOM_FWD_VLAN_PKT_SVTAG) | (fw_msg->pkt_flags & IPCOM_FWD_VLAN_PKT_CVTAG) | (ipcom_fw->rule_flags[ix] & IPCOM_FWD_VLAN_ADD_CVLAN)) { case 5: /* 101: S-VLAN + egress rule as add C-VLAN */ /* modify S-VLAN to C-VLAN */ *(Ip_u32 *)(fw_msg->packet+LL_ETH_ADDRS_SZ) = fw_msg->evlan->c_vlan_tag; #ifdef IPCOM_USE_OCTEON_FORWARDER fw_msg->pkt_flags |= IPCOM_FWD_VLAN_PKT_CVTAG; fw_msg->pkt_flags &= ~IPCOM_FWD_VLAN_PKT_SVTAG; #endif return; case 3: /* 011: S-VLAN + C-VLAN + egress rule as remove C-VLAN */ /* remove two VLAN tags: S-VLAN + C-VLAN */ /* copy dst + src MAC addresses: remove space for 2 VLAN tags */ pkt_ptr = fw_msg->packet+LL_ETH_ADDRS_SZ; *(Ip_u64 *)pkt_ptr = *(Ip_u64 *)(pkt_ptr-8); *(Ip_u32 *)(pkt_ptr-4) = *(Ip_u32 *)(pkt_ptr-12); fw_msg->packet += 8; fw_msg->len_mod = -8; #ifdef IPCOM_USE_OCTEON_FORWARDER fw_msg->pkt_flags &= ~IPCOM_FWD_VLAN_PKT_CVTAG; fw_msg->pkt_flags &= ~IPCOM_FWD_VLAN_PKT_SVTAG; #endif return; case 1: /* 001: S-VLAN + egress rule as no C-VLAN */ case 7: /* 111: S-VLAN + C-VLAN + egress rule as keep C-VLAN */ /* remove S-VLAN */ /* copy dst + src MAC addresses: remove space for one VLAN tag */ pkt_ptr = fw_msg->packet+LL_ETH_ADDRS_SZ; *(Ip_u64 *)(pkt_ptr-4) = *(Ip_u64 *)(pkt_ptr-8); *(Ip_u32 *)(pkt_ptr-8) = *(Ip_u32 *)(pkt_ptr-12); fw_msg->packet += 4; fw_msg->len_mod = -4; #ifdef IPCOM_USE_OCTEON_FORWARDER fw_msg->pkt_flags &= ~IPCOM_FWD_VLAN_PKT_SVTAG; #endif return; case 0: /* 000: egress rule as no C-VLAN */ case 6: /* 110: C-VLAN + egress rule as keep C-VLAN */ /* nothing to do */ return; case 2: /* 010: C-VLAN + egress rule as remove C-VLAN */ /* remove C-VLAN */ /* copy dst + src MAC addresses: remove space for one VLAN tag */ pkt_ptr = fw_msg->packet+LL_ETH_ADDRS_SZ; *(Ip_u64 *)(pkt_ptr-4) = *(Ip_u64 *)(pkt_ptr-8); *(Ip_u32 *)(pkt_ptr-8) = *(Ip_u32 *)(pkt_ptr-12); fw_msg->packet += 4; fw_msg->len_mod = -4; #ifdef IPCOM_USE_OCTEON_FORWARDER fw_msg->pkt_flags &= ~IPCOM_FWD_VLAN_PKT_CVTAG; #endif return; case 4: /* 100: egress rule as add C-VLAN */ /* add C-VLAN */ /* copy dst + src MAC addresses: add space for one VLAN tag */ *(Ip_u64 *)(fw_msg->packet-4) = *(Ip_u64 *)(fw_msg->packet); *(Ip_u32 *)(fw_msg->packet+4) = *(Ip_u32 *)(fw_msg->packet+8); *(Ip_u32 *)(fw_msg->packet+8) = fw_msg->evlan->c_vlan_tag; fw_msg->packet -= 4; fw_msg->len_mod = 4; #ifdef IPCOM_USE_OCTEON_FORWARDER fw_msg->pkt_flags |= IPCOM_FWD_VLAN_PKT_CVTAG; #endif return; } } /* *=========================================================================== * ipcom_forwarder_vlan_tx *=========================================================================== * Context: * Description: Transmit a VLAN stacking frame * Parameters: * Returns: * */ IP_STATIC __inline__ int ipcom_forwarder_vlan_tx(fw_msg_t *fw_msg, void *fw_cookie) { int ix_num = 0; fw_mac_station_t *station; #ifdef FW_VLAN_DEBUG ipcom_printf("Enter ipcom_forwarder_vlan_tx, ipprt %d\n", fw_msg->ipprt); #endif /* Check whether dst MAC is on the receiving link */ station = ipcom_forwarder_get_mac(fw_msg); if (station && (station->ipprt == fw_msg->ipprt)) { /* dst MAC on the same link, don't forward, drop the pkt */ ipcom_forwarder_drop_fw_msg(fw_msg, fw_cookie); #ifndef SVLAN_SC_FWD_TABLE if (ipcom_fw->port_type[fw_msg->ipprt] & IPCOM_FWD_VLAN_PORT_SC) ipcom_free(fw_msg->evlan); #endif return 0; } #ifdef FW_VLAN_DEBUG ipcom_printf("ipcom_forwarder_vlan_tx: pkt_flags %x, MBCAST %x, & %d\n", fw_msg->pkt_flags, IPCOM_FWD_VLAN_PKT_BMCAST, fw_msg->pkt_flags & IPCOM_FWD_VLAN_PKT_BMCAST); #endif fw_msg->len_mod = 0; if (!(fw_msg->pkt_flags & IPCOM_FWD_VLAN_PKT_BMCAST)) { if (fw_msg->evlan->tx_ports_num == 1) { /* only one tx port, get the port number */ #ifdef IPCOM_USE_OCTEON_FORWARDER ix_num = FFS_LSB(fw_msg->evlan->tx_ports[0]? fw_msg->evlan->tx_ports[0]: fw_msg->evlan->tx_ports[1]) - 1; #else while ((fw_msg->evlan->tx_ports[ix_num>>5]&(1<<(ix_num&0x1f)))==0) ix_num ++; #endif } else { /* get the port with dst MAC in station list */ ix_num = ipcom_forwarder_lkup_mac(fw_msg); if (IP_LIKELY(ix_num < 0)) goto multi_tx; } #ifdef FW_VLAN_DEBUG ipcom_printf("ipcom_forwarder_vlan_tx: ix_num %d, tx_ports_num %d\n", ix_num, fw_msg->evlan->tx_ports_num); #endif /* output to one port */ FWD_SVLAN_FMT(fw_msg->ipprt, fw_msg, ix_num); #ifdef FW_VLAN_DEBUG ipcom_printf("ipcom_forwarder_vlan_tx: single port %d, to forwarder_tx_fw\n", ix_num); #endif #ifndef SVLAN_SC_FWD_TABLE if (ipcom_fw->port_type[fw_msg->ipprt] & IPCOM_FWD_VLAN_PORT_SC) ipcom_free(fw_msg->evlan); #endif return ipcom_forwarder_tx_fw(ix_num, fw_msg, fw_cookie); } /* output to multiple ports */ multi_tx: #ifdef FW_VLAN_DEBUG ipcom_printf("ipcom_forwarder_vlan_tx: multi ports, tx_ports_num %d\n", fw_msg->evlan->tx_ports_num); #endif #ifdef SVLAN_SC_FWD_TABLE return ipcom_forwarder_vlan_hw_tx[fw_msg->ipprt](fw_msg, fw_cookie); #else ipcom_forwarder_vlan_hw_tx[fw_msg->ipprt](fw_msg, fw_cookie); if (ipcom_fw->port_type[fw_msg->ipprt] & IPCOM_FWD_VLAN_PORT_SC) ipcom_free(fw_msg->evlan); return 0; #endif } #ifdef __cplusplus } #endif #endif #endif /* **************************************************************************** * END OF FILE **************************************************************************** */