/* @doc RTL_LAYEREDDRV_API @module rtl865x_fdb.c - RTL865x Home gateway controller Layered driver API documentation | This document explains the API interface of the table driver module. Functions with rtl865x prefix are external functions. @normal Hyking Liu (Hyking_liu@realsil.com.cn) <date> Copyright <cp>2008 Realtek<tm> Semiconductor Cooperation, All Rights Reserved. @head3 List of Symbols | Here is a list of all functions and variables in this module. @index | RTL_LAYEREDDRV_API */ #include <linux/version.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0) #include <linux/kconfig.h> #else #include <linux/config.h> #endif #include <linux/module.h> #include <linux/kernel.h> #include <net/rtl/rtl_types.h> #include <net/rtl/rtl_glue.h> #include <net/rtl/rtl865x_fdb_api.h> #include "AsicDriver/asicRegs.h" //#include <common/rtl8651_aclLocal.h> #include <net/rtl/rtl865x_netif.h> #include "common/rtl865x_eventMgr.h" #include "common/mbuf.h" #include "rtl865x_fdb.h" #include "common/rtl_errno.h" #include "common/rtl865x_vlan.h" #include <linux/netdevice.h> #include "../../../net/bridge/br_private.h" #include <net/rtl/rtl867x_hwnat_api.h> #define DBG_FDB_PRK( comment ,arg...) DBG_PRK(RTL_DEBUG_LEVEL_FDB,"[FDB]",39,94,comment,##arg) #ifdef CONFIG_RTL_XDSL_WIFI_HWACCELERATION extern struct net_device *rtk_xdsl_hwnat_wifiDev[]; struct list_head rtk_xdsl_hwnat_mbssid_mac_mappingListHead[RTK_XDSL_HWNAT_MBSSID_MAC_MAPPING_BUCKET_SIZE]; #endif struct rtl865x_L2Tables sw_FDB_Table; int32 arpAgingTime = 450; ether_addr_t cpu_mac = { {0x00, 0x00, 0x0a, 0x00, 0x00, 0x0f} }; rtl865x_tblAsicDrv_l2Param_t tmpL2buff; #ifdef CONFIG_RTK_REMOTE_ETH_PHY extern int remotePhy_evt_notify(unsigned char event,void * data); #define EWAN_LINK_DOWN 100 #define EWAN_LINK_UP 101 #endif static RTL_DECLARE_MUTEX(l2_sem); DEFINE_SPINLOCK(fdb_spinlock); int32 _rtl865x_fdb_alloc(void) { int32 index = 0; memset( &sw_FDB_Table, 0, sizeof( sw_FDB_Table ) ); #ifdef __KERNEL__ TBL_MEM_ALLOC(sw_FDB_Table.filterDB, rtl865x_filterDbTable_t, RTL865x_FDB_NUMBER); #else sw_FDB_Table.filterDB = (rtl865x_filterDbTable_t *)malloc(RTL865x_FDB_NUMBER * sizeof(rtl865x_filterDbTable_t)); #endif {//Initial free filter database entry rtl865x_filterDbTableEntry_t * tempFilterDb = NULL; #ifdef __KERNEL__ TBL_MEM_ALLOC(tempFilterDb, rtl865x_filterDbTableEntry_t, RTL8651_L2TBL_ROW); #else #endif SLIST_INIT(&sw_FDB_Table.freefdbList.filterDBentry); for(index=0; index<RTL8651_L2TBL_ROW; index++) SLIST_INSERT_HEAD(&sw_FDB_Table.freefdbList.filterDBentry, &tempFilterDb[index], nextFDB); } return SUCCESS; } int32 rtl865x_getReserveMacAddr(ether_addr_t *macAddr) { memcpy( macAddr, &cpu_mac, sizeof(ether_addr_t)); return SUCCESS; } int32 _rtl865x_layer2_patch(void) { int32 retval = 0; #ifndef CONFIG_REMOTE_ADSL_PHY ether_addr_t mac = { {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} }; #endif int fid; #ifdef CONFIG_RTL_MULTI_LAN_DEV #ifndef CONFIG_REMOTE_ADSL_PHY /*cancel default trap FF:FF:FF:FF:FF:FF to CPU,make it flood to all ports*/ uint32 portmask=0x0; /* in ETHWAN, we let broadcast pkts up to cpu */ #endif #else #ifndef CONFIG_REMOTE_ADSL_PHY uint32 portmask=0xffffffff; #endif #endif for(fid=0;fid<RTL865X_FDB_NUMBER;fid++) { #ifndef CONFIG_RTL_IVL_SUPPORT if(fid != RTL_LAN_FID) continue; #endif #ifndef CONFIG_REMOTE_ADSL_PHY retval = _rtl865x_addFilterDatabaseEntry(RTL865x_L2_TYPEII, fid, &mac , FDB_TYPE_TRAPCPU, portmask , TRUE, FALSE); #endif retval = _rtl865x_addFilterDatabaseEntry(RTL865x_L2_TYPEII, fid, &cpu_mac , FDB_TYPE_TRAPCPU, 0 , TRUE, FALSE); } assert(retval == SUCCESS); return SUCCESS; } int32 _rtl865x_fdb_collect(void) { int32 index = 0; int32 index1 = 0; rtl865x_filterDbTable_t *filterDbPtr; rtl865x_filterDbTableEntry_t * tempFilterDbPtr; for(index=0,filterDbPtr=&sw_FDB_Table.filterDB[0]; index<RTL865x_FDB_NUMBER; index++,filterDbPtr++) { for(index1=0; index1<RTL8651_L2TBL_ROW; index1++) while(SLIST_FIRST(&(filterDbPtr->database[index1]))) { tempFilterDbPtr = SLIST_FIRST(&(filterDbPtr->database[index1])); SLIST_REMOVE_HEAD(&(filterDbPtr->database[index1]), nextFDB); SLIST_INSERT_HEAD(&sw_FDB_Table.freefdbList.filterDBentry, tempFilterDbPtr, nextFDB); } } return SUCCESS; } int32 _rtl865x_fdb_init(void) { int32 index, index1; /*Initial Filter database*/ rtl865x_filterDbTable_t *fdb_t = &sw_FDB_Table.filterDB[0]; for(index=0; index<RTL865x_FDB_NUMBER; index++, fdb_t++) { for(index1=0; index1<RTL8651_L2TBL_ROW; index1++) SLIST_INIT(&(fdb_t->database[index1])); fdb_t->valid = 1; } #ifdef CONFIG_RTL_XDSL_WIFI_HWACCELERATION { int i; for(i = 0; i < RTK_XDSL_HWNAT_MBSSID_MAC_MAPPING_BUCKET_SIZE; i++) INIT_LIST_HEAD(&rtk_xdsl_hwnat_mbssid_mac_mappingListHead[i]); rtk_xdsl_hwnat_wifiDev[0] = rtk_xdsl_hwnat_wifiDev[1] = NULL; } #endif return SUCCESS; } int32 _rtl865x_clearHWL2Table(void) { int i, j; for (i = 0; i < RTL8651_L2TBL_ROW; i++) for (j = 0; j < RTL8651_L2TBL_COLUMN; j++) { rtl8651_delAsicL2Table(i, j); } return SUCCESS; } int32 rtl865x_layer2_init(void) { _rtl865x_fdb_alloc(); _rtl865x_fdb_init(); _rtl865x_layer2_patch(); return SUCCESS; } int32 rtl865x_layer2_reinit(void) { _rtl865x_clearHWL2Table(); _rtl865x_fdb_collect(); //_rtl865x_fdb_alloc(); _rtl865x_fdb_init(); _rtl865x_layer2_patch(); return SUCCESS; } uint32 rtl865x_getHWL2Index(ether_addr_t * macAddr, uint16 fid) { return (rtl8651_filterDbIndex(macAddr, fid)); } int32 rtl865x_setHWL2Table(uint32 row, uint32 column, rtl865x_tblAsicDrv_l2Param_t *l2p) { return (rtl8651_setAsicL2Table(row, column, l2p)); } int32 rtl865x_getHWL2Table(uint32 row, uint32 column, rtl865x_tblAsicDrv_l2Param_t *l2p) { return rtl8651_getAsicL2Table(row, column, l2p); } int32 rtl865x_refleshHWL2Table(ether_addr_t * macAddr, uint32 flags,uint16 fid) { rtl865x_tblAsicDrv_l2Param_t L2temp, *L2buff; uint32 rowIdx, col_num; uint32 colIdx = 0; uint32 found = FALSE; L2buff = &L2temp; memset(L2buff, 0, sizeof(rtl865x_tblAsicDrv_l2Param_t)); rowIdx = rtl8651_filterDbIndex(macAddr, fid); for(colIdx=0; colIdx<RTL8651_L2TBL_COLUMN; colIdx++) { if ((rtl8651_getAsicL2Table(rowIdx, colIdx, L2buff))!=SUCCESS || memcmp(&(L2buff->macAddr), macAddr, 6)!= 0) continue; if (((flags&FDB_STATIC) && L2buff->isStatic) || ((flags&FDB_DYNAMIC) && !L2buff->isStatic)) { assert(colIdx); col_num = colIdx; found = TRUE; break; } } if (found == TRUE) { L2buff->ageSec = arpAgingTime; rtl8651_setAsicL2Table(rowIdx, colIdx, L2buff); return SUCCESS; } else { return FAILED; } } int32 rtl_get_hw_fdb_age(uint32 fid,ether_addr_t *mac, uint32 flags) { uint32 rowIdx; uint32 colIdx = 0; int32 retval = 0; rtl865x_tblAsicDrv_l2Param_t L2buff; memset(&L2buff,0,sizeof(rtl865x_tblAsicDrv_l2Param_t)); rowIdx = rtl8651_filterDbIndex(mac, fid); for(colIdx=0; colIdx<RTL8651_L2TBL_COLUMN; colIdx++) { retval = rtl8651_getAsicL2Table(rowIdx, colIdx, &L2buff); if (retval !=SUCCESS || memcmp(&(L2buff.macAddr), mac, 6)!= 0) continue; if (((flags&FDB_DYNAMIC) && !L2buff.isStatic)||((flags&FDB_STATIC) && L2buff.isStatic)) { retval = L2buff.ageSec; break; } } return retval; } int32 rtl865x_Lookup_fdb_entry(uint32 fid, ether_addr_t *mac, uint32 flags, uint32 *col_num, rtl865x_tblAsicDrv_l2Param_t *L2buff) { uint32 rowIdx; uint32 colIdx = 0; int32 retval = 0; rowIdx = rtl8651_filterDbIndex(mac, fid); for(colIdx=0; colIdx<RTL8651_L2TBL_COLUMN; colIdx++) { retval = rtl8651_getAsicL2Table(rowIdx, colIdx, L2buff); if (retval !=SUCCESS || memcmp(&(L2buff->macAddr), mac, 6)!= 0) continue; if (((flags&FDB_STATIC) && L2buff->isStatic) || ((flags&FDB_DYNAMIC) && !L2buff->isStatic)) { assert(colIdx); *col_num = colIdx; return SUCCESS; } } return FAILED; } int32 rtl865x_Lookup_L2_by_MAC(const unsigned char *addr) { ether_addr_t *macAddr; int32 column; rtl865x_tblAsicDrv_l2Param_t fdbEntry; int32 found; macAddr = (ether_addr_t *)(addr); #if defined(CONFIG_RTL_SW_FDB_LEARNING) found = rtl865x_Lookup_fdb_entry(RTL_LAN_FID, macAddr, FDB_STATIC, &column, &fdbEntry); #else found = rtl865x_Lookup_fdb_entry(RTL_LAN_FID, macAddr, FDB_DYNAMIC, &column, &fdbEntry); #endif return found; } /* @func enum RTL_RESULT | rtl865x_addFdbEntry | Add an MAC address, said Filter Database Entry @parm uint32 | fid | The filter database index (valid: 0~3) @parm ether_addr_t * | mac | The MAC address to be added @parm uint32 | portmask | The portmask of this MAC address belongs to @parm uint32 | type | fdb entry type @rvalue SUCCESS | Add success @rvalue FAILED | General failure @comm Add a Filter Database Entry to L2 Table(1024-Entry) @devnote (under construction) */ int32 rtl865x_addFilterDatabaseEntry( uint32 fid, ether_addr_t * mac, uint32 portmask, uint32 type ) { int32 retval; unsigned long flags; if (type != FDB_TYPE_FWD && type != FDB_TYPE_SRCBLK && type != FDB_TYPE_TRAPCPU) return RTL_EINVALIDINPUT; /* Invalid parameter */ if (fid >= RTL865x_FDB_NUMBER) return RTL_EINVALIDINPUT; if (mac == (ether_addr_t *)NULL) return RTL_EINVALIDINPUT; if (sw_FDB_Table.filterDB[fid].valid == 0) return RTL_EINVALIDFID; /*l2 lock*/ //rtl_down_interruptible(&l2_sem); spin_lock_irqsave(&fdb_spinlock,flags); #ifdef CONFIG_RTL865X_LANPORT_RESTRICTION retval = _rtl865x_addFilterDatabaseEntry(RTL865x_L2_TYPEII, fid, mac, type, portmask, TRUE, FALSE); #else retval = _rtl865x_addFilterDatabaseEntry(RTL865x_L2_TYPEII, fid, mac, type, portmask, FALSE, FALSE); #endif /*l2 unlock*/ //rtl_up(&l2_sem); spin_unlock_irqrestore(&fdb_spinlock,flags); return retval; } int32 rtl865x_addFilterDatabaseEntryExtension( uint16 fid, rtl865x_filterDbTableEntry_t * L2entry) { int32 retval; unsigned long flags; if (L2entry->process != FDB_TYPE_FWD && L2entry->process != FDB_TYPE_SRCBLK && L2entry->process != FDB_TYPE_TRAPCPU) return RTL_EINVALIDINPUT; /* Invalid parameter */ if (fid >= RTL865x_FDB_NUMBER) return RTL_EINVALIDINPUT; if (&(L2entry->macAddr) == (ether_addr_t *)NULL) return RTL_EINVALIDINPUT; if (sw_FDB_Table.filterDB[fid].valid == 0) return RTL_EINVALIDFID; /*l2 lock*/ //rtl_down_interruptible(&l2_sem); spin_lock_irqsave(&fdb_spinlock,flags); retval = _rtl865x_addFilterDatabaseEntry( L2entry->l2type, fid, &(L2entry->macAddr), L2entry->process, L2entry->memberPortMask, L2entry->auth, L2entry->SrcBlk); /*l2 unlock*/ //rtl_up(&l2_sem); spin_unlock_irqrestore(&fdb_spinlock,flags); return retval; } #if 0 int32 rtl865x_addFilterDatabaseEntry( uint32 fid, ether_addr_t * mac, uint32 portmask, uint32 type , uint32 isStatic) { int32 retval; if (type != FDB_TYPE_FWD && type != FDB_TYPE_SRCBLK && type != FDB_TYPE_TRAPCPU) return RTL_EINVALIDINPUT; /* Invalid parameter */ if (fid >= RTL865x_FDB_NUMBER) return RTL_EINVALIDINPUT; if (mac == (ether_addr_t *)NULL) return RTL_EINVALIDINPUT; if (sw_FDB_Table.filterDB[fid].valid == 0) return RTL_EINVALIDFID; /*l2 lock*/ #ifdef RTL865XC_LAN_PORT_NUM_RESTRIT retval = _rtl865x_addFilterDatabaseEntry(isStatic == TRUE? RTL865x_L2_TYPEII:RTL865x_L2_TYPEI, fid, mac, type, portmask, TRUE, FALSE); #else retval = _rtl865x_addFilterDatabaseEntry(isStatic == TRUE? RTL865x_L2_TYPEII:RTL865x_L2_TYPEI, fid, mac, type, portmask, FALSE, FALSE); #endif /*l2 unlock*/ return retval; } #endif int32 _rtl865x_removeFilterDatabaseEntry(uint16 fid, ether_addr_t * mac, uint32 rowIdx) { rtl865x_filterDbTable_t *fdb_t = &sw_FDB_Table.filterDB[fid]; rtl865x_filterDbTableEntry_t * l2entry_t ; if (SLIST_FIRST(&(fdb_t->database[rowIdx]))) { SLIST_FOREACH(l2entry_t, &(fdb_t->database[rowIdx]), nextFDB) { if (memcmp(&(l2entry_t->macAddr), mac, 6)== 0) { SLIST_REMOVE( &(fdb_t->database[rowIdx]), l2entry_t, rtl865x_filterDbTableEntry_s, nextFDB ); SLIST_INSERT_HEAD(&sw_FDB_Table.freefdbList.filterDBentry, l2entry_t, nextFDB); rtl865x_raiseEvent(EVENT_DEL_FDB, (void *)(l2entry_t)); return SUCCESS; } } } return FAILED; } int32 rtl865x_lookup_FilterDatabaseEntry(uint16 fid, ether_addr_t * mac, rtl865x_filterDbTableEntry_t *l2_entry) { rtl865x_filterDbTable_t *fdb_t = &sw_FDB_Table.filterDB[fid]; rtl865x_filterDbTableEntry_t * l2entry_t ; uint32 rowIdx; rowIdx = rtl8651_filterDbIndex(mac, fid); if (SLIST_FIRST(&(fdb_t->database[rowIdx]))) { SLIST_FOREACH(l2entry_t, &(fdb_t->database[rowIdx]), nextFDB) { if (memcmp(&(l2entry_t->macAddr), mac, 6)== 0) { l2_entry->asicPos = l2entry_t->asicPos; l2_entry->auth = l2entry_t->auth; l2_entry->l2type = l2entry_t->l2type; l2_entry->linkId = l2entry_t->linkId; l2_entry->memberPortMask = l2entry_t->memberPortMask; l2_entry->nhFlag = l2entry_t->nhFlag; l2_entry->process = l2entry_t->process; l2_entry->SrcBlk = l2entry_t->SrcBlk; l2_entry->vid = l2entry_t->vid; return SUCCESS; } } } l2_entry = NULL; return FAILED; } int32 _rtl865x_addFilterDatabaseEntry(uint16 l2Type, uint16 fid, ether_addr_t * macAddr, uint32 type, uint32 portMask, uint8 auth, uint8 SrcBlk) { uint32 rowIdx = 0; uint32 colIdx = 0; uint32 col_num = 0; uint32 col_tmp = 0; uint16 tmp_age = 0xffff; int32 found = FALSE; int32 flag = FALSE; int32 nexthp_flag = FALSE; int32 isStatic = FALSE; int32 toCpu = FALSE; rtl865x_filterDbTableEntry_t * l2entry_t = NULL; rtl865x_filterDbTableEntry_t *tmpL2; rtl865x_filterDbTable_t *fdb_t = &sw_FDB_Table.filterDB[fid]; rtl865x_tblAsicDrv_l2Param_t l2entry_tmp,*L2buff; int32 retval = 0; // printk("%s:%d\n,fid(%d),mac(%02x:%02x:%02x:%02x:%02x:%02x)\n",__FUNCTION__,__LINE__,fid,macAddr->octet[0],macAddr->octet[1], // macAddr->octet[2],macAddr->octet[3],macAddr->octet[4],macAddr->octet[5]); // // printk("%s:%d\n",__FUNCTION__,__LINE__); L2buff = &l2entry_tmp; memset(L2buff,0,sizeof(rtl865x_tblAsicDrv_l2Param_t)); rowIdx = rtl8651_filterDbIndex(macAddr, fid); for(colIdx=0; colIdx<RTL8651_L2TBL_COLUMN; colIdx++) { retval = rtl8651_getAsicL2Table(rowIdx, colIdx, L2buff); if ((retval)==SUCCESS) { /*check whether mac address has been learned to HW or not*/ if (memcmp(&(L2buff->macAddr), macAddr, 6)== 0) { /*the entry has been auto learned*/ if (L2buff->memberPortMask==portMask) { /*the entry has been auto learned */ found = TRUE; col_tmp = colIdx; } else /*portmask is different , it should be overwrited*/ { found = FALSE; flag = TRUE; } break; } /*no matched entry, try get minimum aging time L2 Asic entry*/ if (tmp_age> L2buff->ageSec) { tmp_age = L2buff->ageSec; col_num = colIdx; } } else { /*there is still empty l2 asic entry*/ flag = TRUE; break; } } switch(l2Type) { case RTL865x_L2_TYPEI: nexthp_flag = FALSE;isStatic = FALSE; break; case RTL865x_L2_TYPEII: nexthp_flag = TRUE; isStatic = TRUE; break; case RTL865x_L2_TYPEIII: nexthp_flag = FALSE;isStatic = TRUE; break; default: assert(0); } switch(type) { case FDB_TYPE_FWD: toCpu = FALSE; break; case FDB_TYPE_DSTBLK: toCpu = FALSE; break; case FDB_TYPE_SRCBLK: toCpu = FALSE; break; case FDB_TYPE_TRAPCPU: toCpu = TRUE; break; default: assert(0); } if (found == FALSE) { #if 0 /*no empty entry, overwrite the biggest aging time asic l2 entry*/ if(flag == FALSE) { /*delete the biggest aging time software entry*/ rtl8651_getAsicL2Table(rowIdx, col_num, L2buff); _rtl865x_removeFilterDatabaseEntry(fid, &(L2buff->macAddr), rowIdx); /*overwrite asic entry*/ rtl8651_setAsicL2Table_Patch( rowIdx, col_num, macAddr, toCpu, SrcBlk, portMask, arpAgingTime, isStatic, nexthp_flag, fid, auth); col_tmp = col_num; } else #endif /*portmask is different , so it should overwrite the original asic entry. Or there is empty entry, set it to asic*/ if(flag == TRUE) { rtl8651_setAsicL2Table_Patch( rowIdx, colIdx, macAddr, toCpu, SrcBlk, portMask, arpAgingTime, isStatic, nexthp_flag, fid, auth); col_tmp = colIdx; } } /*find the same asic entry, should update the aging time*/ else { rtl8651_setAsicL2Table_Patch( rowIdx, col_tmp, macAddr, toCpu, SrcBlk, portMask, arpAgingTime, isStatic, nexthp_flag, fid, auth); } /* colIdx=0; tmpL2 = SLIST_FIRST(&(fdb_t->database[rowIdx])); tmpL2->nextFDB.sle_next = NULL; for(colIdx=0; colIdx<RTL8651_L2TBL_COLUMN; colIdx++) { if ((rtl8651_getAsicL2Table(rowIdx, colIdx, L2buff))==SUCCESS) { l2entry_t->asicPos =colIdx; l2entry_t->auth = L2buff->auth; l2entry_t->configToAsic = 0; l2entry_t->memberPortMask = L2buff->memberPortMask; l2entry_t->SrcBlk = L2buff->srcBlk; memcpy(&l2entry_t->macAddr, &L2buff->macAddr, sizeof(ether_addr_t)); tmpL2->nextFDB.sle_next = l2entry_t; l2entry_t->nextFDB.sle_next = NULL; tmpL2 = l2entry_t; } } */ if (SLIST_FIRST(&sw_FDB_Table.freefdbList.filterDBentry) == NULL) return RTL_ENOFREEBUFFER; /*config the SW l2 entry */ l2entry_t = SLIST_FIRST(&sw_FDB_Table.freefdbList.filterDBentry); SLIST_REMOVE_HEAD(&sw_FDB_Table.freefdbList.filterDBentry, nextFDB); l2entry_t->asicPos =col_tmp; l2entry_t->auth = auth; l2entry_t->configToAsic = 0; l2entry_t->memberPortMask = portMask; l2entry_t->l2type = l2Type; l2entry_t->nhFlag = nexthp_flag; l2entry_t->SrcBlk = FALSE; memcpy(&l2entry_t->macAddr, macAddr, sizeof(ether_addr_t)); switch(type) { case FDB_TYPE_FWD: l2entry_t->process = FDB_TYPE_FWD; l2entry_t->memberPortMask = portMask; break; case FDB_TYPE_DSTBLK: l2entry_t->process = FDB_TYPE_DSTBLK; l2entry_t->memberPortMask = 0; break; case FDB_TYPE_SRCBLK: l2entry_t->process = FDB_TYPE_SRCBLK; l2entry_t->memberPortMask = 0; break; case FDB_TYPE_TRAPCPU: l2entry_t->process = FDB_TYPE_TRAPCPU; l2entry_t->memberPortMask = portMask; break; default: assert(0); } /*write the SW l2 entry */ if (SLIST_FIRST(&(fdb_t->database[rowIdx]))) { SLIST_FOREACH(tmpL2, &(fdb_t->database[rowIdx]), nextFDB) { if (memcmp(&(tmpL2->macAddr), macAddr, 6)== 0) { if( (tmpL2->auth != auth) || (tmpL2->process != type) || (tmpL2->SrcBlk != SrcBlk) || (tmpL2->memberPortMask != portMask) || (tmpL2->l2type != l2Type) ) { tmpL2->auth = auth; tmpL2->process = type; tmpL2->SrcBlk = SrcBlk; tmpL2->memberPortMask = portMask; tmpL2->l2type = l2Type; /*duplicate entry,avoid memory leak*/ SLIST_INSERT_HEAD(&sw_FDB_Table.freefdbList.filterDBentry, l2entry_t, nextFDB); break; /* tmpL2 ->refCount = 1;*/ } else { /* tmpL2->refCount +=1;*/ /*duplicate entry,avoid memory leak*/ SLIST_INSERT_HEAD(&sw_FDB_Table.freefdbList.filterDBentry, l2entry_t, nextFDB); return SUCCESS; } } else if (tmpL2->nextFDB.sle_next == NULL) { /* l2entry_t ->refCount = 1;*/ tmpL2->nextFDB.sle_next = l2entry_t; l2entry_t->nextFDB.sle_next = NULL; break; } } } else { /* l2entry_t ->refCount = 1;*/ SLIST_INSERT_HEAD(&(fdb_t->database[rowIdx]), l2entry_t, nextFDB); } /* TypeII entry can not exceed RTL8651_L2TBL_COLUMN */ /* if (typeII == RTL8651_L2TBL_COLUMN && l2Type == RTL8651_L2_TYPEII) return RTL_NOFREEBUFFER; */ rtl865x_raiseEvent(EVENT_ADD_FDB, (void *)(l2entry_t)); return SUCCESS; } /* @func int32 | rtl8651_delFilterDatabaseEntry | Remove a filter database entry. @parm uint16 | fid | Filter database entry. @parm ether_addr_t * | macAddr | Pointer to a MAC Address. @rvalue RTL_EINVALIDFID | Filter database ID. @rvalue RTL_NULLMACADDR | The specified MAC address is NULL. @rvalue RTL_EINVALIDINPUT | Invalid input parameter. */ int32 rtl865x_delFilterDatabaseEntry(uint16 l2Type, uint16 fid, ether_addr_t * macAddr) { int32 retval; unsigned long flags; DBG_FDB_PRK("Enter %s mac:%pM @ %d\n",__func__,&macAddr->octet[0],__LINE__); if (fid >= RTL865x_FDB_NUMBER) return RTL_EINVALIDINPUT; if (macAddr == (ether_addr_t *)NULL) return RTL_EINVALIDINPUT; if (sw_FDB_Table.filterDB[fid].valid == 0) return RTL_EINVALIDFID; /* L2 lock */ //rtl_down_interruptible(&l2_sem); spin_lock_irqsave(&fdb_spinlock,flags); retval = _rtl865x_delFilterDatabaseEntry(RTL865x_L2_TYPEII, fid, macAddr); /* L2 unlock */ //rtl_up(&l2_sem); spin_unlock_irqrestore(&fdb_spinlock,flags); return retval; } int32 _rtl865x_delFilterDatabaseEntry(uint16 l2Type, uint16 fid, ether_addr_t * macAddr) { uint32 rowIdx = 0; uint32 colIdx = 0; rtl865x_tblAsicDrv_l2Param_t L2temp, *L2buff ; rtl865x_filterDbTableEntry_t * l2entry_t = NULL; rtl865x_filterDbTable_t *fdb_t = &sw_FDB_Table.filterDB[fid]; int32 found = FALSE; rowIdx = rtl8651_filterDbIndex(macAddr, fid); L2buff = &L2temp; memset(L2buff, 0 , sizeof (rtl865x_tblAsicDrv_l2Param_t)); for(colIdx=0; colIdx<RTL8651_L2TBL_COLUMN; colIdx++) { if ((rtl8651_getAsicL2Table(rowIdx, colIdx, L2buff))==SUCCESS) { /*check whether mac address has been learned to SW or not*/ if (memcmp(&(L2buff->macAddr), macAddr, 6)== 0) { /*the entry has been auto learned*/ found = TRUE; break; } } } /* when a sta from eth0 to wlan0, the layer driver fdb will be deleted, but linux bridge fdb still exist, so delete the asic entry anyway to avoid layer connection issue. eg: first moment .sta is at eth0 second moment:connect sta to wlan0, the layer driver fdb will be deleted, but linux bridge fdb still there third moment:move sta to eth0 again, layer driver fdb won't be created due to linux bridge fdb already exist. */ if (found == TRUE) { rtl8651_delAsicL2Table(rowIdx, colIdx); } SLIST_FOREACH(l2entry_t, &(fdb_t->database[rowIdx]), nextFDB) { if (!memcmp(&l2entry_t->macAddr, macAddr, sizeof(ether_addr_t))) { SLIST_REMOVE( &(fdb_t->database[rowIdx]), l2entry_t, rtl865x_filterDbTableEntry_s, nextFDB ); SLIST_INSERT_HEAD(&sw_FDB_Table.freefdbList.filterDBentry, l2entry_t, nextFDB); rtl865x_raiseEvent(EVENT_DEL_FDB, (void *)(l2entry_t)); break; } } return SUCCESS; } //#ifdef CONFIG_RTL865X_SYNC_L2 int32 rtl865x_arrangeFdbEntry(const unsigned char *timeout_addr, int32 *port) { int32 found = FAILED; ether_addr_t *macAddr; int32 column; rtl865x_tblAsicDrv_l2Param_t fdbEntry; rtl865x_filterDbTableEntry_t l2temp_entry; int32 rowIdx; macAddr = (ether_addr_t *)(timeout_addr); rowIdx = rtl8651_filterDbIndex(macAddr, RTL_LAN_FID); found = rtl865x_Lookup_fdb_entry(RTL_LAN_FID, macAddr, FDB_DYNAMIC, &column, &fdbEntry); if (found != SUCCESS) { if (rtl865x_lookup_FilterDatabaseEntry(RTL_LAN_FID, macAddr, &l2temp_entry) == SUCCESS) { /* printk("\nlook up sw fdb success\n"); printk("\nport mask is %x\n", l2temp_entry.memberPortMask); */ *port = rtl865x_ConvertPortMasktoPortNum(l2temp_entry.memberPortMask); return RTL865X_FDBENTRY_TIMEOUT; } } else { if(fdbEntry.ageSec == 450) { return RTL865X_FDBENTRY_450SEC; } if(fdbEntry.ageSec == 300) { return RTL865X_FDBENTRY_300SEC; } if(fdbEntry.ageSec == 150) { return RTL865X_FDBENTRY_150SEC; } } return FAILED; } void update_hw_l2table(const char *srcName,const unsigned char *addr) { #if defined(CONFIG_RTL_SW_FDB_LEARNING) #if defined(CONFIG_RTL_LAYERED_DRIVER_L3) extern void rtl_delArpByAssociatedMac(const unsigned char *addr); #endif extern void rtl_del_hwnat_l2(const unsigned char *addr); DBG_FDB_PRK("Enter %s srcDev:%s mac:%pM @ %d\n",__func__,srcName,addr,__LINE__); rtl_del_hwnat_l2(addr); #if defined(CONFIG_RTL_LAYERED_DRIVER_L3) rtl_delArpByAssociatedMac(addr); #endif #else //int32 found = FAILED; ether_addr_t *macAddr; int32 ret = 0; int fid; int32 column; rtl865x_tblAsicDrv_l2Param_t fdbEntry; DBG_FDB_PRK("Enter %s srcDev:%s mac:%pM @ %d\n",__func__,srcName,addr,__LINE__); macAddr = (ether_addr_t *)(addr); if (memcmp(srcName, RTL_WLAN_NAME, 4) ==0) { for(fid=0;fid<RTL865X_FDB_NUMBER;fid++) { #ifndef CONFIG_RTL_IVL_SUPPORT if(fid != RTL_LAN_FID) continue; #endif if (rtl865x_Lookup_fdb_entry(fid, macAddr, FDB_DYNAMIC, &column,&fdbEntry) == SUCCESS) { if((fdbEntry.memberPortMask & RTL8651_PHYSICALPORTMASK)!=0) { ret = rtl865x_delFilterDatabaseEntry(RTL865x_L2_TYPEII, fid, macAddr); } } } } #endif } int32 rtl865x_addFDBEntry(const unsigned char *addr) { int32 found = FAILED; ether_addr_t *macAddr; int32 ret=FAILED; int8 port_num = -1; int32 column; rtl865x_tblAsicDrv_l2Param_t fdbEntry; rtl865x_filterDbTableEntry_t l2temp_entry; if (addr == NULL) return RTL_EINVALIDINPUT; macAddr = (ether_addr_t *)(addr); found = rtl865x_Lookup_fdb_entry(RTL_LAN_FID, macAddr, FDB_DYNAMIC, &column, &fdbEntry); if (found == SUCCESS ) { port_num = rtl865x_ConvertPortMasktoPortNum(fdbEntry.memberPortMask); /* printk("\nbefore rtl865x_lookup_FilterDatabaseEntry, port is %d\n", port_num); */ if (rtl865x_lookup_FilterDatabaseEntry(fdbEntry.fid, macAddr, &l2temp_entry) != SUCCESS) { l2temp_entry.l2type = (fdbEntry.nhFlag==0)?RTL865x_L2_TYPEI: RTL865x_L2_TYPEII; l2temp_entry.process = FDB_TYPE_FWD; l2temp_entry.memberPortMask = fdbEntry.memberPortMask; /* l2temp_entry.auth = TRUE;*/ /* l2temp_entry.SrcBlk = FALSE;*/ memcpy(&(l2temp_entry.macAddr), macAddr, sizeof(ether_addr_t)); ret =_rtl865x_addFilterDatabaseEntry( l2temp_entry.l2type, fdbEntry.fid, &(l2temp_entry.macAddr), l2temp_entry.process, l2temp_entry.memberPortMask, FALSE, FALSE); } } else { /*add */ } return ret; } int32 rtl865x_delLanFDBEntry(uint16 l2Type, const unsigned char *addr) { int32 ret=FAILED; ether_addr_t *macAddr; DBG_FDB_PRK("Enter %s mac:%pM @ %d\n",__func__,addr,__LINE__); if (addr == NULL) { return RTL_EINVALIDINPUT; } macAddr = (ether_addr_t *)(addr); ret =_rtl865x_delFilterDatabaseEntry( l2Type, RTL_LAN_FID, macAddr); return ret; } int32 rtl865x_ConvertPortMasktoPortNum(int32 portmask) { int32 i = 0; for (i = PHY0; i < EXT3; i++) { if(((portmask >> i) & 0x01) == 1) { return i; } } return FAILED; } #ifdef CONFIG_RTL865X_LANPORT_RESTRICTION int32 _rtl865x_addAuthFilterDatabaseEntry(uint16 l2Type, uint16 fid, ether_addr_t * macAddr, uint32 type, uint32 portMask, uint8 auth, uint8 SrcBlk) { uint32 rowIdx = 0; uint32 colIdx = 0; uint32 col_num = 0; uint32 col_tmp = 0; uint16 tmp_age = 0xffff; int32 found = FALSE; int32 flag = FALSE; int32 nexthp_flag = FALSE; int32 isStatic = FALSE; int32 toCpu = FALSE; rtl865x_filterDbTableEntry_t * l2entry_t = NULL; rtl865x_filterDbTableEntry_t *tmpL2; rtl865x_filterDbTable_t *fdb_t = &sw_FDB_Table.filterDB[fid]; rtl865x_tblAsicDrv_l2Param_t l2entry_tmp,*L2buff; int32 retval = 0; int32 overwite_blk_flag = FALSE; L2buff = &l2entry_tmp; memset(L2buff,0,sizeof(rtl865x_tblAsicDrv_l2Param_t)); rowIdx = rtl8651_filterDbIndex(macAddr, fid); for(colIdx=0; colIdx<RTL8651_L2TBL_COLUMN; colIdx++) { retval = rtl8651_getAsicL2Table(rowIdx, colIdx, L2buff); if ((retval)==SUCCESS) { /*check whether mac address has been learned to HW or not*/ if (memcmp(&(L2buff->macAddr), macAddr, 6)== 0) { /*the entry has been auto learned*/ if (L2buff->memberPortMask==portMask) { /*the entry has been auto learned */ if ((L2buff->srcBlk == TRUE) && (L2buff->auth == FALSE)) { overwite_blk_flag = TRUE; } found = TRUE; col_tmp = colIdx; } else /*portmask is different , it should be overwrited*/ { found = FALSE; flag = TRUE; } break; } /*no matched entry, try get minimum aging time L2 Asic entry*/ if (tmp_age> L2buff->ageSec) { tmp_age = L2buff->ageSec; col_num = colIdx; } } else { /*there is still empty l2 asic entry*/ flag = TRUE; break; } } switch(l2Type) { case RTL865x_L2_TYPEI: nexthp_flag = FALSE;isStatic = FALSE; break; case RTL865x_L2_TYPEII: nexthp_flag = TRUE; isStatic = TRUE; break; case RTL865x_L2_TYPEIII: nexthp_flag = FALSE;isStatic = TRUE; break; default: assert(0); } switch(type) { case FDB_TYPE_FWD: toCpu = FALSE; break; case FDB_TYPE_DSTBLK: toCpu = FALSE; break; case FDB_TYPE_SRCBLK: toCpu = FALSE; break; case FDB_TYPE_TRAPCPU: toCpu = TRUE; break; default: assert(0); } if (found == FALSE) { /*no empty entry, overwrite the biggest aging time asic l2 entry*/ if(flag == FALSE) { /*delete the biggest aging time software entry*/ rtl8651_getAsicL2Table(rowIdx, col_num, L2buff); _rtl865x_removeFilterDatabaseEntry(fid, &(L2buff->macAddr), rowIdx); /*overwrite asic entry*/ rtl8651_setAsicL2Table_Patch( rowIdx, col_num, macAddr, toCpu, SrcBlk, portMask, arpAgingTime, isStatic, nexthp_flag, fid, auth); col_tmp = col_num; } /*portmask is different , so it should overwrite the original asic entry. Or there is empty entry, set it to asic*/ else if(flag == TRUE) { rtl8651_setAsicL2Table_Patch( rowIdx, colIdx, macAddr, toCpu, SrcBlk, portMask, arpAgingTime, isStatic, nexthp_flag, fid, auth); col_tmp = colIdx; } } /*find the same asic entry, should update the aging time*/ else { rtl8651_setAsicL2Table_Patch( rowIdx, col_tmp, macAddr, toCpu, SrcBlk, portMask, arpAgingTime, isStatic, nexthp_flag, fid, auth); } /* colIdx=0; tmpL2 = SLIST_FIRST(&(fdb_t->database[rowIdx])); tmpL2->nextFDB.sle_next = NULL; for(colIdx=0; colIdx<RTL8651_L2TBL_COLUMN; colIdx++) { if ((rtl8651_getAsicL2Table(rowIdx, colIdx, L2buff))==SUCCESS) { l2entry_t->asicPos =colIdx; l2entry_t->auth = L2buff->auth; l2entry_t->configToAsic = 0; l2entry_t->memberPortMask = L2buff->memberPortMask; l2entry_t->SrcBlk = L2buff->srcBlk; memcpy(&l2entry_t->macAddr, &L2buff->macAddr, sizeof(ether_addr_t)); tmpL2->nextFDB.sle_next = l2entry_t; l2entry_t->nextFDB.sle_next = NULL; tmpL2 = l2entry_t; } } */ if (SLIST_FIRST(&sw_FDB_Table.freefdbList.filterDBentry) == NULL) return RTL_ENOFREEBUFFER; /*config the SW l2 entry */ l2entry_t = SLIST_FIRST(&sw_FDB_Table.freefdbList.filterDBentry); SLIST_REMOVE_HEAD(&sw_FDB_Table.freefdbList.filterDBentry, nextFDB); l2entry_t->asicPos =col_tmp; l2entry_t->auth = auth; l2entry_t->configToAsic = 0; l2entry_t->memberPortMask = portMask; l2entry_t->l2type = l2Type; l2entry_t->nhFlag = nexthp_flag; l2entry_t->SrcBlk = SrcBlk; memcpy(&l2entry_t->macAddr, macAddr, sizeof(ether_addr_t)); switch(type) { case FDB_TYPE_FWD: l2entry_t->process = FDB_TYPE_FWD; l2entry_t->memberPortMask = portMask; break; case FDB_TYPE_DSTBLK: l2entry_t->process = FDB_TYPE_DSTBLK; l2entry_t->memberPortMask = 0; break; case FDB_TYPE_SRCBLK: l2entry_t->process = FDB_TYPE_SRCBLK; l2entry_t->memberPortMask = 0; break; case FDB_TYPE_TRAPCPU: l2entry_t->process = FDB_TYPE_TRAPCPU; l2entry_t->memberPortMask = portMask; break; default: assert(0); } /*write the SW l2 entry */ check_swfdb: if (SLIST_FIRST(&(fdb_t->database[rowIdx]))) { SLIST_FOREACH(tmpL2, &(fdb_t->database[rowIdx]), nextFDB) { if (memcmp(&(tmpL2->macAddr), macAddr, 6)== 0) { if( (tmpL2->auth != auth) || (tmpL2->process != type) || (tmpL2->SrcBlk != SrcBlk) || (tmpL2->memberPortMask != portMask) || (tmpL2->l2type != l2Type) ) { tmpL2->auth = auth; tmpL2->process = type; tmpL2->SrcBlk = SrcBlk; tmpL2->memberPortMask = portMask; tmpL2->l2type = l2Type; /* tmpL2 ->refCount = 0;*/ break; } else { goto out; } } /*try to check whether there is timeout sw fdb entry*/ else if(tmpL2->asicPos == col_tmp) { /* if ((tmpL2->SrcBlk == TRUE) && (tmpL2->auth == FALSE))*/ { _rtl865x_removeFilterDatabaseEntry(fid, &(tmpL2->macAddr), rowIdx); goto check_swfdb; } } else if (tmpL2->nextFDB.sle_next == NULL) { /* l2entry_t->refCount = 0;*/ tmpL2->nextFDB.sle_next = l2entry_t; l2entry_t->nextFDB.sle_next = NULL; break; } } } else { /* l2entry_t ->refCount = 0;*/ SLIST_INSERT_HEAD(&(fdb_t->database[rowIdx]), l2entry_t, nextFDB); } /* TypeII entry can not exceed RTL8651_L2TBL_COLUMN */ /* if (typeII == RTL8651_L2TBL_COLUMN && l2Type == RTL8651_L2_TYPEII) return RTL_NOFREEBUFFER; */ if (!((l2entry_t->SrcBlk == TRUE) && (l2entry_t->auth == FALSE))) { rtl865x_raiseEvent(EVENT_ADD_FDB, (void *)(l2entry_t)); } if (overwite_blk_flag != TRUE) { rtl865x_raiseEvent(EVENT_ADD_AUTHED_FDB, (void *)(l2entry_t)); } else { /* printk("\nover blk entry, no raise event\n");*/ } out: return SUCCESS; } int32 _rtl865x_addAuthSWl2Entry(uint16 l2Type, uint16 fid, ether_addr_t * macAddr, uint32 type, int32 port, uint8 auth, uint8 SrcBlk) { uint32 rowIdx = 0; int32 nexthp_flag = FALSE; rtl865x_filterDbTableEntry_t * l2entry_t = NULL; rtl865x_filterDbTableEntry_t *tmpL2; rtl865x_filterDbTable_t *fdb_t = &sw_FDB_Table.filterDB[fid]; rtl865x_tblAsicDrv_l2Param_t l2entry_tmp,*L2buff; int32 overwite_blk_flag = FALSE; L2buff = &l2entry_tmp; memset(L2buff,0,sizeof(rtl865x_tblAsicDrv_l2Param_t)); rowIdx = rtl8651_filterDbIndex(macAddr, fid); if (SLIST_FIRST(&sw_FDB_Table.freefdbList.filterDBentry) == NULL) return RTL_ENOFREEBUFFER; /*config the SW l2 entry */ l2entry_t = SLIST_FIRST(&sw_FDB_Table.freefdbList.filterDBentry); SLIST_REMOVE_HEAD(&sw_FDB_Table.freefdbList.filterDBentry, nextFDB); l2entry_t->auth = auth; l2entry_t->configToAsic = 0; l2entry_t->memberPortMask = 1<<port; l2entry_t->l2type = l2Type; l2entry_t->nhFlag = nexthp_flag; l2entry_t->SrcBlk = SrcBlk; memcpy(&l2entry_t->macAddr, macAddr, sizeof(ether_addr_t)); switch(type) { case FDB_TYPE_FWD: l2entry_t->process = FDB_TYPE_FWD; l2entry_t->memberPortMask = 1<<port; break; case FDB_TYPE_DSTBLK: l2entry_t->process = FDB_TYPE_DSTBLK; l2entry_t->memberPortMask = 0; break; case FDB_TYPE_SRCBLK: l2entry_t->process = FDB_TYPE_SRCBLK; l2entry_t->memberPortMask = 0; break; case FDB_TYPE_TRAPCPU: l2entry_t->process = FDB_TYPE_TRAPCPU; l2entry_t->memberPortMask = 1<<port; break; default: assert(0); } /*write the SW l2 entry */ if (SLIST_FIRST(&(fdb_t->database[rowIdx]))) { SLIST_FOREACH(tmpL2, &(fdb_t->database[rowIdx]), nextFDB) { if (memcmp(&(tmpL2->macAddr), macAddr, 6)== 0) { if( (tmpL2->auth != auth) || (tmpL2->process != type) || (tmpL2->SrcBlk != SrcBlk) || (tmpL2->memberPortMask != 1<<port) || (tmpL2->l2type != l2Type) ) { if ( ( tmpL2->auth == FALSE) && (tmpL2->SrcBlk == TRUE)) { overwite_blk_flag = TRUE; } tmpL2->auth = auth; tmpL2->process = type; tmpL2->SrcBlk = SrcBlk; tmpL2->memberPortMask = 1<<port; tmpL2->l2type = l2Type; /* tmpL2 ->refCount = 0;*/ break; } else { SLIST_INSERT_HEAD(&sw_FDB_Table.freefdbList.filterDBentry, l2entry_t, nextFDB); goto out; } } else if (tmpL2->nextFDB.sle_next == NULL) { /* l2entry_t->refCount = 0;*/ tmpL2->nextFDB.sle_next = l2entry_t; l2entry_t->nextFDB.sle_next = NULL; break; } } } else { /* l2entry_t ->refCount = 0;*/ SLIST_INSERT_HEAD(&(fdb_t->database[rowIdx]), l2entry_t, nextFDB); } /* TypeII entry can not exceed RTL8651_L2TBL_COLUMN */ /* if (typeII == RTL8651_L2TBL_COLUMN && l2Type == RTL8651_L2_TYPEII) return RTL_NOFREEBUFFER; */ rtl865x_raiseEvent(EVENT_ADD_FDB, (void *)(l2entry_t)); if (overwite_blk_flag != TRUE) { rtl865x_raiseEvent(EVENT_ADD_AUTHED_FDB, (void *)(l2entry_t)); } else { /* printk("\nover blk entry, no raise event\n");*/ } out: return SUCCESS; } int32 rtl865x_addAuthFilterDatabaseEntryExtension( uint16 fid, rtl865x_filterDbTableEntry_t * L2entry) { int32 retval; unsigned long flags; if (L2entry->process != FDB_TYPE_FWD && L2entry->process != FDB_TYPE_SRCBLK && L2entry->process != FDB_TYPE_TRAPCPU) return RTL_EINVALIDINPUT; /* Invalid parameter */ if (fid >= RTL865x_FDB_NUMBER) return RTL_EINVALIDINPUT; if (&(L2entry->macAddr) == (ether_addr_t *)NULL) return RTL_EINVALIDINPUT; if (sw_FDB_Table.filterDB[fid].valid == 0) return RTL_EINVALIDFID; /*l2 lock*/ //rtl_down_interruptible(&l2_sem); spin_lock_irqsave(&fdb_spinlock,flags); retval = _rtl865x_addAuthFilterDatabaseEntry( L2entry->l2type, fid, &(L2entry->macAddr), L2entry->process, L2entry->memberPortMask, L2entry->auth, L2entry->SrcBlk); /*l2 unlock*/ //rtl_up(&l2_sem); spin_unlock_irqrestore(&fdb_spinlock,flags); return retval; } /* @func int32 | rtl8651_delFilterDatabaseEntry | Remove a filter database entry. @parm uint16 | fid | Filter database entry. @parm ether_addr_t * | macAddr | Pointer to a MAC Address. @rvalue RTL_EINVALIDFID | Filter database ID. @rvalue RTL_NULLMACADDR | The specified MAC address is NULL. @rvalue RTL_EINVALIDINPUT | Invalid input parameter. */ int32 rtl865x_delAuthFilterDatabaseEntry(uint16 l2Type, uint16 fid, ether_addr_t * macAddr) { int32 retval; unsigned long flags; if (fid >= RTL865x_FDB_NUMBER) return RTL_EINVALIDINPUT; if (macAddr == (ether_addr_t *)NULL) return RTL_EINVALIDINPUT; if (sw_FDB_Table.filterDB[fid].valid == 0) return RTL_EINVALIDFID; /* L2 lock */ //rtl_down_interruptible(&l2_sem); spin_lock_irqsave(&fdb_spinlock,flags); retval = _rtl865x_delAuthFilterDatabaseEntry(RTL865x_L2_TYPEII, fid, macAddr); /* L2 unlock */ //rtl_up(&l2_sem); spin_unlock_irqrestore(&fdb_spinlock,flags); return retval; } int32 _rtl865x_delAuthFilterDatabaseEntry(uint16 l2Type, uint16 fid, ether_addr_t * macAddr) { uint32 rowIdx = 0; uint32 colIdx = 0; rtl865x_tblAsicDrv_l2Param_t L2temp, *L2buff ; rtl865x_filterDbTableEntry_t * l2entry_t = NULL; rtl865x_filterDbTable_t *fdb_t = &sw_FDB_Table.filterDB[fid]; int32 found = FALSE; rowIdx = rtl8651_filterDbIndex(macAddr, fid); L2buff = &L2temp; memset(L2buff, 0 , sizeof (rtl865x_tblAsicDrv_l2Param_t)); for(colIdx=0; colIdx<RTL8651_L2TBL_COLUMN; colIdx++) { if ((rtl8651_getAsicL2Table(rowIdx, colIdx, L2buff))==SUCCESS) { /*check whether mac address has been learned to SW or not*/ if (memcmp(&(L2buff->macAddr), macAddr, 6)== 0) { /*the entry has been auto learned*/ found = TRUE; break; } } } SLIST_FOREACH(l2entry_t, &(fdb_t->database[rowIdx]), nextFDB) if (!memcmp(&l2entry_t->macAddr, macAddr, sizeof(ether_addr_t))) { /* l2entry_t->refCount -= 1;*/ /* if (l2entry_t->refCount == 0)*/ { SLIST_REMOVE( &(fdb_t->database[rowIdx]), l2entry_t, rtl865x_filterDbTableEntry_s, nextFDB ); SLIST_INSERT_HEAD(&sw_FDB_Table.freefdbList.filterDBentry, l2entry_t, nextFDB); if (found == TRUE) { rtl8651_delAsicL2Table(rowIdx, colIdx); } rtl865x_raiseEvent(EVENT_DEL_FDB, (void *)(l2entry_t)); /* printk("raise event EVENT_DEL_AUTHED_FDB : %x", l2entry_t->memberPortMask);*/ rtl865x_raiseEvent(EVENT_DEL_AUTHED_FDB, (void *)(l2entry_t)); } break; } return SUCCESS; } int32 rtl865x_check_authfdbentry_Byport(int32 port_num, const unsigned char *macAddr) { uint32 rowIdx; int32 retval = FAILED; rtl865x_tblAsicDrv_l2Param_t *L2buff, l2temp; rtl865x_filterDbTable_t *fdb_t = &sw_FDB_Table.filterDB[0]; rtl865x_filterDbTableEntry_t * l2entry_t ; L2buff = &l2temp; memset(L2buff, 0, sizeof(rtl865x_tblAsicDrv_l2Param_t)); for(rowIdx=0; rowIdx<RTL8651_L2TBL_ROW; rowIdx++) { if (SLIST_FIRST(&(fdb_t->database[rowIdx]))) { SLIST_FOREACH(l2entry_t, &(fdb_t->database[rowIdx]), nextFDB) { if ((l2entry_t ->auth == FALSE) && (l2entry_t->SrcBlk == TRUE)) { memcpy((ether_addr_t *)macAddr, &(l2entry_t->macAddr), sizeof(ether_addr_t)); #if 0 printk("\nfind block entry, rowIdx is %d, address2 is %x %x %x %x %x %x\n", rowIdx, ((ether_addr_t *)macAddr)->octet[0], ((ether_addr_t *)macAddr)->octet[1], ((ether_addr_t *)macAddr)->octet[2], ((ether_addr_t *)macAddr)->octet[3], ((ether_addr_t *)macAddr)->octet[4], ((ether_addr_t *)macAddr)->octet[5]); #endif retval = SUCCESS; break; } } } } return retval; } int32 rtl865x_addAuthFDBEntry(const unsigned char *addr, int32 auth, int32 port) { int32 found = FAILED; ether_addr_t *macAddr; int32 ret=FAILED; int8 port_num = -1; int32 column; rtl865x_tblAsicDrv_l2Param_t fdbEntry; rtl865x_filterDbTableEntry_t l2temp_entry; int32 srcblk; if (addr == NULL) return RTL_EINVALIDINPUT; macAddr = (ether_addr_t *)(addr); found = rtl865x_Lookup_fdb_entry(RTL_LAN_FID, macAddr, FDB_DYNAMIC, &column, &fdbEntry); if (auth == TRUE) srcblk = FALSE; else srcblk = TRUE; if (found == SUCCESS ) { port_num = rtl865x_ConvertPortMasktoPortNum(fdbEntry.memberPortMask); /* printk("\nbefore rtl865x_lookup_FilterDatabaseEntry, port is %d, auth is %d\n", port_num, auth); */ if (rtl865x_lookup_FilterDatabaseEntry(fdbEntry.fid, macAddr, &l2temp_entry) != SUCCESS) { l2temp_entry.l2type = (fdbEntry.nhFlag==0)?RTL865x_L2_TYPEI: RTL865x_L2_TYPEII; l2temp_entry.process = FDB_TYPE_FWD; l2temp_entry.memberPortMask = fdbEntry.memberPortMask; /* l2temp_entry.auth = TRUE;*/ /* l2temp_entry.SrcBlk = FALSE;*/ memcpy(&(l2temp_entry.macAddr), macAddr, sizeof(ether_addr_t)); ret =_rtl865x_addAuthFilterDatabaseEntry( l2temp_entry.l2type, fdbEntry.fid, &(l2temp_entry.macAddr), l2temp_entry.process, l2temp_entry.memberPortMask, auth, srcblk); } #if 1 else { if ((l2temp_entry.auth == FALSE) && (l2temp_entry.SrcBlk == TRUE)) { l2temp_entry.l2type = (fdbEntry.nhFlag==0)?RTL865x_L2_TYPEI: RTL865x_L2_TYPEII; l2temp_entry.process = FDB_TYPE_FWD; l2temp_entry.memberPortMask = fdbEntry.memberPortMask; memcpy(&(l2temp_entry.macAddr), macAddr, sizeof(ether_addr_t)); ret =_rtl865x_addAuthFilterDatabaseEntry( l2temp_entry.l2type, fdbEntry.fid, &(l2temp_entry.macAddr), l2temp_entry.process, l2temp_entry.memberPortMask, auth, srcblk); } } #endif } else { /*just add sw l2 table */ l2temp_entry.l2type = RTL865x_L2_TYPEII; l2temp_entry.process = FDB_TYPE_FWD; /* l2temp_entry.auth = TRUE;*/ /* l2temp_entry.SrcBlk = FALSE;*/ memcpy(&(l2temp_entry.macAddr), macAddr, sizeof(ether_addr_t)); ret =_rtl865x_addAuthSWl2Entry( l2temp_entry.l2type, 0, &(l2temp_entry.macAddr), l2temp_entry.process, 1<<port, auth, srcblk); } return ret; } int32 rtl865x_delAuthLanFDBEntry(uint16 l2Type, const unsigned char *addr) { int32 ret=FAILED; ether_addr_t *macAddr; if (addr == NULL) { return RTL_EINVALIDINPUT; } macAddr = (ether_addr_t *)(addr); ret =_rtl865x_delAuthFilterDatabaseEntry( l2Type, RTL_LAN_FID, macAddr); return ret; } int32 rtl865x_BlkCheck(const unsigned char *addr) { ether_addr_t *macAddr; rtl865x_filterDbTableEntry_t l2temp_entry; if (addr == NULL) return RTL_EINVALIDINPUT; macAddr = (ether_addr_t *)(addr); #if 0 printk("\nin rtl865x_BlkCheck, address2 is %x %x %x %x %x %x\n", ((ether_addr_t *)macAddr)->octet[0], ((ether_addr_t *)macAddr)->octet[1], ((ether_addr_t *)macAddr)->octet[2], ((ether_addr_t *)macAddr)->octet[3], ((ether_addr_t *)macAddr)->octet[4], ((ether_addr_t *)macAddr)->octet[5]); #endif if(rtl865x_lookup_FilterDatabaseEntry(RTL_LAN_FID, macAddr, &l2temp_entry) == SUCCESS) { if (l2temp_entry.SrcBlk == TRUE) { return TRUE; } } return FALSE; } /* @func int32 | rtl865x_enableLanPortNumRestrict | enable max guest number restrict feature. @parm uint8 | isEnable | enable feature or not. @rvalue SUCCESS | enable feature. @comm This API may change wan & lan port 802.1x mac base ablity */ int32 rtl865x_enableLanPortNumRestrict(uint8 isEnable) { if( isEnable == TRUE) { rtl8651_setAsic802D1xMacBaseDirection(Dot1xMAC_OPDIR_IN); rtl8651_setAsicGuestVlanProcessControl(EN_8021X_TOCPU); } else { rtl8651_setAsic802D1xMacBaseDirection(Dot1xMAC_OPDIR_BOTH); rtl8651_setAsicGuestVlanProcessControl( EN_8021X_DROP); } return SUCCESS; } /* @func int32 | rtl865x_setRestrictPortNum | set per port max guest number for max host restrict feature. @parm int32 | port | set port number . @parm int8 | isEnable | enable max host restrict feature or not @parm int32 | number | set max guest number @rvalue SUCCESS | set number for this port ok. */ int32 rtl865x_setRestrictPortNum(int32 port, uint8 isEnable, int32 number) { int32 i = 0; if(isEnable==TRUE) { for (i = PHY0; i < EXT3; i++) { if( i == port) { rtl8651_setAsic802D1xMacBaseAbility(i, TRUE); break; } } } if(isEnable==FALSE) { for (i = PHY0; i < EXT3; i++) { if( i == port) { rtl8651_setAsic802D1xMacBaseAbility(i, FALSE); break; } } } return SUCCESS; } void rtl865x_new_AuthFDB(const unsigned char *addr) { extern int32 lan_restrict_CheckStatusByport(int32 port); ether_addr_t *macAddr; int32 column; rtl865x_tblAsicDrv_l2Param_t fdbEntry; int32 port_num = -1; macAddr = (ether_addr_t *)(addr); if (rtl865x_Lookup_fdb_entry(RTL_LAN_FID, macAddr, FDB_DYNAMIC, &column,&fdbEntry) == SUCCESS) { port_num = rtl865x_ConvertPortMasktoPortNum(fdbEntry.memberPortMask); /*function opened, and should be authed*/ if (lan_restrict_CheckStatusByport(port_num) == TRUE) { rtl865x_addAuthFDBEntry(addr, TRUE, port_num); } /*function opened, and set it to block*/ else if(lan_restrict_CheckStatusByport(port_num) == FALSE) { rtl865x_addAuthFDBEntry(addr, FALSE, port_num); } /*function not open , no need to be authed*/ else if(lan_restrict_CheckStatusByport(port_num) == FAILED) { rtl865x_addFDBEntry(addr); } } } #endif //#endif #ifdef CONFIG_RTL_LINKCHG_PROCESS #if defined(CONFIG_RTL_LAYERED_DRIVER_L3) extern void rtl_delArpByAssociatedMac(const unsigned char *addr); #endif int32 _rtl865x_ClearFDBEntryByPort(int32 port_num) { int i, j; rtl865x_tblAsicDrv_l2Param_t l2entry_tmp,*L2buff; L2buff = &l2entry_tmp; for (i = 0; i < RTL8651_L2TBL_ROW; i++) for (j = 0; j < RTL8651_L2TBL_COLUMN; j++) { if ((rtl8651_getAsicL2Table(i, j, L2buff))!=SUCCESS) continue; if ((rtl865x_ConvertPortMasktoPortNum(L2buff->memberPortMask)) != port_num) continue; #if defined(CONFIG_RTL_SW_FDB_LEARNING) if(!memcmp(&L2buff->macAddr.octet[0],&cpu_mac.octet[0],ETH_ALEN) || (L2buff->macAddr.octet[0]&L2buff->macAddr.octet[1]&L2buff->macAddr.octet[2]&L2buff->macAddr.octet[3]&L2buff->macAddr.octet[4]&L2buff->macAddr.octet[5]) == 0xff ) continue; #else if (L2buff->isStatic == TRUE) continue; #endif rtl8651_delAsicL2Table(i, j); _rtl865x_removeFilterDatabaseEntry(RTL_LAN_FID, &(L2buff->macAddr), i); #if defined(CONFIG_RTL_LAYERED_DRIVER_L3) rtl_delArpByAssociatedMac((unsigned char *)&L2buff->macAddr.octet[0]); #endif } return SUCCESS; } int32 rtl865x_LinkChange_Process(void) { uint32 i, status; /* Check each port. */ for ( i = 0; i < RTL8651_MAC_NUMBER; i++ ) { #ifdef CONFIG_PTMWAN if (RTL_PTMWANPORT_MASK == (1 << i)) // skip PTM port continue; #endif /* Read Port Status Register to know the port is link-up or link-down. */ status = READ_MEM32( PSRP0 + i * 4 ); if ( ( status & PortStatusLinkUp ) == FALSE ) { /* Link is down. */ rtl8651_setAsicEthernetLinkStatus( i, FALSE ); _rtl865x_ClearFDBEntryByPort(i); #ifdef CONFIG_RTK_REMOTE_ETH_PHY /*For 8685SB Remote PHY (DSL+EWAN), we don't need to scan port status for every port. We only allow port3 as the EWAN port. Hence we just need to send the event for only one port*/ if(RTL_LANPORT_MASK_2_DEFAULT == (1 << i)) remotePhy_evt_notify(EWAN_LINK_DOWN, NULL); #endif } else { /* Link is up. */ rtl8651_setAsicEthernetLinkStatus( i, TRUE ); #ifdef CONFIG_RTK_REMOTE_ETH_PHY if(RTL_LANPORT_MASK_2_DEFAULT == (1 << i)) remotePhy_evt_notify(EWAN_LINK_UP, NULL); #endif } } return SUCCESS; } #endif #ifdef CONFIG_RTL_PROC_DEBUG int32 rtl865x_sw_l2_proc_read( char *page, char **start, off_t off, int count, int *eof, void *data ) { int32 index = 0; int32 index1 = 0; uint32 port, m=0; rtl865x_filterDbTable_t *filterDbPtr; rtl865x_filterDbTableEntry_t *tempFilterDbPtr; printk("%s\n", "sw l2 table:"); for(index=0,filterDbPtr=&sw_FDB_Table.filterDB[0]; index<RTL865x_FDB_NUMBER; index++,filterDbPtr++) { for(index1=0; index1<RTL8651_L2TBL_ROW; index1++) if (SLIST_FIRST(&(filterDbPtr->database[index1]))) { SLIST_FOREACH(tempFilterDbPtr, &(filterDbPtr->database[index1]), nextFDB) { printk("%4d.[%3d,%d] %02x:%02x:%02x:%02x:%02x:%02x FID:%x mbr(",m, index1, tempFilterDbPtr->asicPos, tempFilterDbPtr->macAddr.octet[0], tempFilterDbPtr->macAddr.octet[1], tempFilterDbPtr->macAddr.octet[2], tempFilterDbPtr->macAddr.octet[3], tempFilterDbPtr->macAddr.octet[4], tempFilterDbPtr->macAddr.octet[5], index); m++; for (port = 0 ; port < RTL8651_PORT_NUMBER + rtl8651_totalExtPortNum ; port ++) { if (tempFilterDbPtr->memberPortMask & (1<<port)) { printk("%d ", port); } } printk(")"); switch(tempFilterDbPtr->process) { case FDB_TYPE_FWD: printk("%s ", "FWD"); break; case FDB_TYPE_DSTBLK: printk("%s ", "DSTBLK"); break; case FDB_TYPE_SRCBLK: printk("%s ", "SRCBLK"); break; case FDB_TYPE_TRAPCPU: printk("%s ", "CPU"); break; } // printk("%s %s %s",tempFilterDbPtr->process?"CPU":"FWD", tempFilterDbPtr->l2type?"STA":"DYN", tempFilterDbPtr->SrcBlk?"BLK":""); printk("%s ", (tempFilterDbPtr->l2type != RTL865x_L2_TYPEI)?"STA":"DYN"); printk("%s ", (tempFilterDbPtr->l2type == RTL865x_L2_TYPEII)?"NH":""); printk("%s ", tempFilterDbPtr->SrcBlk?"BLK":""); if (tempFilterDbPtr->auth) { printk("AUTH:%d ",tempFilterDbPtr->auth); } /* printk("reference count:%d",tempFilterDbPtr->refCount);*/ printk("\n"); } } } #if defined(CONFIG_RTL_XDSL_WIFI_HWACCELERATION) { rtk_xdsl_hwnat_mbssid_mac_mapping_t *mapping; int i; printk("\n --- MBSSID mapping ---\n"); for(i = 0; i < RTK_XDSL_HWNAT_MBSSID_MAC_MAPPING_BUCKET_SIZE; i++) { list_for_each_entry(mapping,&rtk_xdsl_hwnat_mbssid_mac_mappingListHead[i], list) { printk("mac:%pM dev:%s\n",mapping->macAddr.octet,mapping->dev->name); } } } #endif return 0; } int32 rtl865x_sw_l2_proc_write( struct file *filp, const char *buff,unsigned long len, void *data ) { return len; } #endif #if defined(CONFIG_RTL_SW_FDB_LEARNING) #define RTL_HW_L2TBL_SIZE 2048 #define RTL_HWNAT_L2TBL_BUCKET_SIZE RTL_HW_L2TBL_SIZE>>5 #define RTL_HWNAT_L2_TIMEOUT_SECS 60*2 //HWNAT driver L2 data timeout in seconds #define RTL_HWNAT_L2_TICK_SECS 5 //Polling period in seconds unsigned int rtl_hwnat_l2Tbl_chkSet[RTL_HWNAT_L2TBL_BUCKET_SIZE]; struct list_head rtl_hwnat_l2Tbl_chkListHead[RTL_HWNAT_L2TBL_BUCKET_SIZE]; rtl_hwnat_l2Tbl_data_t *rtl_hwnat_l2Tbl[RTL_HW_L2TBL_SIZE]; struct net_bridge_fdb_entry *rtl865x_fdb_lookup_by_mac(ether_addr_t *macAddr) { struct net_device *dev = NULL; struct net_bridge *br = NULL; struct net_bridge_fdb_entry *fdb; //rtnl_lock(); for_each_netdev(&init_net, dev) { if(dev && !(dev->flags & IFF_LOOPBACK) && (dev->flags & IFF_UP) && !strncmp(dev->name,ALIASNAME_BR,2)) { br = netdev_priv(dev); if(br) { int i; struct net_bridge_fdb_entry *f; struct hlist_node *n; //printk("[TRACE] br:%s @ %s %d\n",br->dev->name,__func__,__LINE__); for (i = 0; i < BR_HASH_SIZE; i++) { hlist_for_each_entry_safe(f, n, &br->hash[i], hlist) { //printk("[TRACE] fdb mac:%pM is_local:%d@ %s %d\n",&f->addr.addr[0],f->is_local,__func__,__LINE__); if(!f->is_local && !memcmp(&macAddr->octet[0],&f->addr.addr[0],ETH_ALEN)) { // printk("[TRACE] Found fdb with mac:%pM is_local:%d@ %s %d\n",&f->addr.addr[0],f->is_local,__func__,__LINE__); fdb = f; goto out; } } } } } } out: //rtnl_unlock(); return fdb; } extern int brgScDelete(unsigned char *mac); __IRAM_FWD int rtl865x_smac_learning(void *mac, struct net_device *idev, int vid, int spa) { int rowIdx=0,colIdx,cadCol = -1,ret,asicIdx; int fid = 0,found = 0; ether_addr_t *macAddr = (ether_addr_t *)mac; rtl865x_tblAsicDrv_l2Param_t l2; unsigned long flags; #if defined(CONFIG_RTL_XDSL_WIFI_HWACCELERATION) rtk_xdsl_hwnat_mbssid_mac_mapping_t *mapping, *tmp, *n; int needMapping = 0; #endif DBG_FDB_PRK("Try to learning source mac:%pM vid:%d spa:%d dev:%s @ %s %d\n",&macAddr->octet[0],vid,spa,idev->name,__func__,__LINE__); if(unlikely(mac == NULL || spa < 0 /* || ((0x1<<spa)&(RTL_LANPORT_MASK | RTL_WANPORT_MASK))==0 */)) return FAILED; #if !defined(CONFIG_RTL_XDSL_WIFI_HWACCELERATION) if(spa >= RTL_EXTPORT_P1) { DBG_FDB_PRK("Packet come from CPU, ignore learning. @ %s %d\n",__func__,__LINE__); return FAILED; } #endif fid = ((1<<spa)&RTL_LANPORT_MASK)?RTL_LAN_FID:RTL_WAN_FID; rowIdx = rtl8651_filterDbIndex(macAddr, fid); DBG_FDB_PRK("## mac:%pM fid:%d rowIdx:%d\n",(char *)mac,fid,rowIdx); //Check whether there is entry existed. spin_lock_irqsave(&fdb_spinlock,flags); for(colIdx = 0, cadCol = 0 ; colIdx < RTL8651_L2TBL_COLUMN; colIdx++) { int asicIdx = ((rowIdx << 2) | colIdx); rtl_hwnat_l2Tbl_data_t *pL2data = rtl_hwnat_l2Tbl[asicIdx]; if(pL2data == NULL) { if(cadCol == -1) cadCol = colIdx; continue; } else { if(!memcmp(&pL2data->macAddr.octet[0],&macAddr->octet[0],ETH_ALEN)) { found = 1; #if defined(CONFIG_RTL_XDSL_WIFI_HWACCELERATION) if(spa==RTL_EXTPORT_P3) { needMapping = 1; list_for_each_entry_safe(tmp,n,&rtk_xdsl_hwnat_mbssid_mac_mappingListHead[macAddr->octet[5]],list) { if(!memcmp(&tmp->macAddr.octet[0],&macAddr->octet[0],ETH_ALEN)) { if(tmp->dev == idev) { needMapping = 0; } else { list_del(&tmp->list); kfree(tmp); } break; } } } else { needMapping = 0; list_for_each_entry_safe(tmp,n,&rtk_xdsl_hwnat_mbssid_mac_mappingListHead[macAddr->octet[5]],list) { if(!memcmp(&tmp->macAddr.octet[0],&macAddr->octet[0],ETH_ALEN)) { list_del(&tmp->list); kfree(tmp); break; } } } #endif //Port moving. if(unlikely((0x1<<spa) != pL2data->memberPortMask)) { //Port moving DBG_FDB_PRK("L2(%pM) but spa:%d does not match mbmber(0x%x). @ %s %d\n",&macAddr->octet[0],spa,pL2data->memberPortMask,__func__,__LINE__); memset(&l2,0,sizeof(rtl865x_tblAsicDrv_l2Param_t)); rtl865x_getHWL2Table(rowIdx,colIdx,&l2); //Delete h/w forwarding entry such that Linux could be aware of port changed l2.memberPortMask = (0x1<<spa); ret = rtl865x_setHWL2Table(rowIdx,colIdx,&l2); if(ret != SUCCESS) { DBG_FDB_PRK("Update l2(%d:%d) mac:%pM member:0x%x failed! Leave @ %s %d\n",rowIdx,colIdx,&l2.macAddr.octet[0],l2.memberPortMask,__func__,__LINE__); spin_unlock_irqrestore(&fdb_spinlock,flags); return FAILED; } pL2data->memberPortMask = l2.memberPortMask; } break; } } break; } if(!found) { rtl_hwnat_l2Tbl_data_t *pL2data; //Add memset(&l2,0,sizeof(rtl865x_tblAsicDrv_l2Param_t)); memcpy(&l2.macAddr.octet[0],&macAddr->octet[0],ETH_ALEN); l2.memberPortMask = (0x1<<spa); l2.ageSec = 450; l2.isStatic = 1; l2.fid = fid; l2.auth = 1; DBG_FDB_PRK("Add l2(%d:%d) mac:%pM member:0x%x @ %s %d\n",rowIdx,cadCol,&l2.macAddr.octet[0],l2.memberPortMask,__func__,__LINE__); ret = rtl865x_setHWL2Table(rowIdx,cadCol,&l2); if(ret != SUCCESS) { DBG_FDB_PRK("Add l2(%d:%d) mac:%pM failed(ret=%d)! Leave @ %s %d\n",rowIdx,cadCol,&l2.macAddr.octet[0],ret,__func__,__LINE__); spin_unlock_irqrestore(&fdb_spinlock,flags); return FAILED; } asicIdx = (rowIdx<<2 | cadCol); pL2data = kmalloc(sizeof(rtl_hwnat_l2Tbl_data_t),GFP_KERNEL); memset(pL2data,0,sizeof(rtl_hwnat_l2Tbl_data_t)); pL2data->memberPortMask = l2.memberPortMask; pL2data->timeout = RTL_HWNAT_L2_TIMEOUT_SECS; pL2data->dev = idev; memcpy(&pL2data->macAddr.octet[0],&macAddr->octet[0],ETH_ALEN); pL2data->asicIdx = asicIdx; INIT_LIST_HEAD(&pL2data->clist); list_add_tail(&pL2data->clist,&rtl_hwnat_l2Tbl_chkListHead[asicIdx>>5]); rtl_hwnat_l2Tbl[asicIdx] = pL2data; DBG_FDB_PRK(" Learn mac:%pM index:%d @ %s %d\n",macAddr->octet,asicIdx,__func__,__LINE__); rtl_hwnat_l2Tbl_chkSet[asicIdx>>5] |= (1<<(asicIdx&0x1f)); } else DBG_FDB_PRK("Entry existed! l2(%d) mac:%pM spa:%d @ %s %d\n",asicIdx,&macAddr->octet[0],spa,__func__,__LINE__); #if defined(CONFIG_RTL_XDSL_WIFI_HWACCELERATION) //Add mbssid to mac mapping. if(spa==RTL_EXTPORT_P3 && (!found || needMapping)) { DBG_FDB_PRK(" Learn mac:%pM index:%d @ %s %d\n",macAddr->octet,asicIdx,__func__,__LINE__); mapping = kmalloc(sizeof(rtk_xdsl_hwnat_mbssid_mac_mapping_t),GFP_KERNEL); memcpy(&mapping->macAddr.octet[0],&macAddr->octet[0],ETH_ALEN); mapping->dev = idev; INIT_LIST_HEAD(&mapping->list); list_add_tail(&mapping->list,&rtk_xdsl_hwnat_mbssid_mac_mappingListHead[macAddr->octet[5]]); } #endif spin_unlock_irqrestore(&fdb_spinlock,flags); return SUCCESS; } void rtl_del_hwnat_l2(const unsigned char *addr) { int i = 0; rtl_hwnat_l2Tbl_data_t *pL2,*n; unsigned long flags; spin_lock_irqsave(&fdb_spinlock,flags); for(i = 0; i < RTL_HWNAT_L2TBL_BUCKET_SIZE; i++) { if(rtl_hwnat_l2Tbl_chkSet[i]) { list_for_each_entry_safe(pL2,n,&rtl_hwnat_l2Tbl_chkListHead[i],clist) { rtl865x_tblAsicDrv_l2Param_t l2t; if(memcmp(&pL2->macAddr.octet[0],addr,ETH_ALEN)) continue; memset(&l2t,0,sizeof(rtl865x_tblAsicDrv_l2Param_t)); rtl865x_getHWL2Table(pL2->asicIdx>>2,pL2->asicIdx&0x3,&l2t); if(l2t.memberPortMask) { /* Delete H/W L2 */ l2t.memberPortMask = 0; l2t.ageSec = 0; l2t.isStatic = 0; DBG_FDB_PRK("Delete mac:%pM index:%d @ %s %d\n",l2t.macAddr.octet,pL2->asicIdx,__func__,__LINE__); rtl865x_setHWL2Table(pL2->asicIdx>>2,pL2->asicIdx&0x3,&l2t); /* Delete S/W L2 */ rtl_hwnat_l2Tbl_chkSet[pL2->asicIdx>>5] &= ~(0x1<<(pL2->asicIdx&0x1f)); list_del(&pL2->clist); rtl_hwnat_l2Tbl[pL2->asicIdx] = NULL; kfree(pL2); spin_unlock_irqrestore(&fdb_spinlock,flags); return ; } } } } spin_unlock_irqrestore(&fdb_spinlock,flags); } #include <net/rtl/rtl867x_hwnat_api.h> static struct timer_list rtl865x_l2_sync_timer; void rtl865x_l2_sync_handle(unsigned long _data) { int i = 0; rtl_hwnat_l2Tbl_data_t *pL2,*n; unsigned long flags; spin_lock_irqsave(&fdb_spinlock,flags); for(i = 0; i < RTL_HWNAT_L2TBL_BUCKET_SIZE; i++) { if(rtl_hwnat_l2Tbl_chkSet[i]) { if(list_empty(&rtl_hwnat_l2Tbl_chkListHead[i])) { DBG_FDB_PRK("WARNING! HWNAT L2 checking list unsyncronized! @ %s %d\n",__func__,__LINE__); goto out; } list_for_each_entry_safe(pL2,n,&rtl_hwnat_l2Tbl_chkListHead[i],clist) { rtl865x_tblAsicDrv_l2Param_t l2t; memset(&l2t,0,sizeof(rtl865x_tblAsicDrv_l2Param_t)); rtl865x_getHWL2Table(pL2->asicIdx>>2,pL2->asicIdx&0x3,&l2t); if(l2t.memberPortMask) { struct net_bridge_fdb_entry *fdb = NULL; fdb = rtl865x_fdb_lookup_by_mac(&l2t.macAddr); /* e.g., port link-down */ if(fdb) pL2->fdbExist = 1; //Count down or refresh if(l2t.ageSec > 300) { pL2->timeout = RTL_HWNAT_L2_TIMEOUT_SECS; if(fdb) fdb->updated = jiffies; } else { pL2->timeout -= RTL_HWNAT_L2_TICK_SECS; DBG_FDB_PRK("L2:%d remained:%d(seconds) [mac:%pM] @ %s %d\n",pL2->asicIdx,pL2->timeout,l2t.macAddr.octet,__func__,__LINE__); } /* Aging out or linux fdb deleted */ if(pL2->timeout <= 0 || (pL2->fdbExist && fdb == NULL)) { DBG_FDB_PRK("L2:%d timeout! [mac:%pM] @ %s %d\n",pL2->asicIdx,l2t.macAddr.octet,__func__,__LINE__); /* Flush fastbridge entries by mac */ brgScDelete(&l2t.macAddr.octet[0]); /* Clear associated H/W forwarding rule */ rtl8676_del_L2Unicast_hwacc_agingout_mac(&l2t.macAddr.octet[0]); /* Delete H/W L2 */ l2t.memberPortMask = 0; l2t.ageSec = 0; l2t.isStatic = 0; DBG_FDB_PRK("Delete mac:%pM index:%d @ %s %d\n",l2t.macAddr.octet,pL2->asicIdx,__func__,__LINE__); rtl865x_setHWL2Table(pL2->asicIdx>>2,pL2->asicIdx&0x3,&l2t); /* Delete S/W L2 */ rtl_hwnat_l2Tbl_chkSet[pL2->asicIdx>>5] &= ~(0x1<<(pL2->asicIdx&0x1f)); list_del(&pL2->clist); rtl_hwnat_l2Tbl[pL2->asicIdx] = NULL; kfree(pL2); } } else { DBG_FDB_PRK("WARING!! index:%d (mac:%pM) does exist in hardware L2 table! @%s %d\n",pL2->asicIdx,&pL2->macAddr.octet[0],__func__,__LINE__); rtl_hwnat_l2Tbl_chkSet[pL2->asicIdx>>5] &= ~(0x1<<(pL2->asicIdx&0x1f)); list_del(&pL2->clist); rtl_hwnat_l2Tbl[pL2->asicIdx] = NULL; kfree(pL2); continue; } //printk("HWL2idx:%d mac:%pM age:%d timeout:%d \n",pL2->asicIdx,&pL2->macAddr.octet[0],l2t.ageSec,pL2->timeout); } } } out: mod_timer(&rtl865x_l2_sync_timer, jiffies + RTL_HWNAT_L2_TICK_SECS*HZ); spin_unlock_irqrestore(&fdb_spinlock,flags); } static int __init __rtl865x_l2_sync_init(void) { int i; for(i = 0; i < RTL_HWNAT_L2TBL_BUCKET_SIZE; i++) { INIT_LIST_HEAD(&rtl_hwnat_l2Tbl_chkListHead[i]); rtl_hwnat_l2Tbl_chkSet[i] = 0; } for(i = 0; i < RTL_HW_L2TBL_SIZE; i++) rtl_hwnat_l2Tbl[i] = NULL; init_timer(&rtl865x_l2_sync_timer); rtl865x_l2_sync_timer.function = rtl865x_l2_sync_handle; rtl865x_l2_sync_timer.data = (unsigned long)NULL; mod_timer(&rtl865x_l2_sync_timer, jiffies + RTL_HWNAT_L2_TICK_SECS*HZ); return 0; } module_init(__rtl865x_l2_sync_init); #endif //defined(CONFIG_RTL_HWNAT_FDB_TIMER_SYNC)