/* * IPv6 fastpath core function. * Author ql_xu * * ==FILEVERSION 20130316== * ============== modification revision ============== * 20130318 modify version to beta v0.01 * 20130319 entry_path->conn maybe NULL, support the case when NF_CONNTRACK_IPV6 is closed. * */ #include <linux/fs.h> #include <linux/seq_file.h> #include "ip6_fastpath_core.h" #define MODULE_NAME "Realtek SD5-FastPath" #define MODULE_VERSION_FP "Realtek Ipv6FastPath-betaV0.01" enum { ST_CONN_UNREP=0, ST_CONN_ESTABLISH, ST_PATH_EXIST }; enum { FPTYPE_PUREROUTING=0, FPTYPE_SNAT=1, FPTYPE_SNPT=2, FPTYPE_DNAT=4, FPTYPE_DNPT=8, FPTYPE_SNAPT=0x3, FPTYPE_DNAPT=0xC }; #ifndef CONFIG_REMOTE_ADSL_PHY int ip6_fp_on=1; #else int ip6_fp_on=0; #endif #define PATH_ENTRY_AGEING_TIME (90*HZ) //1.5min #define UDP_ELAPSE_TIME (20*100) //20s =>n tick #define DEF_ELAPSE_TIME (1*60*100) //1min => n tick #define DEBUG_PROCFILE /* Create ProcFile for debug */ extern unsigned long volatile jiffies; #if !defined(CONFIG_NF_CONNTRACK_IPV6) struct timer_list ip6_fp_timer; #endif /* ==================================================================================================== */ static rwlock_t ip6_fp_napt_lock; static rwlock_t ip6_fp_path_lock; struct Ip6_Napt_Table { CTAILQ_HEAD(Ip6_Napt_list_entry_head, Ip6_Napt_List_Entry) list[IP6_NAPT_TABLE_LIST_MAX]; }; CTAILQ_HEAD(Ip6_Napt_list_inuse_head, Ip6_Napt_List_Entry) ip6_napt_list_inuse; CTAILQ_HEAD(Ip6_Napt_list_free_head, Ip6_Napt_List_Entry) ip6_napt_list_free; __DRAM struct Ip6_Napt_Table *ip6_table_napt; struct Ip6_Path_Table { CTAILQ_HEAD(Ip6_Path_list_entry_head, Ip6_Path_List_Entry) list[IP6_PATH_TABLE_LIST_MAX]; }; CTAILQ_HEAD(Ip6_Path_list_inuse_head, Ip6_Path_List_Entry) ip6_path_list_inuse; CTAILQ_HEAD(Ip6_Path_list_free_head, Ip6_Path_List_Entry) ip6_path_list_free; __DRAM struct Ip6_Path_Table *ip6_table_path; static int isIpv6AlgConn(struct ipv6hdr *ip6hdr, __u16 sport, __u16 dport, __u8 protocol); /* ==================================================================================================== */ static __u32 Ip6_FastPath_Hash_NAPT_Entry(struct in6_addr *intIp,__u32 intPort, struct in6_addr *extIp, __u32 extPort, struct in6_addr *remIp, __u32 remPort) { __u32 hash = 0; int i; for (i=0; i<8; i++) { hash ^= intIp->s6_addr16[i]; } for (i=0; i<8; i++) { hash ^= extIp->s6_addr16[i]; } for (i=0; i<8; i++) { hash ^= remIp->s6_addr16[i]; } hash ^= intPort; hash ^= extPort; hash ^= remPort; return (IP6_NAPT_TABLE_LIST_MAX-1) & (hash ^ (hash >> 11)); } inline static __u32 Ip6_FastPath_Hash_PATH_Entry(struct in6_addr *sip, __u32 sport, struct in6_addr *dip, __u32 dport, __u16 proto) { register __u32 hash = 0; int i; for (i=0; i<8; i++) { hash ^= sip->s6_addr16[i]; } for (i=0; i<8; i++) { hash ^= dip->s6_addr16[i]; } hash ^= sport; hash ^= dport; hash ^= proto; return (IP6_PATH_TABLE_LIST_MAX-1) & (hash ^ (hash >> 11)); } /* ==================================================================================================== */ enum LR_RESULT ip6_fastpath_addNaptConnection(void *ct, struct IP6_FP_NAPT_entry *napt, int state) { struct Ip6_Napt_List_Entry *ep; struct Ip6_Napt_List_Entry *entry_napt; struct nf_conn *nf_ct; #if IP6_FP_DBG char intipStr[INET6_ADDRSTRLEN], extipStr[INET6_ADDRSTRLEN], remipStr[INET6_ADDRSTRLEN]; #endif __u8 *proto; __u32 hash; if (isIpv6AlgConn(NULL, napt->intPort, napt->remPort, napt->protocol)) return LR_FAILED; if( napt->protocol == IPPROTO_TCP ) { proto = (__u8 *)"TCP"; } else if( napt->protocol == IPPROTO_UDP ) { proto = (__u8 *)"UDP"; } else { proto = (__u8 *)"unknow"; } hash = Ip6_FastPath_Hash_NAPT_Entry(&napt->intIp, napt->intPort, &napt->extIp, napt->extPort, &napt->remIp, napt->remPort); #if 1 IP6_DEBUGP_SYS("%s: P=%s int=%s/%d ext=%s/%d rem=%s/%d (H=%u, Ha=%u, Hb=%u),state=%x\n", __func__, proto, ip6_sprintf(intipStr, &napt->intIp), napt->intPort, ip6_sprintf(extipStr, &napt->extIp), napt->extPort, ip6_sprintf(remipStr, &napt->remIp), napt->remPort, hash, Ip6_FastPath_Hash_PATH_Entry(&napt->intIp, napt->intPort, &napt->remIp, napt->remPort, napt->protocol), Ip6_FastPath_Hash_PATH_Entry(&napt->remIp, napt->remPort, &napt->extIp, napt->extPort, napt->protocol), state); #endif /* Lookup */ write_lock_bh(&ip6_fp_napt_lock); CTAILQ_FOREACH(ep, &ip6_table_napt->list[hash], napt_link) { if ((ep->protocol == napt->protocol) && ipv6_addr_equal(&ep->intIp, &napt->intIp) && (ep->intPort == napt->intPort) && ipv6_addr_equal(&ep->extIp, &napt->extIp) && (ep->extPort == napt->extPort) && ipv6_addr_equal(&ep->remIp, &napt->remIp) && (ep->remPort == napt->remPort)) { IP6_DEBUGP_SYS("%s: ERROR - the entry already exist! \n", __func__); if (ep->state != 1) { if (state==1) {//current is the establish conntrack. if (ep->state == 2) {//PATH already exist. ep->state = 1; write_unlock_bh(&ip6_fp_napt_lock); return LR_SUCCESS; } else { ep->state = 1; entry_napt = ep; write_unlock_bh(&ip6_fp_napt_lock); goto ADD_PATH; } } else { if (ep->state == 2){ write_unlock_bh(&ip6_fp_napt_lock); return LR_SUCCESS; } if ((++(ep->refcnt)) >= 10) { ep->state = 2; entry_napt = ep; write_unlock_bh(&ip6_fp_napt_lock); goto ADD_PATH; } } } write_unlock_bh(&ip6_fp_napt_lock); return LR_SUCCESS; } } if (state==0) { //printk("only add napt entry.\n"); if(!CTAILQ_EMPTY(&ip6_napt_list_free) && !CTAILQ_EMPTY(&ip6_path_list_free)) { entry_napt = CTAILQ_FIRST(&ip6_napt_list_free); entry_napt->protocol = napt->protocol; entry_napt->intIp = napt->intIp; entry_napt->intPort = napt->intPort; entry_napt->extIp = napt->extIp; entry_napt->extPort = napt->extPort; entry_napt->remIp = napt->remIp; entry_napt->remPort = napt->remPort; entry_napt->state = 0; entry_napt->refcnt = 0; entry_napt->valid = 0xff; CTAILQ_REMOVE(&ip6_napt_list_free, entry_napt, tqe_link); CTAILQ_INSERT_TAIL(&ip6_napt_list_inuse, entry_napt, tqe_link); CTAILQ_INSERT_TAIL(&ip6_table_napt->list[hash], entry_napt, napt_link); } write_unlock_bh(&ip6_fp_napt_lock); return LR_SUCCESS; } if(!CTAILQ_EMPTY(&ip6_napt_list_free) && !CTAILQ_EMPTY(&ip6_path_list_free)) { entry_napt = CTAILQ_FIRST(&ip6_napt_list_free); entry_napt->protocol = napt->protocol; entry_napt->intIp = napt->intIp; entry_napt->intPort = napt->intPort; entry_napt->extIp = napt->extIp; entry_napt->extPort = napt->extPort; entry_napt->remIp = napt->remIp; entry_napt->remPort = napt->remPort; entry_napt->state = 1; entry_napt->valid = 0xff; CTAILQ_REMOVE(&ip6_napt_list_free, entry_napt, tqe_link); CTAILQ_INSERT_TAIL(&ip6_napt_list_inuse, entry_napt, tqe_link); CTAILQ_INSERT_TAIL(&ip6_table_napt->list[hash], entry_napt, napt_link); write_unlock_bh(&ip6_fp_napt_lock); goto ADD_PATH; } else { IP6_DEBUGP_SYS("%s: ERROR - Napt_list_free is empty! \n", __func__); write_unlock_bh(&ip6_fp_napt_lock); return LR_FAILED; } write_unlock_bh(&ip6_fp_napt_lock); ADD_PATH: /* add Path Table Entry */ if (1) { struct Ip6_Path_List_Entry *entry_path; if (CTAILQ_EMPTY(&ip6_path_list_free)) { entry_napt->state = 0; return LR_SUCCESS; } //upstream /* course = 1 (Outbound) */ write_lock_bh(&ip6_fp_path_lock); hash = Ip6_FastPath_Hash_PATH_Entry(&napt->intIp, napt->intPort, &napt->remIp, napt->remPort, napt->protocol); entry_path = CTAILQ_FIRST(&ip6_path_list_free); entry_path->pps = 0; entry_path->protocol = &entry_napt->protocol; entry_path->in_sIp = &entry_napt->intIp; entry_path->in_sPort = &entry_napt->intPort; entry_path->in_dIp = &entry_napt->remIp; entry_path->in_dPort = &entry_napt->remPort; entry_path->out_sIp = &entry_napt->extIp; entry_path->out_sPort = &entry_napt->extPort; entry_path->out_dIp = &entry_napt->remIp; entry_path->out_dPort = &entry_napt->remPort; entry_path->out_ifname = NULL; entry_path->course = 1; entry_path->valid = 0xff; entry_path->dst = NULL; entry_path->mark = 0; entry_path->type = 0; /* Init: Normal (Only Routing) */ entry_path->last_refresh_time = jiffies; entry_path->fdb_ageing = jiffies; entry_path->conn = ct; if (!ipv6_addr_equal(entry_path->in_sIp, entry_path->out_sIp)) { entry_path->type |= FPTYPE_SNAT; /* SNAT */ } if (*entry_path->in_sPort != *entry_path->out_sPort) { entry_path->type |= FPTYPE_SNPT; /* SNPT */ } nf_ct = (struct nf_conn *)ct; memcpy(&entry_path->orig_tuple, &nf_ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(entry_path->orig_tuple)); CTAILQ_REMOVE(&ip6_path_list_free, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_path_list_inuse, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_table_path->list[hash], entry_path, path_link); //downstream if (CTAILQ_EMPTY(&ip6_path_list_free)) { entry_path->valid = 0x00; CTAILQ_REMOVE(&ip6_table_path->list[hash], entry_path, path_link); CTAILQ_REMOVE(&ip6_path_list_inuse, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_path_list_free, entry_path, tqe_link); entry_napt->state = 0; write_unlock_bh(&ip6_fp_path_lock); return LR_SUCCESS; } /* course = 2 (Inbound) */ hash = Ip6_FastPath_Hash_PATH_Entry(&napt->remIp, napt->remPort, &napt->extIp, napt->extPort, napt->protocol); entry_path = CTAILQ_FIRST(&ip6_path_list_free); entry_path->pps = 0; entry_path->protocol = &entry_napt->protocol; entry_path->in_sIp = &entry_napt->remIp; entry_path->in_sPort = &entry_napt->remPort; entry_path->in_dIp = &entry_napt->extIp; entry_path->in_dPort = &entry_napt->extPort; entry_path->out_sIp = &entry_napt->remIp; entry_path->out_sPort = &entry_napt->remPort; entry_path->out_dIp = &entry_napt->intIp; entry_path->out_dPort = &entry_napt->intPort; entry_path->out_ifname = NULL; entry_path->course = 2; entry_path->valid = 0xff; entry_path->dst = NULL; entry_path->type = 0; /* Init: Normal (Only Routing) */ entry_path->mark = 0; entry_path->last_refresh_time = jiffies; entry_path->fdb_ageing = jiffies; entry_path->conn = ct; if (!ipv6_addr_equal(entry_path->in_dIp, entry_path->out_dIp)) { entry_path->type |= FPTYPE_DNAT; /* DNAT */ } if (*entry_path->in_dPort != *entry_path->out_dPort) { entry_path->type |= FPTYPE_DNPT; /* DNPT */ } nf_ct = (struct nf_conn *)ct; memcpy(&entry_path->orig_tuple, &nf_ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(entry_path->orig_tuple)); CTAILQ_REMOVE(&ip6_path_list_free, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_path_list_inuse, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_table_path->list[hash], entry_path, path_link); write_unlock_bh(&ip6_fp_path_lock); } return LR_SUCCESS; } enum LR_RESULT ip6_fastpath_delNaptConnection(struct IP6_FP_NAPT_entry *napt) { struct Ip6_Napt_List_Entry *ep; #if IP6_FP_DBG char intipStr[INET6_ADDRSTRLEN], extipStr[INET6_ADDRSTRLEN], remipStr[INET6_ADDRSTRLEN]; #endif __u8 *proto; __u32 hash1; if( napt->protocol == IPPROTO_TCP ) { proto = (__u8 *)"TCP"; } else if( napt->protocol == IPPROTO_UDP ) { proto = (__u8 *)"UDP"; } else { proto = (__u8 *)"unknow"; } hash1 = Ip6_FastPath_Hash_NAPT_Entry(&napt->intIp, napt->intPort, &napt->extIp, napt->extPort, &napt->remIp, napt->remPort); IP6_DEBUGP_API("%s: P=%s int=%s/%u ext=%s/%u rem=%s:%u \n", __func__, proto, ip6_sprintf(intipStr, &napt->intIp), napt->intPort, ip6_sprintf(extipStr, &napt->extIp), napt->extPort, ip6_sprintf(remipStr, &napt->remIp), napt->remPort); /* Lookup */ write_lock_bh(&ip6_fp_napt_lock); CTAILQ_FOREACH(ep, &ip6_table_napt->list[hash1], napt_link) { if ((ep->protocol == napt->protocol) && ipv6_addr_equal(&ep->intIp, &napt->intIp) && (ep->intPort == napt->intPort) && ipv6_addr_equal(&ep->extIp, &napt->extIp) && (ep->extPort == napt->extPort) && ipv6_addr_equal(&ep->remIp, &napt->remIp) && (ep->remPort == napt->remPort)) { ep->valid = 0x00; CTAILQ_REMOVE(&ip6_table_napt->list[hash1], ep, napt_link); CTAILQ_REMOVE(&ip6_napt_list_inuse, ep, tqe_link); CTAILQ_INSERT_TAIL(&ip6_napt_list_free, ep, tqe_link); /* del Path Table Entry */ if (1) { __u32 hash2; struct Ip6_Path_List_Entry *entry_path; /* course = 1 (Outbound) */ write_lock_bh(&ip6_fp_path_lock); hash2 = Ip6_FastPath_Hash_PATH_Entry(&napt->intIp, napt->intPort, &napt->remIp, napt->remPort, napt->protocol); CTAILQ_FOREACH(entry_path, &ip6_table_path->list[hash2], path_link) { if ((entry_path->protocol == &ep->protocol) && (entry_path->course == 1)) { entry_path->valid = 0x00; if( entry_path->dst ) { dst_release(entry_path->dst); } CTAILQ_REMOVE(&ip6_table_path->list[hash2], entry_path, path_link); CTAILQ_REMOVE(&ip6_path_list_inuse, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_path_list_free, entry_path, tqe_link); break; } } /* course = 2 (Inbound) */ hash2 = Ip6_FastPath_Hash_PATH_Entry(&napt->remIp, napt->remPort, &napt->extIp, napt->extPort, napt->protocol); CTAILQ_FOREACH(entry_path, &ip6_table_path->list[hash2], path_link) { if ((entry_path->protocol == &ep->protocol) && (entry_path->course == 2)) { entry_path->valid = 0x00; if( entry_path->dst ) { dst_release(entry_path->dst); } CTAILQ_REMOVE(&ip6_table_path->list[hash2], entry_path, path_link); CTAILQ_REMOVE(&ip6_path_list_inuse, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_path_list_free, entry_path, tqe_link); break; } } write_unlock_bh(&ip6_fp_path_lock); } write_unlock_bh(&ip6_fp_napt_lock); return LR_SUCCESS; } } write_unlock_bh(&ip6_fp_napt_lock); return LR_NONEXIST; } enum LR_RESULT ip6_fastpath_updateNaptConnection(struct IP6_FP_NAPT_entry *napt, unsigned int mark) { struct Ip6_Napt_List_Entry *ep; #if IP6_FP_DBG char intipStr[INET6_ADDRSTRLEN], extipStr[INET6_ADDRSTRLEN], remipStr[INET6_ADDRSTRLEN]; #endif __u8 *proto; __u32 hash1; if( napt->protocol == IPPROTO_TCP ) { proto = (__u8 *)"TCP"; } else if( napt->protocol == IPPROTO_UDP ) { proto = (__u8 *)"UDP"; } else { proto = (__u8 *)"unknow"; } hash1 = Ip6_FastPath_Hash_NAPT_Entry(&napt->intIp, napt->intPort, &napt->extIp, napt->extPort, &napt->remIp, napt->remPort); /* Lookup */ read_lock_bh(&ip6_fp_napt_lock); CTAILQ_FOREACH(ep, &ip6_table_napt->list[hash1], napt_link) { if ((ep->protocol == napt->protocol) && ipv6_addr_equal(&ep->intIp, &napt->intIp) && (ep->intPort == napt->intPort) && ipv6_addr_equal(&ep->extIp, &napt->extIp) && (ep->extPort == napt->extPort) && ipv6_addr_equal(&ep->remIp, &napt->remIp) && (ep->remPort == napt->remPort)) { IP6_DEBUGP_API("%s: P=%s int=%s/%u ext=%s/%u rem=%s/%u mark=%x\n", __func__, proto, ip6_sprintf(intipStr, &napt->intIp), napt->intPort, ip6_sprintf(extipStr, &napt->extIp), napt->extPort, ip6_sprintf(remipStr, &napt->remIp), napt->remPort, mark); /* update Path Table Entry */ if (1) { __u32 hash2; struct Ip6_Path_List_Entry *entry_path; /* course = 1 (Outbound) */ hash2 = Ip6_FastPath_Hash_PATH_Entry(&napt->intIp, napt->intPort, &napt->remIp, napt->remPort, napt->protocol); write_lock_bh(&ip6_fp_path_lock); CTAILQ_FOREACH(entry_path, &ip6_table_path->list[hash2], path_link) { if ((entry_path->protocol == &ep->protocol) && (entry_path->course == 1)) { entry_path->mark = mark; break; } } write_unlock_bh(&ip6_fp_path_lock); #if 0//prepare for downstream IP QoS /* course = 2 (Inbound) */ hash = Ip6_FastPath_Hash_PATH_Entry(&napt->remIp, napt->remPort, &napt->extIp, napt->extPort, napt->protocol); CTAILQ_FOREACH(entry_path, &ip6_table_path->list[hash], path_link) { if ((entry_path->protocol == &ep->protocol) && (entry_path->course == 2)) { entry_path->mark = mark; break; } } #endif } read_unlock_bh(&ip6_fp_napt_lock); return LR_SUCCESS; } } read_unlock_bh(&ip6_fp_napt_lock); return LR_NONEXIST; } #if !defined(CONFIG_NF_CONNTRACK_IPV6) static int reclaimOldFastpathConn(void) { struct Ip6_Napt_List_Entry *napt_ep; __u32 hash; int path_exist=0; /* Lookup */ write_lock_bh(&ip6_fp_napt_lock); CTAILQ_FOREACH(napt_ep, &ip6_napt_list_inuse, tqe_link) { /* del Path Table Entry */ struct Ip6_Path_List_Entry *entry_path; /* course = 1 (Outbound) */ hash = Ip6_FastPath_Hash_PATH_Entry(&napt_ep->intIp, napt_ep->intPort, &napt_ep->remIp, napt_ep->remPort, napt_ep->protocol); write_lock_bh(&ip6_fp_path_lock); CTAILQ_FOREACH(entry_path, &ip6_table_path->list[hash], path_link) { if ((entry_path->protocol == &napt_ep->protocol) && (entry_path->course == 1)) { if ((jiffies - entry_path->last_refresh_time) >= PATH_ENTRY_AGEING_TIME) { entry_path->valid = 0x00; if( entry_path->dst ) { dst_release(entry_path->dst); } CTAILQ_REMOVE(&ip6_table_path->list[hash], entry_path, path_link); CTAILQ_REMOVE(&ip6_path_list_inuse, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_path_list_free, entry_path, tqe_link); } else path_exist = 1; break; } } /* course = 2 (Inbound) */ hash = Ip6_FastPath_Hash_PATH_Entry(&napt_ep->remIp, napt_ep->remPort, &napt_ep->extIp, napt_ep->extPort, napt_ep->protocol); CTAILQ_FOREACH(entry_path, &ip6_table_path->list[hash], path_link) { if ((entry_path->protocol == &napt_ep->protocol) && (entry_path->course == 2)) { if ((jiffies - entry_path->last_refresh_time) >= PATH_ENTRY_AGEING_TIME) { entry_path->valid = 0x00; if( entry_path->dst ) { dst_release(entry_path->dst); } CTAILQ_REMOVE(&ip6_table_path->list[hash], entry_path, path_link); CTAILQ_REMOVE(&ip6_path_list_inuse, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_path_list_free, entry_path, tqe_link); } else path_exist = 1; break; } } if (0 == path_exist) { napt_ep->valid = 0x00; hash = Ip6_FastPath_Hash_NAPT_Entry(&napt_ep->intIp, napt_ep->intPort, &napt_ep->extIp, napt_ep->extPort, &napt_ep->remIp, napt_ep->remPort); CTAILQ_REMOVE(&ip6_table_napt->list[hash], napt_ep, napt_link); CTAILQ_REMOVE(&ip6_napt_list_inuse, napt_ep, tqe_link); CTAILQ_INSERT_TAIL(&ip6_napt_list_free, napt_ep, tqe_link); break; } write_unlock_bh(&ip6_fp_path_lock); } write_unlock_bh(&ip6_fp_napt_lock); return 1; } static void ip6_fastpath_timeout(unsigned long data) { reclaimOldFastpathConn(); mod_timer(&ip6_fp_timer, jiffies + 60*HZ); } static void init_ip6_fastpath_time(void) { init_timer(&ip6_fp_timer); ip6_fp_timer.function = ip6_fastpath_timeout; ip6_fp_timer.expires = jiffies + 60*HZ; add_timer(&ip6_fp_timer); } enum LR_RESULT ip6_fastpath_addNaptConnectionWithoutNFV6(struct IP6_FP_NAPT_entry *napt, int course) { struct Ip6_Napt_List_Entry *ep; struct Ip6_Napt_List_Entry *entry_napt; #if IP6_FP_DBG //char intipStr[INET6_ADDRSTRLEN], extipStr[INET6_ADDRSTRLEN], remipStr[INET6_ADDRSTRLEN]; #endif __u8 *proto; __u32 hash; if (isIpv6AlgConn(NULL, napt->intPort, napt->remPort, napt->protocol)) return LR_FAILED; if( napt->protocol == IPPROTO_TCP ) { proto = (__u8 *)"TCP"; } else if( napt->protocol == IPPROTO_UDP ) { proto = (__u8 *)"UDP"; } else { proto = (__u8 *)"unknow"; } hash = Ip6_FastPath_Hash_NAPT_Entry(&napt->intIp, napt->intPort, &napt->extIp, napt->extPort, &napt->remIp, napt->remPort); #if 0 IP6_DEBUGP_SYS("%s: P=%s int=%s/%d ext=%s/%d rem=%s/%d (H=%u, Ha=%u, Hb=%u),course=%x\n", __func__, proto, ip6_sprintf(intipStr, &napt->intIp), napt->intPort, ip6_sprintf(extipStr, &napt->extIp), napt->extPort, ip6_sprintf(remipStr, &napt->remIp), napt->remPort, hash, Ip6_FastPath_Hash_PATH_Entry(&napt->intIp, napt->intPort, &napt->remIp, napt->remPort, napt->protocol), Ip6_FastPath_Hash_PATH_Entry(&napt->remIp, napt->remPort, &napt->extIp, napt->extPort, napt->protocol), course); #endif /* Lookup */ write_lock_bh(&ip6_fp_napt_lock); CTAILQ_FOREACH(ep, &ip6_table_napt->list[hash], napt_link) { if ((ep->protocol == napt->protocol) && ipv6_addr_equal(&ep->intIp, &napt->intIp) && (ep->intPort == napt->intPort) && ipv6_addr_equal(&ep->extIp, &napt->extIp) && (ep->extPort == napt->extPort) && ipv6_addr_equal(&ep->remIp, &napt->remIp) && (ep->remPort == napt->remPort)) { //IP6_DEBUGP_SYS("%s: ERROR - the entry already exist! \n", __func__); if (ep->state == ST_PATH_EXIST) { write_unlock_bh(&ip6_fp_napt_lock); return LR_SUCCESS; } else if (ep->state != ST_CONN_ESTABLISH) { if (ep->course != course) {//current is the establish conntrack. ep->state = ST_CONN_ESTABLISH; entry_napt = ep; write_unlock_bh(&ip6_fp_napt_lock); goto ADD_PATH; } else { if ((++(ep->refcnt)) >= 10) { ep->state = ST_PATH_EXIST; entry_napt = ep; write_unlock_bh(&ip6_fp_napt_lock); goto ADD_PATH; } } } write_unlock_bh(&ip6_fp_napt_lock); return LR_SUCCESS; } } //printk("only add napt entry.\n"); if(!CTAILQ_EMPTY(&ip6_napt_list_free) && !CTAILQ_EMPTY(&ip6_path_list_free)) { entry_napt = CTAILQ_FIRST(&ip6_napt_list_free); entry_napt->protocol = napt->protocol; entry_napt->intIp = napt->intIp; entry_napt->intPort = napt->intPort; entry_napt->extIp = napt->extIp; entry_napt->extPort = napt->extPort; entry_napt->remIp = napt->remIp; entry_napt->remPort = napt->remPort; entry_napt->course = course; entry_napt->state = 0; entry_napt->refcnt = 0; entry_napt->valid = 0xff; CTAILQ_REMOVE(&ip6_napt_list_free, entry_napt, tqe_link); CTAILQ_INSERT_TAIL(&ip6_napt_list_inuse, entry_napt, tqe_link); CTAILQ_INSERT_TAIL(&ip6_table_napt->list[hash], entry_napt, napt_link); } else reclaimOldFastpathConn(); write_unlock_bh(&ip6_fp_napt_lock); return LR_SUCCESS; ADD_PATH: /* add Path Table Entry */ if (1) { struct Ip6_Path_List_Entry *entry_path; if (CTAILQ_EMPTY(&ip6_path_list_free)) { entry_napt->state = 0; return LR_SUCCESS; } //upstream write_lock_bh(&ip6_fp_path_lock); /* course = 1 (Outbound) */ hash = Ip6_FastPath_Hash_PATH_Entry(&napt->intIp, napt->intPort, &napt->remIp, napt->remPort, napt->protocol); entry_path = CTAILQ_FIRST(&ip6_path_list_free); entry_path->protocol = &entry_napt->protocol; entry_path->in_sIp = &entry_napt->intIp; entry_path->in_sPort = &entry_napt->intPort; entry_path->in_dIp = &entry_napt->remIp; entry_path->in_dPort = &entry_napt->remPort; entry_path->out_sIp = &entry_napt->extIp; entry_path->out_sPort = &entry_napt->extPort; entry_path->out_dIp = &entry_napt->remIp; entry_path->out_dPort = &entry_napt->remPort; entry_path->out_ifname = NULL; entry_path->course = 1; entry_path->valid = 0xff; entry_path->dst = NULL; entry_path->mark = 0; entry_path->type = 0; /* Init: Normal (Only Routing) */ entry_path->last_refresh_time = jiffies; entry_path->fdb_ageing = jiffies; entry_path->conn = NULL; if (!ipv6_addr_equal(entry_path->in_sIp, entry_path->out_sIp)) { entry_path->type |= FPTYPE_SNAT; /* SNAT */ } if (*entry_path->in_sPort != *entry_path->out_sPort) { entry_path->type |= FPTYPE_SNPT; /* SNPT */ } CTAILQ_REMOVE(&ip6_path_list_free, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_path_list_inuse, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_table_path->list[hash], entry_path, path_link); //downstream if (CTAILQ_EMPTY(&ip6_path_list_free)) { entry_path->valid = 0x00; CTAILQ_REMOVE(&ip6_table_path->list[hash], entry_path, path_link); CTAILQ_REMOVE(&ip6_path_list_inuse, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_path_list_free, entry_path, tqe_link); entry_napt->state = 0; write_unlock_bh(&ip6_fp_path_lock); return LR_SUCCESS; } /* course = 2 (Inbound) */ hash = Ip6_FastPath_Hash_PATH_Entry(&napt->remIp, napt->remPort, &napt->extIp, napt->extPort, napt->protocol); entry_path = CTAILQ_FIRST(&ip6_path_list_free); entry_path->protocol = &entry_napt->protocol; entry_path->in_sIp = &entry_napt->remIp; entry_path->in_sPort = &entry_napt->remPort; entry_path->in_dIp = &entry_napt->extIp; entry_path->in_dPort = &entry_napt->extPort; entry_path->out_sIp = &entry_napt->remIp; entry_path->out_sPort = &entry_napt->remPort; entry_path->out_dIp = &entry_napt->intIp; entry_path->out_dPort = &entry_napt->intPort; entry_path->out_ifname = NULL; entry_path->course = 2; entry_path->valid = 0xff; entry_path->dst = NULL; entry_path->type = 0; /* Init: Normal (Only Routing) */ entry_path->mark = 0; entry_path->last_refresh_time = jiffies; entry_path->fdb_ageing = jiffies; entry_path->conn = NULL; if (!ipv6_addr_equal(entry_path->in_dIp, entry_path->out_dIp)) { entry_path->type |= FPTYPE_DNAT; /* DNAT */ } if (*entry_path->in_dPort != *entry_path->out_dPort) { entry_path->type |= FPTYPE_DNPT; /* DNPT */ } CTAILQ_REMOVE(&ip6_path_list_free, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_path_list_inuse, entry_path, tqe_link); CTAILQ_INSERT_TAIL(&ip6_table_path->list[hash], entry_path, path_link); write_unlock_bh(&ip6_fp_path_lock); } return LR_SUCCESS; } enum LR_RESULT ip6_fastpath_delNaptConnectionWithoutNFV6(struct IP6_FP_NAPT_entry *napt, int course) { return ip6_fastpath_delNaptConnection(napt); } #endif /*delete concerning rules to filter chain*/ int Ip6_clearFastPathEntry(void) { struct Ip6_Path_List_Entry *path_ep; struct Ip6_Napt_List_Entry *napt_ep; __u32 hash; REMOVE_PATH: CTAILQ_FOREACH(path_ep, &ip6_path_list_inuse, tqe_link) { path_ep->valid = 0; if (path_ep->dst) dst_release(path_ep->dst); hash = Ip6_FastPath_Hash_PATH_Entry(path_ep->in_sIp, *path_ep->in_sPort, path_ep->in_dIp, *path_ep->in_dPort, *path_ep->protocol); CTAILQ_REMOVE(&ip6_table_path->list[hash], path_ep, path_link); CTAILQ_REMOVE(&ip6_path_list_inuse, path_ep, tqe_link); CTAILQ_INSERT_TAIL(&ip6_path_list_free, path_ep, tqe_link); goto REMOVE_PATH; } REMOVE_NAPT: CTAILQ_FOREACH(napt_ep, &ip6_napt_list_inuse, tqe_link) { napt_ep->valid = 0; hash = Ip6_FastPath_Hash_NAPT_Entry(&napt_ep->intIp, napt_ep->intPort, &napt_ep->extIp, napt_ep->extPort, &napt_ep->remIp, napt_ep->remPort); CTAILQ_REMOVE(&ip6_table_napt->list[hash], napt_ep, napt_link); CTAILQ_REMOVE(&ip6_napt_list_inuse, napt_ep, tqe_link); CTAILQ_INSERT_TAIL(&ip6_napt_list_free, napt_ep, tqe_link); goto REMOVE_NAPT; } return 1; } /* ==================================================================================================== */ /* cached hardware header; allow for machine alignment needs. */ #define HH_DATA_MOD 16 #define HH_DATA_ALIGN(__len) \ (((__len)+(HH_DATA_MOD-1))&~(HH_DATA_MOD - 1)) #if 0//move to ip6_fastpath_api.c static inline int Ip6_packetParser(struct ipv6hdr *ip6hdr, __u8 *protoType, __u16 *sport, __u16 *dport, __u8 **l4hdr) { __u8 *datap; int nxthdr; nxthdr = ip6hdr->nexthdr; datap = (__u8 *)(ip6hdr + 1); while (nxthdr != IPPROTO_NONE) { struct ipv6_opt_hdr *opt; *protoType = nxthdr; if ((nxthdr == IPPROTO_TCP) || (nxthdr == IPPROTO_UDP)) { *l4hdr = datap; *sport = ntohs(*(__u16 *)datap); *dport = ntohs(*(__u16 *)(datap+2)); break; } else if ((nxthdr == IPPROTO_ROUTING) || (nxthdr == IPPROTO_DSTOPTS)) {//routing header or target option header opt = (struct ipv6_opt_hdr *)datap; nxthdr = opt->nexthdr; datap += ((opt->hdrlen+1)<<3); } else if (nxthdr == IPPROTO_AH) { opt = (struct ipv6_opt_hdr *)datap; nxthdr = opt->nexthdr; datap += ((opt->hdrlen+2)<<2); } #if 0 /* any packet with hop-by-hop extension header can not pass fastpath, so ignore such packet */ else if ((nxthdr == IPPROTO_HOPOPTS) || (nxthdr == IPPROTO_ICMPV6) || (nxthdr == IPPROTO_FRAGMENT) || (nxthdr == IPPROTO_ESP) ) { break; } #endif else break; } return 1; } #endif static int isIpv6AlgConn(struct ipv6hdr *ip6hdr, __u16 sport, __u16 dport, __u8 protocol) { /**************** ftp v6 alg**********************/ if(protocol==IPPROTO_TCP && (sport==21 || dport==21)) return 1; /* TODO: add more alg here */ return 0; } int Ip6_FastPath_Process(void * pskb, struct ipv6hdr *ip6hdr) { struct in6_addr *sip, *dip; #if IP6_FP_DBG char sipStr[INET6_ADDRSTRLEN], dipStr[INET6_ADDRSTRLEN]; #endif __u8 protocol=0; __u16 sPort=0, dPort=0; __u8 *l4hdr=NULL; #ifdef CONFIG_PPP int xmitOnPPP=0; #endif void *dst; __u8 course; if (ip6hdr->nexthdr == IPPROTO_HOPOPTS) return 0; sip = &ip6hdr->saddr; dip = &ip6hdr->daddr; Ip6_packetParser(ip6hdr, &protocol, &sPort, &dPort, &l4hdr); if (isIpv6AlgConn(ip6hdr, sPort, dPort, protocol)) return 0; write_lock_bh(&ip6_fp_path_lock); switch (protocol) { case IPPROTO_TCP: { struct tcphdr *tcph; __u32 hash; struct Ip6_Path_List_Entry *entry_path; tcph = (struct tcphdr*)l4hdr; //IP6_DEBUGP_PKT("==>> [%08X] SIP: %s/%u -> DIP: %s/%u <TCP>\n", // tcph->check, // ip6_sprintf(sipStr, sip), sPort, // ip6_sprintf(dipStr, dip), dPort); if (tcph == NULL){ write_unlock_bh(&ip6_fp_path_lock); return 0; } if (tcph->fin || tcph->rst || tcph->syn){ write_unlock_bh(&ip6_fp_path_lock); return 0; } hash = Ip6_FastPath_Hash_PATH_Entry(sip, sPort, dip, dPort, protocol); CTAILQ_FOREACH(entry_path, &ip6_table_path->list[hash], path_link) { if ((*entry_path->in_sPort == sPort) && (*entry_path->in_dPort == dPort) && ipv6_addr_equal(entry_path->in_sIp, sip) && ipv6_addr_equal(entry_path->in_dIp, dip) && (*entry_path->protocol == IPPROTO_TCP)) { if (entry_path->dst == NULL) { IP6_DEBUGP_PKT("course(%d) %s(%d)->%s(%d) entry_path->dst is NULL.\n", entry_path->course, ip6_sprintf(sipStr, sip), sPort, ip6_sprintf(dipStr, dip), dPort); if (!fp_ip6route_input(pskb, ip6hdr, entry_path->out_dIp, entry_path->course)) { IP6_DEBUGP_PKT("course(%d) %s->%s can not find dst.\n", entry_path->course, ip6_sprintf(sipStr, sip), ip6_sprintf(dipStr, dip)); write_unlock_bh(&ip6_fp_path_lock); return 0; } SetFPDst(pskb, &entry_path->dst); setSkbDst(pskb, NULL); // Check if downstream dst is bridge interface (ex. br0) if (entry_path->course==2 && !(((struct dst_entry *)entry_path->dst)->dev->priv_flags & IFF_EBRIDGE)) { // downstream dst not bridged-LAN, ignore this dst printk("WARNING!! incorrect dst, ignore it!\n"); entry_path->dst = 0; write_unlock_bh(&ip6_fp_path_lock); return 0; } entry_path->out_ifname = fastpath_getdstifName(entry_path->dst); } if(needFragment(pskb, entry_path->dst)) { write_unlock_bh(&ip6_fp_path_lock); return 0; } #ifdef CONFIG_PPP if(ARPHRD_PPP == getDevTypeFromDestentry(entry_path->dst)){ IP6_DEBUGP_PKT("pppoe proxy or upstream pppoe, interface = %s\n", entry_path->out_ifname); xmitOnPPP = 1; } #endif if (isNeighCreated(entry_path->dst, pskb) #ifdef CONFIG_PPP || xmitOnPPP #endif ) { if ((entry_path->course == 1) && ((jiffies - entry_path->fdb_ageing) > br_ageing_time)) { entry_path->fdb_ageing = jiffies; ip6_fp_br_fdb_update(pskb); } #ifdef CONFIG_PPP if (!xmitOnPPP) { #endif setSkbDst(pskb, entry_path->dst); FastPathHoldDst(pskb); if (isDestLo(pskb)) goto FINISH; #ifdef CONFIG_PPP } #endif IP6_DEBUGP_PKT("FORWARD to [%s] \n", entry_path->out_ifname); switch(entry_path->type) { case FPTYPE_PUREROUTING: { /* Only Routing */ break; } case FPTYPE_SNAT: { /* SNAT */ IP6_FASTPATH_ADJUST_CHKSUM_NAT(entry_path->out_sIp, sip, tcph->check); ip6hdr->saddr = *entry_path->out_sIp; break; } case FPTYPE_SNPT: /* SNPT */ case FPTYPE_SNAPT: { /* SNAPT */ IP6_FASTPATH_ADJUST_CHKSUM_NAPT(entry_path->out_sIp, sip, *entry_path->out_sPort, tcph->source, tcph->check); ip6hdr->saddr = *entry_path->out_sIp; tcph->source = *entry_path->out_sPort; break; } case FPTYPE_DNAT: { /* DNAT */ IP6_FASTPATH_ADJUST_CHKSUM_NAT(entry_path->out_dIp, dip, tcph->check); ip6hdr->daddr = *entry_path->out_dIp; break; } case FPTYPE_DNPT: /* DNPT */ case FPTYPE_DNAPT: { /* DNAPT */ IP6_FASTPATH_ADJUST_CHKSUM_NAPT(entry_path->out_dIp, dip, *entry_path->out_dPort, tcph->dest, tcph->check); ip6hdr->daddr = *entry_path->out_dIp; tcph->dest = *entry_path->out_dPort; break; } default: { IP6_FASTPATH_ADJUST_CHKSUM_NAPT(entry_path->out_sIp, sip, *entry_path->out_sPort, tcph->source, tcph->check); IP6_FASTPATH_ADJUST_CHKSUM_NAPT(entry_path->out_dIp, dip, *entry_path->out_dPort, tcph->dest, tcph->check); ip6hdr->saddr = *entry_path->out_sIp; tcph->source = *entry_path->out_sPort; ip6hdr->daddr = *entry_path->out_dIp; tcph->dest = *entry_path->out_dPort; break; } } if ((jiffies - entry_path->last_refresh_time) > DEF_ELAPSE_TIME) { entry_path->last_refresh_time = jiffies; } dst = entry_path->dst; course = entry_path->course; entry_path->pps++; write_unlock_bh(&ip6_fp_path_lock); #ifdef CONFIG_PPP if (xmitOnPPP) #if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)||defined(CONFIG_ATP_SUPPORT_ETHUP) pppoe_proxy_output(pskb, getDevFromDestentry(dst), course, 0); #else pppoe_proxy_output(pskb, getDevFromDestentry(dst), course); #endif else { #endif initSkbHdr(pskb); ip6_finish_output3(pskb, course); #ifdef CONFIG_PPP } #endif return NET_RX_DROP; } break; } } break; } case IPPROTO_UDP: { struct udphdr *udph; __u32 hash; struct Ip6_Path_List_Entry *entry_path; udph = (struct udphdr*)l4hdr; if (udph == NULL){ write_unlock_bh(&ip6_fp_path_lock); return 0; } sPort = udph->source; dPort = udph->dest; //IP6_DEBUGP_PKT("==>> [%08X] SIP: %s/%u -> DIP: %s/%u <UDP>\n", // udph->check, ip6_sprintf(sipStr, sip), udph->source, ip6_sprintf(dipStr, dip), udph->dest); hash = Ip6_FastPath_Hash_PATH_Entry(sip, sPort, dip, dPort, protocol); CTAILQ_FOREACH(entry_path, &ip6_table_path->list[hash], path_link) { if ((*entry_path->in_sPort == sPort) && (*entry_path->in_dPort == dPort) && ipv6_addr_equal(entry_path->in_sIp, sip) && ipv6_addr_equal(entry_path->in_dIp, dip) && (*entry_path->protocol == IPPROTO_UDP)) { if (entry_path->dst == NULL) { if (!fp_ip6route_input(pskb, ip6hdr, entry_path->out_dIp, entry_path->course)) { write_unlock_bh(&ip6_fp_path_lock); return 0; } SetFPDst(pskb, &entry_path->dst); setSkbDst(pskb, NULL); // Check if downstream dst is bridge interface (ex. br0) if (entry_path->course==2 && !(((struct dst_entry *)entry_path->dst)->dev->priv_flags & IFF_EBRIDGE)) { // downstream dst not bridged-LAN, ignore this dst printk("WARNING!! incorrect dst, ignore it!\n"); entry_path->dst = 0; write_unlock_bh(&ip6_fp_path_lock); return 0; } entry_path->out_ifname = fastpath_getdstifName(entry_path->dst); } if(needFragment(pskb, entry_path->dst)) { write_unlock_bh(&ip6_fp_path_lock); return 0; } #ifdef CONFIG_PPP if(ARPHRD_PPP == getDevTypeFromDestentry(entry_path->dst)){ IP6_DEBUGP_PKT("pppoe proxy or upstream pppoe, interface = %s\n", entry_path->out_ifname); xmitOnPPP = 1; } #endif if (isNeighCreated(entry_path->dst, pskb) #ifdef CONFIG_PPP || xmitOnPPP #endif ) { if ((entry_path->course == 1) && ((jiffies - entry_path->fdb_ageing) > br_ageing_time)) { entry_path->fdb_ageing = jiffies; ip6_fp_br_fdb_update(pskb); } #ifdef CONFIG_PPP if (!xmitOnPPP) { #endif setSkbDst(pskb, entry_path->dst); FastPathHoldDst(pskb); if (isDestLo(pskb)) goto FINISH; #ifdef CONFIG_PPP } #endif IP6_DEBUGP_PKT("FORWARD to [%s] \n", entry_path->out_ifname); switch(entry_path->type) { case FPTYPE_PUREROUTING: { /* Only Routing */ break; } case FPTYPE_SNAT: { /* SNAT */ IP6_FASTPATH_ADJUST_CHKSUM_NAT_UDP(entry_path->out_sIp, sip, udph->check); ip6hdr->saddr = *entry_path->out_sIp; break; } case FPTYPE_SNPT: /* SNPT */ case FPTYPE_SNAPT: { /* SNAPT */ IP6_FASTPATH_ADJUST_CHKSUM_NAPT_UDP(entry_path->out_sIp, sip, *entry_path->out_sPort, udph->source, udph->check); ip6hdr->saddr = *entry_path->out_sIp; udph->source = *entry_path->out_sPort; break; } case FPTYPE_DNAT: { /* DNAT */ IP6_FASTPATH_ADJUST_CHKSUM_NAT_UDP(entry_path->out_dIp, dip, udph->check); ip6hdr->daddr = *entry_path->out_dIp; break; } case FPTYPE_DNPT: /* DNPT */ case FPTYPE_DNAPT: { /* DNAPT */ IP6_FASTPATH_ADJUST_CHKSUM_NAPT_UDP(entry_path->out_dIp, dip, *entry_path->out_dPort, udph->dest, udph->check); ip6hdr->daddr = *entry_path->out_dIp; udph->dest = *entry_path->out_dPort; break; } default: { IP6_FASTPATH_ADJUST_CHKSUM_NAPT_UDP(entry_path->out_sIp, sip, *entry_path->out_sPort, udph->source, udph->check); IP6_FASTPATH_ADJUST_CHKSUM_NAPT_UDP(entry_path->out_dIp, dip, *entry_path->out_dPort, udph->dest, udph->check); ip6hdr->saddr = *entry_path->out_sIp; ip6hdr->daddr = *entry_path->out_dIp; udph->source = *entry_path->out_sPort; udph->dest = *entry_path->out_dPort; break; } } if ( (jiffies - entry_path->last_refresh_time) > 20*HZ) { entry_path->last_refresh_time = jiffies; updateConxTimer(entry_path); } dst = entry_path->dst; course = entry_path->course; entry_path->pps++; write_unlock_bh(&ip6_fp_path_lock); #ifdef CONFIG_PPP if (xmitOnPPP) #if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)||defined(CONFIG_ATP_SUPPORT_ETHUP) pppoe_proxy_output(pskb, getDevFromDestentry(dst), course, 0); #else pppoe_proxy_output(pskb, getDevFromDestentry(dst), course); #endif else { #endif initSkbHdr(pskb); ip6_finish_output3(pskb, course); #ifdef CONFIG_PPP } #endif return NET_RX_DROP; } break; } } break; } case IPPROTO_ICMPV6: { break; } default: { __u32 hash; struct Ip6_Path_List_Entry *entry_path; hash = Ip6_FastPath_Hash_PATH_Entry(sip, sPort, dip, dPort, protocol); CTAILQ_FOREACH(entry_path, &ip6_table_path->list[hash], path_link) { if ((*entry_path->protocol == protocol) && ipv6_addr_equal(entry_path->in_sIp, sip) && ipv6_addr_equal(entry_path->in_dIp, dip)) { if (entry_path->dst == NULL) { if (!fp_ip6route_input(pskb, ip6hdr, entry_path->out_dIp, entry_path->course)) { write_unlock_bh(&ip6_fp_path_lock); return 0; } SetFPDst(pskb, &entry_path->dst); setSkbDst(pskb, NULL); // Check if downstream dst is bridge interface (ex. br0) if (entry_path->course==2 && !(((struct dst_entry *)entry_path->dst)->dev->priv_flags & IFF_EBRIDGE)) { // downstream dst not bridged-LAN, ignore this dst printk("WARNING!! incorrect dst, ignore it!\n"); entry_path->dst = 0; write_unlock_bh(&ip6_fp_path_lock); return 0; } entry_path->out_ifname = fastpath_getdstifName(entry_path->dst); } if(needFragment(pskb, entry_path->dst)) { write_unlock_bh(&ip6_fp_path_lock); return 0; } #ifdef CONFIG_PPP if(ARPHRD_PPP == getDevTypeFromDestentry(entry_path->dst)){ IP6_DEBUGP_PKT("pppoe proxy or upstream pppoe, interface = %s\n", entry_path->out_ifname); xmitOnPPP = 1; } #endif if (isNeighCreated(entry_path->dst, pskb) #ifdef CONFIG_PPP || xmitOnPPP #endif ) { if ((entry_path->course == 1) && ((jiffies - entry_path->fdb_ageing) > br_ageing_time)) { entry_path->fdb_ageing = jiffies; ip6_fp_br_fdb_update(pskb); } #ifdef CONFIG_PPP if (!xmitOnPPP) { #endif setSkbDst(pskb, entry_path->dst); FastPathHoldDst(pskb); if (isDestLo(pskb)) goto FINISH; #ifdef CONFIG_PPP } #endif IP6_DEBUGP_PKT("FORWARD to [%s] \n", entry_path->out_ifname); switch(entry_path->type) { case FPTYPE_PUREROUTING: /* Only Routing */ case FPTYPE_SNPT: /* SNPT */ case FPTYPE_DNPT: break; case FPTYPE_DNAT: { /* DNAT */ ip6hdr->daddr = *entry_path->out_dIp; break; } case FPTYPE_SNAT:{ /* SNAT */ ip6hdr->saddr = *entry_path->out_sIp; break; } default: { ip6hdr->saddr = *entry_path->out_sIp; ip6hdr->daddr = *entry_path->out_dIp; break; } } //lifetime check, default lease time set to be 1 min if ((jiffies - entry_path->last_refresh_time) > DEF_ELAPSE_TIME) { entry_path->last_refresh_time = jiffies; updateConxTimer(entry_path); } dst = entry_path->dst; course = entry_path->course; entry_path->pps++; write_unlock_bh(&ip6_fp_path_lock); #ifdef CONFIG_PPP if (xmitOnPPP) #if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)||defined(CONFIG_ATP_SUPPORT_ETHUP) pppoe_proxy_output(pskb, getDevFromDestentry(dst), course, 0); #else pppoe_proxy_output(pskb, getDevFromDestentry(dst), course); #endif else { #endif initSkbHdr(pskb); ip6_finish_output3(pskb, course); #ifdef CONFIG_PPP } #endif return NET_RX_DROP; } break; } } break; } } FINISH: write_unlock_bh(&ip6_fp_path_lock); return 0; } #ifdef DEBUG_PROCFILE static int ip6_fastpath_table_napt_show(struct seq_file *m, void *v) { struct Ip6_Napt_List_Entry *ep; char intipStr[INET6_ADDRSTRLEN], extipStr[INET6_ADDRSTRLEN], remipStr[INET6_ADDRSTRLEN]; __u8 *proto; CTAILQ_FOREACH(ep, &ip6_napt_list_inuse, tqe_link) { if( ep->protocol == IPPROTO_TCP ) { proto = (__u8 *)"TCP"; } else if( ep->protocol == IPPROTO_UDP ) { proto = (__u8 *)"UDP"; } else { proto = (__u8 *)"unknow"; } seq_printf(m, "~Napt: [%s] int=%s/%-5u\n ext=%s/%-5u rem=%s/%-5u state=%d\n", proto, ip6_sprintf(intipStr, &ep->intIp), ep->intPort, ip6_sprintf(extipStr, &ep->extIp), ep->extPort, ip6_sprintf(remipStr, &ep->remIp), ep->remPort, ep->state); } return 0; } static int ip6_fastpath_table_napt_open(struct inode *inode, struct file *file) { return single_open(file, ip6_fastpath_table_napt_show, NULL); } static const struct file_operations ip6_fastpath_table_napt_proc_fops = { .open = ip6_fastpath_table_napt_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .owner = THIS_MODULE, }; static int ip6_fastpath_table_path_show(struct seq_file *m, void *v) { struct Ip6_Path_List_Entry *ep; char intsipStr[INET6_ADDRSTRLEN], indipStr[INET6_ADDRSTRLEN], outsipStr[INET6_ADDRSTRLEN], outdipStr[INET6_ADDRSTRLEN]; __u8 *proto; CTAILQ_FOREACH(ep, &ip6_path_list_inuse, tqe_link) { if( *ep->protocol == IPPROTO_TCP ) { proto = (__u8 *)"TCP"; } else if( *ep->protocol == IPPROTO_UDP ) { proto = (__u8 *)"UDP"; } else if( *ep->protocol == IPPROTO_GRE ) { proto = (__u8 *)"GRE"; } else if( *ep->protocol == IPPROTO_IPIP ) { proto = (__u8 *)"IPIP"; } else { proto = (__u8 *)"unknow"; } seq_printf(m, "~Path: <%5u> [%s] in-S=%s/%-5u in-D=%s/%-5u\n out-S=%s/%-5u out-D=%s/%-5u\n out-ifname=%-5s mark=%10x <%s> {%d} hash %u\n", ep->pps, proto, ip6_sprintf(intsipStr, ep->in_sIp), *ep->in_sPort, ip6_sprintf(indipStr, ep->in_dIp), *ep->in_dPort, ip6_sprintf(outsipStr, ep->out_sIp), *ep->out_sPort, ip6_sprintf(outdipStr, ep->out_dIp), *ep->out_dPort, ep->out_ifname, ep->mark, (ep->course==1)?"up":"down", ep->type,Ip6_FastPath_Hash_PATH_Entry(ep->in_sIp, *ep->in_sPort, ep->in_dIp, *ep->in_dPort, *ep->protocol)); } return 0; } static int ip6_fastpath_table_path_open(struct inode *inode, struct file *file) { return single_open(file, ip6_fastpath_table_path_show, NULL); } static const struct file_operations ip6_fastpath_table_path_proc_fops = { .open = ip6_fastpath_table_path_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .owner = THIS_MODULE, }; static int ip6_fastpath_hash_path_show(struct seq_file *m, void *v) { int i; for (i=0; i<IP6_PATH_TABLE_LIST_MAX; i++) { seq_printf(m, "%5d ", CTAILQ_TOTAL(&ip6_table_path->list[i])); if (i%12 == 11) seq_printf(m, "\n"); } seq_printf(m, "\n"); return 0; } static int ip6_fastpath_hash_path_open(struct inode *inode, struct file *file) { return single_open(file, ip6_fastpath_hash_path_show, NULL); } static const struct file_operations ip6_fastpath_hash_path_proc_fops = { .open = ip6_fastpath_hash_path_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .owner = THIS_MODULE, }; #endif /* DEBUG_PROCFILE */ static struct proc_dir_entry *IP6_FP_Proc_File; #define PROCFS_NAME "Ip6FastPath" #define REALTEK_IP6_FASTPATH_VERSION MODULE_VERSION_FP #include <asm/uaccess.h> static int ip6_fp_proc_read(struct seq_file*s , void* v) { if(ip6_fp_on==1) seq_printf(s, "%s fastpath ON!\n",REALTEK_IP6_FASTPATH_VERSION); if(ip6_fp_on==0) seq_printf(s, "%s fastpath OFF!\n",REALTEK_IP6_FASTPATH_VERSION); return 0; } static int ip6_fp_proc_open(struct inode *inode, struct file *file) { return(single_open(file, ip6_fp_proc_read, NULL)); } #ifdef CONFIG_RTL867X_KERNEL_MIPS16_NET __NOMIPS16 #endif static ssize_t ip6_fp_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *data) { char proc_buffer[count]; /* write data to the buffer */ memset(proc_buffer, 0, sizeof(proc_buffer)); if ( copy_from_user(proc_buffer, buffer, count) ) { return -EFAULT; } switch(proc_buffer[0]) { case '0': ip6_fp_on = 0; Ip6_clearFastPathEntry(); break; case '1': ip6_fp_on = 1; break; default: printk("Error setting!\n"); } return -1; } static const struct file_operations ip6_fp_proc_fops = { .open = ip6_fp_proc_open, .write = ip6_fp_proc_write, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int ip6_fastpath_memory_init(void) { int i; /* Napt-Table Init */ ip6_table_napt = (struct Ip6_Napt_Table *)kmalloc(sizeof(struct Ip6_Napt_Table), GFP_ATOMIC); if (ip6_table_napt == NULL) { IP6_DEBUGP_SYS("MALLOC Failed! (IPv6 Napt Table) \n"); return -1; } CTAILQ_INIT(&ip6_napt_list_inuse); CTAILQ_INIT(&ip6_napt_list_free); for (i=0; i<IP6_NAPT_TABLE_LIST_MAX; i++) { CTAILQ_INIT(&ip6_table_napt->list[i]); } /* Napt-List Init */ for (i=0; i<IP6_NAPT_TABLE_ENTRY_MAX; i++) { struct Ip6_Napt_List_Entry *entry_napt = (struct Ip6_Napt_List_Entry *)kmalloc(sizeof(struct Ip6_Napt_List_Entry), GFP_ATOMIC); if (entry_napt == NULL) { IP6_DEBUGP_SYS("MALLOC Failed! (IPv6 Napt Table Entry) \n"); return -2; } CTAILQ_INSERT_TAIL(&ip6_napt_list_free, entry_napt, tqe_link); } /* Path-Table Init */ ip6_table_path = (struct Ip6_Path_Table *)kmalloc(sizeof(struct Ip6_Path_Table), GFP_ATOMIC); if (ip6_table_path == NULL) { IP6_DEBUGP_SYS("MALLOC Failed! (IPv6 Path Table) \n"); return -1; } CTAILQ_INIT(&ip6_path_list_inuse); CTAILQ_INIT(&ip6_path_list_free); for (i=0; i<IP6_PATH_TABLE_LIST_MAX; i++) { CTAILQ_INIT(&ip6_table_path->list[i]); } /* Path-List Init */ for (i=0; i<IP6_PATH_TABLE_ENTRY_MAX; i++) { struct Ip6_Path_List_Entry *entry_path = (struct Ip6_Path_List_Entry *)kmalloc(sizeof(struct Ip6_Path_List_Entry), GFP_ATOMIC); if (entry_path == NULL) { IP6_DEBUGP_SYS("MALLOC Failed! (IPv6 Path Table Entry) \n"); return -2; } CTAILQ_INSERT_TAIL(&ip6_path_list_free, entry_path, tqe_link); } rwlock_init(&ip6_fp_napt_lock); rwlock_init(&ip6_fp_path_lock); return 0; } static int __init ip6_fastpath_init(void) { #ifdef DEBUG_PROCFILE /* proc file for debug */ proc_create("fp6_napt", 0, init_net.proc_net, &ip6_fastpath_table_napt_proc_fops); proc_create("fp6_path", 0, init_net.proc_net, &ip6_fastpath_table_path_proc_fops); proc_create("fp6_hash_path", 0, init_net.proc_net, &ip6_fastpath_hash_path_proc_fops); #endif /* DEBUG_PROCFILE */ printk("%s %s\n",MODULE_NAME, MODULE_VERSION_FP); //create proc IP6_FP_Proc_File= proc_create_data(PROCFS_NAME, 0644, NULL, &ip6_fp_proc_fops, NULL); if (IP6_FP_Proc_File == NULL) { printk(KERN_ALERT "Error: Could not initialize /proc/%s\n", PROCFS_NAME); return -ENOMEM; } printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME); ip6_fastpath_memory_init(); #if !defined(CONFIG_NF_CONNTRACK_IPV6) init_ip6_fastpath_time(); #endif return 0; } static void __exit ip6_fastpath_exit(void) { printk("%s %s removed!\n", MODULE_NAME, MODULE_VERSION_FP); } module_init(ip6_fastpath_init); module_exit(ip6_fastpath_exit);