/* * Copyright c Realtek Semiconductor Corporation, 2008 * All rights reserved. * * Program : napt table driver * Abstract : * Author : hyking (hyking_liu@realsil.com.cn) */ /* @doc RTL_LAYEREDDRV_API @module rtl865x_nat.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 <net/rtl/rtl_types.h> #include <net/rtl/rtl_glue.h> #include <net/rtl/rtl865x_netif.h> #include "common/mbuf.h" #include "AsicDriver/rtl865x_asicCom.h" //#include "assert.h" //#include "rtl865xc_swNic.h" //#include <common/types.h> #include "AsicDriver/rtl865x_hwPatch.h" /* define for chip related spec */ #ifdef CONFIG_RTL_LAYERED_ASIC_DRIVER #include "AsicDriver/rtl865x_asicL4.h" #else #include "common/rtl8651_aclLocal.h" #include "rtl865xC_tblAsicDrv.h" #endif #include "common/rtl_errno.h" //#include <net/rtl/rtl_queue.h> #include "AsicDriver/rtl865xc_asicregs.h" #include "common/rtl865x_eventMgr.h" #include "l3Driver/rtl865x_ip.h" #include <net/rtl/rtl865x_nat.h> #include "rtl865x_nat_local.h" //#include "rtl865x_ppp.h" #include "common/rtl865x_netif_local.h" #ifdef CONFIG_RTL_PROC_DEBUG #include <linux/seq_file.h> #endif #include <linux/jiffies.h> #ifdef CONFIG_RTL_HW_QOS_SUPPORT #include <net/rtl/rtl865x_outputQueue.h> #endif struct nat_table nat_tbl; static int32 rtl865x_enableNaptFourWay=FALSE; static int32 rtl865x_enableEnhancehash1=TRUE; int MAX_NAPT_HW_NUMBER=0; DEFINE_SPINLOCK(l4napt_spinlock); struct nat_table *getSwNAPTTbl(void) { return &nat_tbl; } static int32 _rtl865x_nat_init(void) { rtl865x_tblAsicDrv_naptTcpUdpParam_t naptTcpUdp; uint32 flowTblIdx; memset(nat_tbl.nat_bucket, 0, sizeof(struct nat_entry)*RTL8676_TCPUDPTBL_SIZE_HW); nat_tbl.tcp_timeout = TCP_TIMEOUT; nat_tbl.udp_timeout = UDP_TIMEOUT; nat_tbl.freeHWEntryNum=RTL8676_TCPUDPTBL_SIZE_HW; MAX_NAPT_HW_NUMBER=0; /* Set ASIC timeout value */ rtl8651_setAsicNaptTcpLongTimeout(TCP_TIMEOUT); rtl8651_setAsicNaptTcpMediumTimeout(TCP_TIMEOUT); rtl8651_setAsicNaptTcpFastTimeout(TCP_TIMEOUT); rtl8651_setAsicNaptUdpTimeout(UDP_TIMEOUT); /*enable 865xC enhanced hash1*/ _rtl8651_enableEnhancedHash1(); #if defined(CONFIG_RTL8685SB) _rtl819x_enableNaptPerFlow(); #endif /* Initial ASIC NAT Table */ memset( &naptTcpUdp, 0, sizeof(naptTcpUdp) ); #if !defined(CONFIG_RTL_HW_NAPT_4KENTRY) naptTcpUdp.isCollision = 1; naptTcpUdp.isCollision2 = 1; #endif for(flowTblIdx=0; flowTblIdx<RTL8676_TCPUDPTBL_SIZE_HW; flowTblIdx++) rtl8651_setAsicNaptTcpUdpTable(TRUE, flowTblIdx, &naptTcpUdp ); //rtl865x_nat_register_event(); return SUCCESS; } static struct nat_entry * _rtl865x_nat_outbound_lookup(struct nat_tuple *nat_tuple) { struct nat_entry *nat_out; uint32 i,hash; hash = rtl8651_naptTcpUdpTableIndex((uint8)nat_tuple->proto, nat_tuple->int_host.ip, nat_tuple->int_host.port, nat_tuple->rem_host.ip, nat_tuple->rem_host.port); assert(hash<RTL8676_TCPUDPTBL_SIZE_HW); if(rtl865x_enableNaptFourWay==TRUE) { for(i=0; i<4; i++) { nat_out = &nat_tbl.nat_bucket[hash]; if (!memcmp(nat_out, nat_tuple, sizeof(*nat_tuple)) && (nat_out->flags&NAT_OUTBOUND)) { return nat_out; } hash=(hash&0xFFFFFFFC)+(hash+1)%4; } } else { nat_out = &nat_tbl.nat_bucket[hash]; if (!memcmp(nat_out, nat_tuple, sizeof(*nat_tuple)) && (nat_out->flags&NAT_OUTBOUND)) return nat_out; } return NULL; } static struct nat_entry * _rtl865x_nat_inbound_lookup(struct nat_tuple *nat_tuple) { struct nat_entry *nat_in; uint32 hash; #if defined(CONFIG_RTL_HW_NAPT_4KENTRY) int i; #endif hash = rtl8651_naptTcpUdpTableIndex((uint8)nat_tuple->proto, nat_tuple->rem_host.ip, nat_tuple->rem_host.port, nat_tuple->ext_host.ip, nat_tuple->ext_host.port); assert(hash<RTL8676_TCPUDPTBL_SIZE_HW); nat_in = &nat_tbl.nat_bucket[hash]; #if defined(CONFIG_RTL_HW_NAPT_4KENTRY) for(i=0; i<4; i++) { nat_in = &nat_tbl.nat_bucket[hash]; if (!memcmp(nat_in, nat_tuple, sizeof(*nat_tuple)) && (nat_in->flags&NAT_INBOUND)) { return nat_in; } hash=(hash&0xFFFFFFFC)+(hash+1)%4; } #else if (!memcmp(nat_in, nat_tuple, sizeof(*nat_tuple)) && (nat_in->flags&NAT_INBOUND)) { return nat_in; } #endif return NULL; } #if defined (CONFIG_RTL_INBOUND_COLLISION_AVOIDANCE) static int _rtl865x_isEntryPreReserved(uint32 index) { struct nat_entry *natEntry; if(index>=RTL8676_TCPUDPTBL_SIZE_HW) { return FALSE; } natEntry= &nat_tbl.nat_bucket[index]; if((natEntry->flags & NAT_PRE_RESERVED)) { if(jiffies>=natEntry->reserveTime) { if(jiffies>(natEntry->reserveTime+RESERVE_EXPIRE_TIME*HZ)) { /*pre-reserve become invalid now*/ natEntry->flags &= (~(NAT_PRE_RESERVED)); natEntry->reserveTime=0; return FALSE; } else { return TRUE; } } else { /*timer overflow*/ if(((0xFFFFFFFF-natEntry->reserveTime)+jiffies+1)>(RESERVE_EXPIRE_TIME*HZ)) { natEntry->flags &= (~(NAT_PRE_RESERVED)); natEntry->reserveTime=0; return FALSE; } else { return TRUE; } } return TRUE; } return FALSE; } static int _rtl865x_PreReserveEntry(uint32 index) { struct nat_entry *natEntry; if(index>=RTL8676_TCPUDPTBL_SIZE_HW) { return FAILED; } natEntry= &nat_tbl.nat_bucket[index]; if(NAT_INUSE(natEntry)) { /*already used by other napt connection, can not reserve it*/ natEntry->flags &= (~(NAT_PRE_RESERVED)); natEntry->reserveTime=0; } else { natEntry->flags|=NAT_PRE_RESERVED; natEntry->reserveTime=jiffies; } return SUCCESS; } static int _rtl865x_getNaptHashInfo( uint32 protocol, ipaddr_t intIp, uint32 intPort, ipaddr_t extIp, uint32 extPort, ipaddr_t remIp, uint32 remPort, rtl865x_naptHashInfo_t *naptHashInfo) { uint32 in, out; uint32 i,index; struct nat_entry *nat_in, *nat_out; struct nat_entry *natEntry; if(naptHashInfo==NULL) { return FAILED; } memset(naptHashInfo, 0, sizeof(rtl865x_naptHashInfo_t)); in = rtl8651_naptTcpUdpTableIndex((uint8)protocol, remIp, remPort, extIp, extPort); out = rtl8651_naptTcpUdpTableIndex((uint8)protocol, intIp, intPort, remIp, remPort); if(rtl865x_enableNaptFourWay==TRUE) { uint32 outAvailIdx=0xFFFFFFFF; index=out; for(i=0;i<4;i++) { natEntry = &nat_tbl.nat_bucket[index]; if (NAT_INUSE(natEntry) || _rtl865x_isEntryPreReserved(index)) { } else { if(index==in) { /*collide with inbound*/ } else { out=index; break; } } index=(index&0xFFFFFFFC)+(index+1)%4; assert(index<=RTL8676_TCPUDPTBL_SIZE_HW); } if(i>=4) { /*only one empty entry, but collide with its own inbound*/ if(outAvailIdx!=0xFFFFFFFF) { out=outAvailIdx; } } else { /*proper empty entry has been found*/ } } naptHashInfo->outIndex=out; naptHashInfo->inIndex=in; if((in&0xFFFFFFFC)==(out&0xFFFFFFFC)) { naptHashInfo->sameFourWay=1; } if(in==out) { naptHashInfo->sameLocation=1; nat_out = &nat_tbl.nat_bucket[out]; if(NAT_INUSE(nat_out)|| _rtl865x_isEntryPreReserved(out)) { naptHashInfo->outCollision=1; } naptHashInfo->inCollision=1; } else { nat_out = &nat_tbl.nat_bucket[out]; nat_in = &nat_tbl.nat_bucket[in]; if(NAT_INUSE(nat_out) || _rtl865x_isEntryPreReserved(out)) { naptHashInfo->outCollision=1; } if (NAT_INUSE(nat_in) || _rtl865x_isEntryPreReserved(in)) { naptHashInfo->inCollision=1; } } index=in; naptHashInfo->inFreeCnt=0; for(i=0;i<4;i++) { natEntry = &nat_tbl.nat_bucket[index]; if (NAT_INUSE(natEntry) || _rtl865x_isEntryPreReserved(index)) { } else { naptHashInfo->inFreeCnt++; } index=(index&0xFFFFFFFC)+(index+1)%4; assert(index<=RTL8676_TCPUDPTBL_SIZE_HW); } #if 0 printk("%s:%d:%s (%u.%u.%u.%u:%u -> %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u) ,out is %d,in is %d\n", __FUNCTION__,__LINE__,protocol?"tcp":"udp", NIPQUAD(intIp), intPort, NIPQUAD(extIp), extPort, NIPQUAD(remIp), remPort, out, in); #endif return SUCCESS; } int rtl865x_optimizeExtPort(unsigned short origDelta, unsigned int rangeSize, unsigned short *newDelta) { int i; int msb; unsigned int bitShift; msb=0; for(i=0;i<16;i++) { if((1<<i) & rangeSize) { msb=i; } } if(((1<<msb)+1)>rangeSize) { if(msb>1) { msb--; } } *newDelta=0; if(msb<10) { bitShift=0x01; for(i=0;i<=msb;i++) { if(i==0)/*bit0 keep the same*/ { if(origDelta&bitShift) { *newDelta|=bitShift; } } else /*original bit1~ bit_maxPower mapped to bit_maxPower~bit1*/ { if(origDelta&bitShift) { *newDelta |=(0x1<<(msb+1-i)); } } bitShift=bitShift<<1; } } else { bitShift=0x01; for(i=0;i<=msb;i++) { if(i==0) /*bit0 keep the same*/ { if(origDelta&bitShift) { *newDelta |=bitShift; } } else if (i<10) /*bit1~ bit9 mapped to bit 9~bit1*/ { if(origDelta&bitShift) { *newDelta |=(0x1<<(10-i)); } } else/*other bits keep the same*/ { if(origDelta&bitShift) { *newDelta |=bitShift; } } bitShift=bitShift<<1; } } return SUCCESS; } int rtl865x_getAsicNaptHashScore( uint32 protocol, ipaddr_t intIp, uint32 intPort, ipaddr_t extIp, uint32 extPort, ipaddr_t remIp, uint32 remPort, uint32 *naptHashScore) { rtl865x_naptHashInfo_t naptHashInfo; _rtl865x_getNaptHashInfo( protocol, intIp, intPort, extIp, extPort, remIp, remPort, &naptHashInfo); /*initialize napt hash score*/ *naptHashScore=100; /*note:we can not change outbound index*/ if(naptHashInfo.inCollision==FALSE) { if(naptHashInfo.inFreeCnt==4) { if(!naptHashInfo.sameFourWay) { *naptHashScore=100; } else { if(!naptHashInfo.sameLocation) { *naptHashScore=80; } else { *naptHashScore=0; } } } else if (naptHashInfo.inFreeCnt==3) { if(!naptHashInfo.sameFourWay) { *naptHashScore=80; } else { if(!naptHashInfo.sameLocation) { *naptHashScore=70; } else { *naptHashScore=0; } } } else if (naptHashInfo.inFreeCnt==2) { if(!naptHashInfo.sameFourWay) { *naptHashScore=70; } else { if(!naptHashInfo.sameLocation==FALSE) { *naptHashScore=60; } else { *naptHashScore=0; } } } else if (naptHashInfo.inFreeCnt==1) { if(naptHashInfo.sameFourWay==FALSE) { *naptHashScore=60; } else { *naptHashScore=0; } } else { *naptHashScore=0; } } else { /*worst case:inbound is collision*/ *naptHashScore=0; } return SUCCESS; } int32 rtl865x_preReserveConn( uint32 protocol, ipaddr_t intIp, uint32 intPort, ipaddr_t extIp, uint32 extPort, ipaddr_t remIp, uint32 remPort) { rtl865x_naptHashInfo_t naptHashInfo; _rtl865x_getNaptHashInfo( protocol, intIp, intPort, extIp, extPort, remIp, remPort, &naptHashInfo); if(naptHashInfo.outCollision==FALSE) { _rtl865x_PreReserveEntry(naptHashInfo.outIndex); } if(naptHashInfo.inCollision==FALSE) { _rtl865x_PreReserveEntry(naptHashInfo.inIndex); } return SUCCESS; } #endif static int32 _rtl865x_lookupNaptConnection (ipaddr_t intIp, uint16 intPort,ipaddr_t extIp, uint16 extPort,ipaddr_t remIp, uint16 remPort,uint8 protocol,uint8 isUpstream) { struct nat_tuple nat_tuple; struct nat_entry* nat_entry; rtl865x_tblAsicDrv_naptTcpUdpParam_t asic_nat; int index,rc; memset(&nat_tuple, 0, sizeof(struct nat_tuple)); nat_tuple.int_host.ip = intIp; nat_tuple.int_host.port = intPort; nat_tuple.ext_host.ip = extIp; nat_tuple.ext_host.port = extPort; nat_tuple.rem_host.ip = remIp; nat_tuple.rem_host.port = remPort; nat_tuple.proto = protocol; if(isUpstream) nat_entry = _rtl865x_nat_outbound_lookup(&nat_tuple); else nat_entry = _rtl865x_nat_inbound_lookup(&nat_tuple); if (nat_entry==NULL) return -1; if(isUpstream) index = nat_entry->out; else index = nat_entry->in; memset(&asic_nat ,0 ,sizeof(rtl865x_tblAsicDrv_naptTcpUdpParam_t)); rc = rtl8651_getAsicNaptTcpUdpTable(index, &asic_nat); assert(rc==SUCCESS); assert(asic_nat.ageSec>=0); return asic_nat.ageSec; } #if defined(CONFIG_RTL_HW_NAPT_4KENTRY) int32 rtl865x_getNaptConnectionPosition (ipaddr_t intIp, uint16 intPort,ipaddr_t extIp, uint16 extPort,ipaddr_t remIp, uint16 remPort,uint8 protocol,uint8 isUpstream) { struct nat_tuple nat_tuple; struct nat_entry* nat_entry; int index; memset(&nat_tuple, 0, sizeof(struct nat_tuple)); nat_tuple.int_host.ip = intIp; nat_tuple.int_host.port = intPort; nat_tuple.ext_host.ip = extIp; nat_tuple.ext_host.port = extPort; nat_tuple.rem_host.ip = remIp; nat_tuple.rem_host.port = remPort; nat_tuple.proto = protocol; if(isUpstream) nat_entry = _rtl865x_nat_outbound_lookup(&nat_tuple); else nat_entry = _rtl865x_nat_inbound_lookup(&nat_tuple); if (nat_entry==NULL) return -1; if(isUpstream) index = nat_entry->out; else index = nat_entry->in; return index; } int32 rtl_xdsl_getAsicNaptIndex(ipaddr_t sip, uint16 sport,ipaddr_t dip, uint16 dport,uint8 protocol,uint8 isUpstream) { int hash; int i; struct nat_entry *natEntry; hash = rtl8651_naptTcpUdpTableIndex(protocol,sip, sport, dip, dport); assert(hash<RTL8676_TCPUDPTBL_SIZE_HW); if(isUpstream) { for(i=0; i<4; i++) { natEntry = &nat_tbl.nat_bucket[hash]; if((natEntry->flags&NAT_OUTBOUND)!= 0 && natEntry->proto_ == protocol && natEntry->int_ip_ == sip && natEntry->int_port_ == sport && natEntry->rem_ip_ == dip && natEntry->rem_port_ == dport) return hash; } if(rtl865x_enableNaptFourWay!=TRUE) return -1; hash=(hash&0xFFFFFFFC)+(hash+1)%4; } else { for(i=0; i<4; i++) { natEntry = &nat_tbl.nat_bucket[hash]; if((natEntry->flags&NAT_INBOUND) != 0 && natEntry->proto_ == protocol && natEntry->rem_ip_ == sip && natEntry->rem_port_ == sport && natEntry->ext_ip_ == dip && natEntry->ext_port_ == dport) return hash; } hash=(hash&0xFFFFFFFC)+(hash+1)%4; } return -1; } #endif //20150128 boyce:add HASH1_NAPT support and HASH1_NAPT only support isUpstream=1 , #if defined(CONFIG_RTL8685SB) #if defined(CONFIG_RTL_HW_NAPT_4KENTRY) static int32 _rtl865x_addNaptConnection (ipaddr_t intIp, uint16 intPort,ipaddr_t extIp, uint16 extPort,ipaddr_t remIp, uint16 remPort,int ipIdx, uint8 protocol,uint8 nxtHop_valid,uint8 nxtHop_index,uint8 pri_valid,uint8 pri_value,uint8 isUpstream,uint8 dscp_remark,uint8 dscp_value) #else static int32 _rtl865x_addNaptConnection (ipaddr_t intIp, uint16 intPort,ipaddr_t extIp, uint16 extPort,ipaddr_t remIp, uint16 remPort,int ipIdx, uint8 protocol,uint8 nxtHop_valid,uint8 nxtHop_index,uint8 pri_valid,uint8 pri_value,uint8 isUpstream) #endif #else static int32 _rtl865x_addNaptConnection (ipaddr_t intIp, uint16 intPort,ipaddr_t extIp, uint16 extPort,ipaddr_t remIp, uint16 remPort,uint8 protocol,uint8 pri_valid,uint8 pri_value,uint8 isUpstream) #endif { uint32 in, out , result_idx; rtl865x_tblAsicDrv_naptTcpUdpParam_t asic_nat; int ret; /* 0. santity check */ if(protocol!=RTL865X_PROTOCOL_TCP && protocol!=RTL865X_PROTOCOL_UDP) return RTL_EINVALIDINPUT; if(pri_valid && (pri_value<0||pri_value>=TOTAL_VLAN_PRIORITY_NUM)) return RTL_EINVALIDINPUT; if(isUpstream==0 && rtl865x_enableEnhancehash1==FALSE) //hash1 not support inbound napt entry return RTL_EINVALIDINPUT; /* 1. duplicate check */ ret = _rtl865x_lookupNaptConnection(intIp, intPort, extIp, extPort, remIp, remPort,protocol,isUpstream); if(ret!=-1) return RTL_EENTRYALREADYEXIST; /* 2. we have to check both inbound and outbound index at the same time (we have to promise that out != in) */ out = rtl8651_naptTcpUdpTableIndex((uint8)protocol, intIp, intPort, remIp, remPort); if(rtl865x_enableEnhancehash1==FALSE){ in = 8192; //hash1 should ingore naptIn,set a large number }else in = rtl8651_naptTcpUdpTableIndex((uint8)protocol, remIp, remPort, extIp, extPort); if(rtl865x_enableNaptFourWay==FALSE) { if(out==in) /*we don't support this case at present, otherwise, when delete napt connection must be very very careful*/ return RTL_EINVALIDINPUT; if(isUpstream) { if ( NAT_INUSE(&(nat_tbl.nat_bucket[out])) ) { /* the index has been occupied*/ return RTL_EINVALIDINPUT; } else result_idx = out; } else /* downstream */ { if ( NAT_INUSE(&(nat_tbl.nat_bucket[in])) ) { /* the index has been occupied*/ return RTL_EINVALIDINPUT; } else result_idx = in; } } else { /* we have 4 chances to find one outbound hash index which is not equal to inbound index */ int i; #if !defined(CONFIG_RTL_HW_NAPT_4KENTRY) if(!isUpstream && (nat_tbl.nat_bucket[in].flags&NAT_OUTBOUND)!=0 && (nat_tbl.nat_bucket[in].flags&NAT_OPTIMIZED_CHECKED)!=0) { rtl865x_tblAsicDrv_naptTcpUdpParam_t naptOccupied; //if(naptOccupied.isValid && naptOccupied.tcpFlag==0x3) //NAPT entry is occupied by an outbound entry,Kick it aside! { int ii; uint32 fourway_index,fourwayInboundEntry=0; // for load-balance for(ii=0;ii<4;ii++) { fourway_index=(in&0xFFFFFFFC)+((in+ii)%4); if( nat_tbl.nat_bucket[fourway_index].flags & NAT_INBOUND) fourwayInboundEntry++; } // Find another available 4-way associated entry for(ii=0;ii<4;ii++) { fourway_index=(in&0xFFFFFFFC)+ii; if(in==fourway_index) continue; if( NAT_INUSE(&(nat_tbl.nat_bucket[fourway_index])) == 0 ) break; } nat_tbl.nat_bucket[in].flags |= NAT_OPTIMIZED_CHECKED; if(ii!=4 && fourwayInboundEntry<=2) // Found it! There is an available entry, put the outbound occupied entry to empty slot. { DBG_PRK(RTL_DEBUG_LEVEL_MULTIWAN,"[HWACC NAPT]",36,40,"in=%d fourway_index=%d",in,fourway_index); memset(&naptOccupied,0,sizeof(rtl865x_tblAsicDrv_naptTcpUdpParam_t)); rtl8651_getAsicNaptTcpUdpTable(in,&naptOccupied); rtl8651_setAsicNaptTcpUdpTable(1,fourway_index,&naptOccupied); memcpy(&(nat_tbl.nat_bucket[fourway_index]),&(nat_tbl.nat_bucket[in]),sizeof(struct nat_entry)); nat_tbl.nat_bucket[in].flags = 0; } } } #endif result_idx=RTL8676_TCPUDPTBL_SIZE_HW; for(i=0;i<4;i++) { uint32 fourway_hash=(out&0xFFFFFFFC)+(out+i)%4; if(fourway_hash==in) continue; if(isUpstream) { if ( NAT_INUSE(&(nat_tbl.nat_bucket[fourway_hash])) ) continue; } else /* downstream */ { if ( NAT_INUSE(&(nat_tbl.nat_bucket[in])) ) break; //exit for loop directly } /* we find it if reaches here */ if(isUpstream) result_idx = fourway_hash; else result_idx = in; break; } #if defined(CONFIG_RTL_HW_NAPT_4KENTRY) if(!isUpstream && NAT_INUSE(&(nat_tbl.nat_bucket[in])) ) { uint32 fourway_hash; for(i=0;i<4;i++) { fourway_hash=(in&0xFFFFFFFC)+(i%4); if ( NAT_INUSE(&(nat_tbl.nat_bucket[fourway_hash])) ) continue; result_idx = fourway_hash; break; } } #endif if(result_idx==RTL8676_TCPUDPTBL_SIZE_HW) return RTL_EINVALIDINPUT; } /* 3. set sw_napt */ memset(&(nat_tbl.nat_bucket[result_idx]), 0, sizeof(struct nat_entry)); nat_tbl.nat_bucket[result_idx].tuple_info.int_host.ip = intIp; nat_tbl.nat_bucket[result_idx].tuple_info.int_host.port = intPort; nat_tbl.nat_bucket[result_idx].tuple_info.ext_host.ip = extIp; if(rtl865x_enableEnhancehash1==FALSE) nat_tbl.nat_bucket[result_idx].tuple_info.ext_host.port = (((intIp+intPort+remIp+remPort+protocol)&0x3f)<<10) +out; else nat_tbl.nat_bucket[result_idx].tuple_info.ext_host.port = extPort; nat_tbl.nat_bucket[result_idx].tuple_info.rem_host.ip = remIp; nat_tbl.nat_bucket[result_idx].tuple_info.rem_host.port = remPort; nat_tbl.nat_bucket[result_idx].tuple_info.proto = protocol; if(isUpstream) { nat_tbl.nat_bucket[result_idx].out =result_idx; SET_NAT_FLAGS(&(nat_tbl.nat_bucket[result_idx]), NAT_OUTBOUND); } else { nat_tbl.nat_bucket[result_idx].in =result_idx; SET_NAT_FLAGS(&(nat_tbl.nat_bucket[result_idx]), NAT_INBOUND); } #ifdef CONFIG_RTL_HW_QOS_SUPPORT if(pri_valid) { SET_NAT_FLAGS(&(nat_tbl.nat_bucket[result_idx]), NAT_PRIOTIY_VALID); nat_tbl.nat_bucket[result_idx].priority = pri_value; } #endif /* 4. finally, set asic napt (hw_napt) */ memset(&asic_nat, 0, sizeof(asic_nat)); asic_nat.insideLocalIpAddr = intIp; asic_nat.insideLocalPort = intPort; asic_nat.isCollision = 0; asic_nat.isCollision2 = 0; asic_nat.isDedicated = 0; asic_nat.isStatic = 1; asic_nat.isTcp = (protocol==RTL865X_PROTOCOL_TCP)? 1: 0; asic_nat.isValid = 1; #if defined(CONFIG_RTL8685SB) asic_nat.nhIdxValid = nxtHop_valid; asic_nat.nhIdx = nxtHop_index; #endif if(isUpstream) { if(rtl865x_enableEnhancehash1==FALSE){ asic_nat.offset=((intIp+intPort+remIp+remPort+protocol)&0x3f); //extPort = (offset<<10)+ outhashIdx }else{ #if defined(CONFIG_RTL_HW_NAPT_4KENTRY) asic_nat.offset = (extPort&0x0000ffff)>>12; asic_nat.selEIdxExt = (extPort>>10)&0x3; #else asic_nat.offset = (extPort&0x0000ffff)>>10; #endif asic_nat.selEIdx = (extPort&0x3ff); #if defined(CONFIG_RTL8685SB) asic_nat.selExtIPIdx = ipIdx; /* for upstream(outbound), the selExtIPIdx is useless.....*/ #else asic_nat.selExtIPIdx = 0; /* for upstream(outbound), the selExtIPIdx is useless.....*/ #endif asic_nat.tcpFlag = 0x3; /* bit0 : 0:inbound 1 : outbound bit1 : 0:the napt flow use 1 entry 1 : the napt flow use 2 entries bit2 : 0:trap SYN/FIN/RST 1 : do not trap (enhanced hash1 doesn't support outbound/inbound share one connection) */ } } else { uint16 very = rtl8651_naptTcpUdpTableIndex(((uint8)protocol) |HASH_FOR_VERI , remIp, remPort, 0, 0); #if defined(CONFIG_RTL_HW_NAPT_4KENTRY) if(IS_RLE0822C_HWNAT()) { asic_nat.offset = (extPort & 0xf); asic_nat.selEIdxExt = (very>>10)&0x3; asic_nat.selExtIPIdx = (extPort & 0xff) >> 4; } else { asic_nat.offset = ((extPort>>2) & 0x3f); asic_nat.selEIdxExt = (extPort & 0x3); asic_nat.selExtIPIdx = (extPort & 0x3ff) >> 6; } #else asic_nat.offset = (extPort & 0x3f); asic_nat.selExtIPIdx = (extPort & 0x3ff) >> 6; #endif asic_nat.selEIdx = very &0x3ff; asic_nat.tcpFlag = 0x2; } asic_nat.ageSec = (protocol==RTL865X_PROTOCOL_TCP)?(nat_tbl.tcp_timeout):(nat_tbl.udp_timeout); #ifdef CONFIG_RTL_HW_QOS_SUPPORT #if defined(CONFIG_RTL_HW_NAPT_4KENTRY) asic_nat.dscpRmkValid = dscp_remark; asic_nat.dscpRmkValue = dscp_value; #endif if(pri_valid) { asic_nat.priValid = TRUE; asic_nat.priority = pri_value; } else #endif asic_nat.priValid = FALSE; /* we use acl to set flow priority , so we do not need napt base ipqos */ rtl8651_setAsicNaptTcpUdpTable(1, result_idx, &asic_nat); nat_tbl.freeHWEntryNum--; MAX_NAPT_HW_NUMBER++; //Check and clear NAPT collision #if defined(CONFIG_RTL_HW_NAPT_4KENTRY) if(result_idx != (RTL8676_TCPUDPTBL_SIZE_HW-1)) { int i,col_idx; rtl865x_tblAsicDrv_naptTcpUdpParam_t entry; memset(&entry,0,sizeof(entry)); for(i = 0; i < 4; i++) { col_idx = (result_idx&~0x3)+i; if(result_idx == col_idx || col_idx >= RTL8676_TCPUDPTBL_SIZE_HW) continue; rtl8651_getAsicNaptTcpUdpTable(col_idx,&entry); if(entry.isCollision) { // printk("NAPT:%d collision:%d @ %s %d\n",col_idx,entry.isCollision,__func__,__LINE__); if(!NAT_INUSE(&(nat_tbl.nat_bucket[col_idx])) ) memset(&entry,0,sizeof(entry)); entry.isCollision = 0; rtl8651_setAsicNaptTcpUdpTable(1,col_idx,&entry); } } } #endif return SUCCESS; } static int32 _rtl865x_delNaptConnection (ipaddr_t intIp, uint16 intPort,ipaddr_t extIp, uint16 extPort,ipaddr_t remIp, uint16 remPort,uint8 protocol,uint8 isUpstream) { struct nat_tuple nat_tuple; struct nat_entry *nat_entry; int index=-1; if(protocol!=RTL865X_PROTOCOL_TCP && protocol!=RTL865X_PROTOCOL_UDP) return RTL_EINVALIDINPUT; memset(&nat_tuple, 0, sizeof(struct nat_tuple)); nat_tuple.int_host.ip = intIp; nat_tuple.int_host.port = intPort; nat_tuple.ext_host.ip = extIp; nat_tuple.ext_host.port = extPort; nat_tuple.rem_host.ip = remIp; nat_tuple.rem_host.port = remPort; nat_tuple.proto = protocol; if(isUpstream) { nat_entry = _rtl865x_nat_outbound_lookup(&nat_tuple); if(nat_entry) index = nat_entry->out; } else { nat_entry = _rtl865x_nat_inbound_lookup(&nat_tuple); if(nat_entry) index = nat_entry->in; } if(index!=-1) { #ifdef CONFIG_RTL_HW_QOS_SUPPORT if(nat_entry->flags&NAT_PRIOTIY_VALID) rtl865x_qosPriorityMappingDeRef(nat_entry->priority); #endif rtl8651_delAsicNaptTcpUdpTable(index, index); nat_tbl.freeHWEntryNum++; MAX_NAPT_HW_NUMBER--; memset(nat_entry, 0, sizeof(struct nat_entry)); } return SUCCESS; } /* isUpstream=1 => Upstream : Src NAPT or Pure routing , naptIp/naptPort = extIp/extPort isUpstream=0 => Downstream : Dst NAPT or Pure routing , naptIp/naptPort = privateIp/privatePort We record Src/Dst NAPT in both sw and hw_napt table , Pure routing only in sw_napt <Note.> napt table is NOT correlated with pure routing (pure routing only use arp,routing table , acl / nexthop table for redirecting) Here, we just record pure routing in sw_napt table only for caller to trace whether the specified conntrack is maintaind by ourselves For example, in dynamical case 1. We use rtl865x_addNaptConnection to add a pure routing connection 2. Then, use dynamic acl to permit this connection. 3. After a while , when kernel's nf_conntrack is going to timeout , use rtl865x_lookupNaptConnection to assure that this connection is maintained by 8676 hw-acc . We have to check whether this connection is still alive in hw. (see _rtl8676_query_L34Unicast_hwacc) If it is also timeout in hw, use rtl865x_delNaptConnection to cancel this maintain relationship. */ #if defined(CONFIG_RTL8685SB) #if defined(CONFIG_RTL_HW_NAPT_4KENTRY) int32 rtl865x_addNaptConnection (ipaddr_t scrIp, uint16 scrPort,ipaddr_t dstIp, uint16 dstPort,ipaddr_t naptIp, uint16 naptPort, uint8 ipIdx, uint8 protocol,uint8 nxtHop_valid,uint8 nxtHop_index,uint8 pri_valid,uint8 pri_value,uint8 isUpstream,uint8 dscp_remark,uint8 dscp_value) { int32 retval = FAILED; unsigned long flags; spin_lock_irqsave(&l4napt_spinlock,flags); if(isUpstream) retval = _rtl865x_addNaptConnection(scrIp,scrPort,naptIp,naptPort,dstIp,dstPort,ipIdx,protocol,nxtHop_valid,nxtHop_index,pri_valid,pri_value,1,dscp_remark,dscp_value); else retval = _rtl865x_addNaptConnection(naptIp,naptPort,dstIp,dstPort,scrIp,scrPort,ipIdx,protocol,nxtHop_valid,nxtHop_index,pri_valid,pri_value,0,dscp_remark,dscp_value); spin_unlock_irqrestore(&l4napt_spinlock,flags); return retval; } #else int32 rtl865x_addNaptConnection (ipaddr_t scrIp, uint16 scrPort,ipaddr_t dstIp, uint16 dstPort,ipaddr_t naptIp, uint16 naptPort, uint8 ipIdx, uint8 protocol,uint8 nxtHop_valid,uint8 nxtHop_index,uint8 pri_valid,uint8 pri_value,uint8 isUpstream) { int32 retval = FAILED; unsigned long flags; spin_lock_irqsave(&l4napt_spinlock,flags); if(isUpstream) retval = _rtl865x_addNaptConnection(scrIp,scrPort,naptIp,naptPort,dstIp,dstPort,ipIdx,protocol,nxtHop_valid,nxtHop_index,pri_valid,pri_value,1); else retval = _rtl865x_addNaptConnection(naptIp,naptPort,dstIp,dstPort,scrIp,scrPort,ipIdx,protocol,nxtHop_valid,nxtHop_index,pri_valid,pri_value,0); spin_unlock_irqrestore(&l4napt_spinlock,flags); return retval; } #endif //defined(CONFIG_RTL_HW_NAPT_4KENTRY) #else int32 rtl865x_addNaptConnection (ipaddr_t scrIp, uint16 scrPort,ipaddr_t dstIp, uint16 dstPort,ipaddr_t naptIp, uint16 naptPort,uint8 protocol,uint8 pri_valid,uint8 pri_value,uint8 isUpstream) { int32 retval = FAILED; unsigned long flags; spin_lock_irqsave(&l4napt_spinlock,flags); if(isUpstream) retval = _rtl865x_addNaptConnection(scrIp,scrPort,naptIp,naptPort,dstIp,dstPort,protocol,pri_valid,pri_value,1); else retval = _rtl865x_addNaptConnection(naptIp,naptPort,dstIp,dstPort,scrIp,scrPort,protocol,pri_valid,pri_value,0); spin_unlock_irqrestore(&l4napt_spinlock,flags); return retval; } #endif int32 rtl865x_delNaptConnection (ipaddr_t scrIp, uint16 scrPort,ipaddr_t dstIp, uint16 dstPort,ipaddr_t naptIp, uint16 naptPort,uint8 protocol,uint8 isUpstream) { int32 retval = FAILED; unsigned long flags; spin_lock_irqsave(&l4napt_spinlock,flags); if(isUpstream) retval = _rtl865x_delNaptConnection(scrIp,scrPort,naptIp,naptPort,dstIp,dstPort,protocol,1); else retval = _rtl865x_delNaptConnection(naptIp,naptPort,dstIp,dstPort,scrIp,scrPort,protocol,0); spin_unlock_irqrestore(&l4napt_spinlock,flags); return retval; } /* return -1 : the entry does not exist 0 : aging out >0 : its aging time left */ int32 rtl865x_lookupNaptConnection (ipaddr_t scrIp, uint16 scrPort,ipaddr_t dstIp, uint16 dstPort,ipaddr_t naptIp, uint16 naptPort,uint8 protocol,uint8 isUpstream) { int32 retval = FAILED; unsigned long flags; spin_lock_irqsave(&l4napt_spinlock,flags); if(isUpstream) retval = _rtl865x_lookupNaptConnection(scrIp,scrPort,naptIp,naptPort,dstIp,dstPort,protocol,1); else retval = _rtl865x_lookupNaptConnection(naptIp,naptPort,dstIp,dstPort,scrIp,scrPort,protocol,0); spin_unlock_irqrestore(&l4napt_spinlock,flags); return retval; } int32 rtl865x_flushNapt(void) { uint32 i; unsigned long flags; for(i=0;i<RTL8676_TCPUDPTBL_SIZE_HW;i++) { spin_lock_irqsave(&l4napt_spinlock,flags); if(NAT_INUSE(&nat_tbl.nat_bucket[i])) { #ifdef CONFIG_RTL_HW_QOS_SUPPORT if(nat_tbl.nat_bucket[i].flags&NAT_PRIOTIY_VALID) rtl865x_qosPriorityMappingDeRef(nat_tbl.nat_bucket[i].priority); #endif nat_tbl.freeHWEntryNum++; MAX_NAPT_HW_NUMBER--; memset(&nat_tbl.nat_bucket[i], 0, sizeof(struct nat_entry)); } rtl8651_delAsicNaptTcpUdpTable(i, i); spin_unlock_irqrestore(&l4napt_spinlock,flags); } return SUCCESS; } int32 rtl865x_delNaptByIdx(int idx) { unsigned long flags; spin_lock_irqsave(&l4napt_spinlock,flags); if(NAT_INUSE(&nat_tbl.nat_bucket[idx])) { #ifdef CONFIG_RTL_HW_QOS_SUPPORT if(nat_tbl.nat_bucket[idx].flags&NAT_PRIOTIY_VALID) rtl865x_qosPriorityMappingDeRef(nat_tbl.nat_bucket[idx].priority); #endif nat_tbl.freeHWEntryNum++; MAX_NAPT_HW_NUMBER--; memset(&nat_tbl.nat_bucket[idx], 0, sizeof(struct nat_entry)); } rtl8651_delAsicNaptTcpUdpTable(idx, idx); spin_unlock_irqrestore(&l4napt_spinlock,flags); return SUCCESS; } #ifdef CONFIG_RTL_HW_QOS_SUPPORT static int32 rtl865x_naptCallbackFn_for_closeIPQos(void *param) { uint32 i; for(i=0;i<RTL8676_TCPUDPTBL_SIZE_HW;i++) { if(NAT_INUSE(&nat_tbl.nat_bucket[i])) { struct nat_entry* entry = &nat_tbl.nat_bucket[i]; if(entry->flags&NAT_PRIOTIY_VALID) { rtl865x_tblAsicDrv_naptTcpUdpParam_t asic_nat; int32 idx= (entry->flags&NAT_INBOUND)?entry->in:entry->out; rtl8651_getAsicNaptTcpUdpTable(idx, &asic_nat); asic_nat.priority = 0; asic_nat.priValid = FALSE; rtl8651_setAsicNaptTcpUdpTable(1, idx, &asic_nat); rtl865x_qosPriorityMappingDeRef(entry->priority); CLR_NAT_FLAGS(entry, NAT_PRIOTIY_VALID); } } } return EVENT_CONTINUE_EXECUTE; } #endif int32 rtl865x_setNatEnhancedHash1(int32 enable) { if(enable){ _rtl8651_enableEnhancedHash1(); WRITE_MEM32( SWTCR1, READ_MEM32( SWTCR1 ) & (~EnNAP8651B) ); }else{ _rtl8651_disableEnhancedHash1(); WRITE_MEM32( SWTCR1, READ_MEM32( SWTCR1 ) | EnNAP8651B ); /*for fin and ret to cpu*/ } rtl865x_enableEnhancehash1=enable; return SUCCESS; } /* @func int32 | rtl865x_setNatFourWay |enable 4way hash algorithm. @parm int32 | enable | enable or disable. @rvalue SUCCESS | success. @comm default is enable in system. */ int32 rtl865x_setNatFourWay(int32 enable) { _set4WayHash(enable); rtl865x_enableNaptFourWay=enable; return SUCCESS; } /* @func int32 | rtl865x_nat_init |initialize napt table. @rvalue SUCCESS | success. @comm */ int32 rtl865x_nat_init(void) { int32 retval = FAILED; #ifdef CONFIG_RTL_HW_QOS_SUPPORT rtl865x_event_Param_t eventParam; #endif retval = _rtl865x_nat_init(); rtl865x_setNatFourWay(TRUE); #ifdef CONFIG_RTL_HW_QOS_SUPPORT eventParam.eventLayerId=DEFAULT_LAYER2_EVENT_LIST_ID; eventParam.eventId=EVENT_FLUSH_QOSRULE; eventParam.eventPriority=0; eventParam.event_action_fn=rtl865x_naptCallbackFn_for_closeIPQos; rtl865x_registerEvent(&eventParam); #endif return retval; } int32 rtl865x_nat_reinit(void) { return rtl865x_nat_init(); } /* return 0: dst ip is in use -1: dst ip is in use by aging out napt connection or not in use by any napt connection */ int32 rtl865x_checkNaptConnection(ipaddr_t ip) //check if dst ip is in use by napt connection { int i; struct nat_entry *natEntryPtr; for(i=0; i<RTL8676_TCPUDPTBL_SIZE_HW; i++) { natEntryPtr= &nat_tbl.nat_bucket[i]; if(NAT_INUSE(natEntryPtr)) { if(natEntryPtr->flags&NAT_OUTBOUND) { if((ip ^ natEntryPtr->rem_ip_)==0){ if(_rtl865x_lookupNaptConnection(natEntryPtr->int_ip_, natEntryPtr->int_port_, natEntryPtr->ext_ip_, natEntryPtr->ext_port_, natEntryPtr->rem_ip_, natEntryPtr->rem_port_, natEntryPtr->proto_, 1) > 0) return 0; } } if(natEntryPtr->flags&NAT_INBOUND) { if((ip ^ natEntryPtr->int_ip_)==0){ if(_rtl865x_lookupNaptConnection(natEntryPtr->int_ip_, natEntryPtr->int_port_, natEntryPtr->ext_ip_, natEntryPtr->ext_port_, natEntryPtr->rem_ip_, natEntryPtr->rem_port_, natEntryPtr->proto_, 0) > 0) return 0; } } } } return -1; } #ifdef CONFIG_RTL_PROC_DEBUG int32 rtl865x_sw_napt_seq_read(struct seq_file *s, void *v) { int i; struct nat_entry *natEntryPtr; //int len=0; printk("%s\n", "sw napt table:"); for(i=0; i<RTL8676_TCPUDPTBL_SIZE_HW; i++) { natEntryPtr= &nat_tbl.nat_bucket[i]; if(NAT_INUSE(natEntryPtr)) { if(natEntryPtr->flags&NAT_OUTBOUND) { printk("[%4d]%s:%pI4:%d---->%pI4:%d---->%pI4:%d\n flags:0x%x,outbound:(%d),inbound:(%d)", i,natEntryPtr->proto_==1?"tcp":"udp" ,&(natEntryPtr->int_ip_),natEntryPtr->int_port_, &(natEntryPtr->ext_ip_),natEntryPtr->ext_port_,&(natEntryPtr->rem_ip_),natEntryPtr->rem_port_,natEntryPtr->flags,natEntryPtr->out, natEntryPtr->in); } if(natEntryPtr->flags&NAT_INBOUND) { printk("[%4d]%s:%pI4:%d<----%pI4:%d<----%pI4:%d\n flags:0x%x, outbound:(%d), inbound:(%d)", i,natEntryPtr->proto_==1?"tcp":"udp" ,&(natEntryPtr->int_ip_),natEntryPtr->int_port_, &(natEntryPtr->ext_ip_),natEntryPtr->ext_port_,&(natEntryPtr->rem_ip_),natEntryPtr->rem_port_,natEntryPtr->flags,natEntryPtr->out,natEntryPtr->in); } #ifdef CONFIG_RTL_HW_QOS_SUPPORT if(natEntryPtr->flags & NAT_PRIOTIY_VALID) printk(" priority:%d\n",natEntryPtr->priority); else #endif printk("\n"); } } printk("total free HW entry number is %d\n",nat_tbl.freeHWEntryNum); return 0; } int32 rtl865x_sw_napt_seq_write( struct file *filp, const char *buff,unsigned long len, loff_t *off ) { char tmpbuf[64]; uint32 delIndex; char *strptr, *cmd_addr; char *tokptr; if (buff && !copy_from_user(tmpbuf, buff, len)) { tmpbuf[len] = '\0'; strptr=tmpbuf; cmd_addr = strsep(&strptr," "); if (cmd_addr==NULL) { goto errout; } if (!memcmp(cmd_addr, "flush", 5)) { rtl865x_flushNapt(); } else if (!memcmp(cmd_addr, "del", 3)) { tokptr = strsep(&strptr," "); if (tokptr==NULL) { goto errout; } delIndex=simple_strtol(tokptr, NULL, 0); if(delIndex>RTL8676_TCPUDPTBL_SIZE_HW) { printk("error input!\n"); return len; } if(NAT_INUSE(&nat_tbl.nat_bucket[delIndex])) { rtl8651_delAsicNaptTcpUdpTable(delIndex, delIndex); nat_tbl.freeHWEntryNum++; MAX_NAPT_HW_NUMBER--; memset(&nat_tbl.nat_bucket[delIndex], 0, sizeof(struct nat_entry)); } } else { goto errout; } } else { errout: return len; } return len; } #endif