/* * IPv6 fastpath core function. * Author ql_xu * * ==FILEVERSION 20130316== * ============== modification revision ============== * 20130318 modify version to beta v0.01 * 20130319 new API when CONFIG_NF_CONNTRACK_IPV6 closed, napt table is useless now. * */ #include "../net/bridge/br_private.h" #include "ip6_fastpath_core.h" #include <linux/if_pppox.h> #include <linux/ipv6.h> #include <net/dst.h> #include <net/neighbour.h> #include <net/ip6_route.h> unsigned int br_ageing_time=270*HZ; extern void neigh_hh_init(struct neighbour *n, struct dst_entry *dst); inline int isNeighCreated(void *pDst, void *pSkb) { struct dst_entry *dst = (struct dst_entry *)pDst; struct sk_buff *skb = (struct sk_buff *)pSkb; struct neighbour *neigh = dst_neigh_lookup_skb(dst, skb); int ret = 1; if (neigh == NULL) return 0; if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE))) ret = 0; neigh_release(neigh); return ret; } inline int needFragment(void *pSkb, void *pDst) { struct dst_entry *dst = (struct dst_entry *)pDst; struct sk_buff *skb = (struct sk_buff *)pSkb; if(skb->len > dst->dev->mtu) { return 1; } return 0; } inline void ip6_fp_br_fdb_update(void *pSt) { struct sk_buff *skb = (struct sk_buff *)pSt; struct net_bridge_port *br_port; br_port = br_port_get_rcu(skb->dev); if (br_port){ br_fdb_update(br_port->br, br_port, eth_hdr(skb)->h_source, 0, false); } } inline void updateConxTimer(struct Ip6_Path_List_Entry *ptr) { struct nf_conntrack_tuple_hash *h; struct nf_conn *ct; static unsigned int nf_ct_timeout; unsigned long newtime; /* look for tuple match */ h = nf_conntrack_find_get(&init_net, NF_CT_DEFAULT_ZONE, &ptr->orig_tuple); if (!h) { //printk("lookup ct failed\n"); return; } else { ct = nf_ct_tuplehash_to_ctrack(h); } if (ct->tuplehash[0].tuple.dst.protonum == IPPROTO_TCP) nf_ct_timeout = ct->ct_net->ct.nf_ct_proto.tcp.timeouts[ct->proto.tcp.state]; else nf_ct_timeout = ct->ct_net->ct.nf_ct_proto.udp.timeouts[UDP_CT_REPLIED]; newtime = nf_ct_timeout + jiffies; if ( (newtime - ct->timeout.expires >= HZ)){ mod_timer_pending(&ct->timeout, newtime); } nf_ct_put(ct); } 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; } __IRAM int fp_ip6route_input(void *pSt /*struct skbuff * */, struct ipv6hdr *iph, struct in6_addr *fp_dip, unsigned int course) { struct sk_buff *skb = (struct sk_buff *)pSt; struct net_device *ndev=skb->dev; struct net_bridge_port *br_port; br_port = br_port_get_rcu(skb->dev); if (course == 1) {//upstream if(br_port!=NULL){ if(br_port->br->dev == NULL) BUG(); ndev = br_port->br->dev; } } __ip6_route_input(skb, &iph->saddr, fp_dip, ndev); if (NULL == skb_dst(skb)){ printk("can't find dest_entry for %s\n", ndev->name); return 0; } return 1; } #ifdef CONFIG_NF_CONNTRACK_IPV6 extern struct net init_net; static enum LR_RESULT ip6_fastpath_nfconn2naptconn(struct sk_buff *skb, struct IP6_FP_NAPT_entry *napt, struct nf_conntrack_tuple *orig_tuple, struct nf_conntrack_tuple *reply_tuple) { struct nf_conntrack_tuple *tpdir1, *tpdir2; #if 0 struct flowi6 fl6; struct dst_entry *dst1, *dst2; #endif int dst_type; //char ip6Str[INET6_ADDRSTRLEN]; //exclude ::1 if (ipv6_addr_loopback(&orig_tuple->src.u3.in6) || ipv6_addr_loopback(&orig_tuple->dst.u3.in6) || ipv6_addr_loopback(&reply_tuple->src.u3.in6) || ipv6_addr_loopback(&reply_tuple->dst.u3.in6)) goto FP_FAIL; /* ignore multicast and linklocal packet */ dst_type = __ipv6_addr_type(&orig_tuple->dst.u3.in6); if (dst_type & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) goto FP_FAIL; dst_type = __ipv6_addr_type(&reply_tuple->dst.u3.in6); if (dst_type & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) goto FP_FAIL; napt->protocol = orig_tuple->dst.protonum; #if USE_SKB_DEV if(skb) { struct net_bridge_port *br_port; if(skb->from_dev) { rcu_read_lock(); br_port = br_port_get_rcu(skb->from_dev); rcu_read_unlock(); } if(br_port) { tpdir1 = orig_tuple; tpdir2 = reply_tuple; } else if(skb->dev) { rcu_read_lock(); br_port = br_port_get_rcu(skb->dev); rcu_read_unlock(); if(br_port) { tpdir1 = reply_tuple; tpdir2 = orig_tuple; } } if(br_port==NULL) { printk("%s orig_tuple from %s, reply_tuple from %s go normal path\n", __func__, skb->from_dev->name, skb->dev->name); goto FP_FAIL; } } else { #if 0 memset(&fl6, 0, sizeof(struct flowi6)); fl6.flowi6_proto = napt->protocol; fl6.daddr = orig_tuple->src.u3.in6; dst1 = ip6_route_output(&init_net, NULL, &fl6); fl6.daddr = reply_tuple->src.u3.in6; dst2 = ip6_route_output(&init_net, NULL, &fl6); if (dst1 && dst1->error==0 && (dst1->dev->priv_flags & IFF_EBRIDGE)) { tpdir1 = orig_tuple; tpdir2 = reply_tuple; }else if (dst2 && dst2->error==0 && (dst2->dev->priv_flags & IFF_EBRIDGE)) { tpdir1 = reply_tuple; tpdir2 = orig_tuple; } else { //printk("%s get dst_entry for failed.\n", __func__); goto FP_FAIL; } #else goto FP_FAIL; #endif } napt->intIp = tpdir1->src.u3.in6; napt->intPort = ntohs(tpdir1->src.u.all); napt->extIp = tpdir2->dst.u3.in6; napt->extPort = ntohs(tpdir2->dst.u.all); napt->remIp = tpdir2->src.u3.in6; napt->remPort = ntohs(tpdir2->src.u.all); return LR_SUCCESS; #else memset(&fl6, 0, sizeof(struct flowi6)); fl6.flowi6_proto = napt->protocol; fl6.daddr = orig_tuple->src.u3.in6; dst1 = ip6_route_output(&init_net, NULL, &fl6); if (dst1->error) { //printk("%s get dst_entry for %s failed.\n", __func__, ip6_sprintf(ip6Str, &fl6.daddr)); goto RELEASE_DST1; } fl6.daddr = reply_tuple->src.u3.in6; dst2 = ip6_route_output(&init_net, NULL, &fl6); if (dst2->error) { //printk("%s get dst_entry for %s failed.\n", __func__, ip6_sprintf(ip6Str, &fl6.daddr)); goto RELEASE_DST2; } //if (dst1->dev->priv_flags & IFF_DOMAIN_ELAN) if (dst1->dev->priv_flags & IFF_EBRIDGE) { tpdir1 = orig_tuple; tpdir2 = reply_tuple; } //else if (dst2->dev->priv_flags & IFF_DOMAIN_ELAN) else if (dst2->dev->priv_flags & IFF_EBRIDGE) { tpdir1 = reply_tuple; tpdir2 = orig_tuple; } else { printk("%s orig_tuple from %s, reply_tuple from %s go normal path\n", __func__, dst1->dev->name, dst2->dev->name); goto RELEASE_DST2; } dst_release(dst1); dst_release(dst2); napt->intIp = tpdir1->src.u3.in6; napt->intPort = ntohs(tpdir1->src.u.all); napt->extIp = tpdir2->dst.u3.in6; napt->extPort = ntohs(tpdir2->dst.u.all); napt->remIp = tpdir2->src.u3.in6; napt->remPort = ntohs(tpdir2->src.u.all); return LR_SUCCESS; RELEASE_DST2: dst_release(dst2); RELEASE_DST1: dst_release(dst1); #endif FP_FAIL: return LR_FAILED; } /* * state: 0-unreplied 1-established */ #ifdef CONFIG_RTL867X_KERNEL_MIPS16_NET __NOMIPS16 #endif enum LR_RESULT fastpath_addRoutedIp6NaptConnection(struct sk_buff *skb, struct nf_conn * ct, struct nf_conntrack_tuple *orig_tuple, struct nf_conntrack_tuple *reply_tuple, int state) { struct IP6_FP_NAPT_entry napt; if (LR_SUCCESS != ip6_fastpath_nfconn2naptconn(skb, &napt, orig_tuple, reply_tuple)) return LR_FAILED; return (ip6_fastpath_addNaptConnection(ct, &napt, state)); } #ifdef CONFIG_RTL867X_KERNEL_MIPS16_NET __NOMIPS16 #endif enum LR_RESULT fastpath_updateIp6NaptConnection(struct sk_buff *skb, struct nf_conntrack_tuple *orig_tuple, struct nf_conntrack_tuple *reply_tuple, unsigned int mark) { struct IP6_FP_NAPT_entry napt; if (LR_SUCCESS != ip6_fastpath_nfconn2naptconn(skb, &napt, orig_tuple, reply_tuple)) return LR_FAILED; return (ip6_fastpath_updateNaptConnection(&napt, mark)); } #ifdef CONFIG_RTL867X_KERNEL_MIPS16_NET __NOMIPS16 #endif enum LR_RESULT fastpath_delIp6NaptConnection (struct nf_conntrack_tuple *orig_tuple, struct nf_conntrack_tuple *reply_tuple) { struct IP6_FP_NAPT_entry napt; if (LR_SUCCESS != ip6_fastpath_nfconn2naptconn(NULL, &napt, orig_tuple, reply_tuple)) return LR_FAILED; return (ip6_fastpath_delNaptConnection(&napt)); } #else//end of CONFIG_NF_CONNTRACK_IPV6 static enum LR_RESULT ip6_fastpath_parseNaptconn(struct IP6_FP_NAPT_entry *napt, int *course, struct ipv6hdr *ip6hdr) { struct dst_entry *dst1, *dst2; int dst_type; struct flowi6 fl6; __u8 protoType=0; __u16 sport=0, dport=0; __u8 *l4hdr; char ip6Str[INET6_ADDRSTRLEN]; //exclude ::1 if (ipv6_addr_loopback(&ip6hdr->saddr) || ipv6_addr_loopback(&ip6hdr->daddr)) goto FP_FAIL; /* ignore multicast and linklocal packet */ dst_type = __ipv6_addr_type(&ip6hdr->daddr); if (dst_type & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) goto FP_FAIL; Ip6_packetParser(ip6hdr, &protoType, &sport, &dport, &l4hdr); if (protoType == IPPROTO_ICMPV6) goto FP_FAIL; memset(&fl6, 0, sizeof(struct flowi6)); fl6.flowi6_proto = protoType; fl6.daddr = ip6hdr->saddr; dst1 = ip6_route_output(&init_net, NULL, &fl6); if (dst1->error) { printk("%s get dst_entry for %s failed.\n", __func__, ip6_sprintf(ip6Str, &fl6.daddr)); goto RELEASE_DST1; } fl6.daddr = ip6hdr->daddr; dst2 = ip6_route_output(&init_net, NULL, &fl6); if (dst2->error) { printk("%s get dst_entry for %s failed.\n", __func__, ip6_sprintf(ip6Str, &fl6.daddr)); goto RELEASE_DST2; } napt->protocol = protoType; //if (dst1->dev->priv_flags & IFF_DOMAIN_ELAN) if (dst1->dev->priv_flags & IFF_EBRIDGE) { *course = 1; napt->intIp = napt->extIp = ip6hdr->saddr; napt->remIp = ip6hdr->daddr; napt->intPort = napt->extPort = sport; napt->remPort = dport; } //else if (dst2->dev->priv_flags & IFF_DOMAIN_ELAN) else if (dst2->dev->priv_flags & IFF_EBRIDGE) { *course = 2; napt->intIp = napt->extIp = ip6hdr->daddr; napt->remIp = ip6hdr->saddr; napt->intPort = napt->extPort = dport; napt->remPort = sport; } else { printk("%s orig_tuple from %s, reply_tuple from %s go normal path\n", __func__, dst1->dev->name, dst2->dev->name); goto RELEASE_DST2; } dst_release(dst1); dst_release(dst2); return LR_SUCCESS; RELEASE_DST2: dst_release(dst2); RELEASE_DST1: dst_release(dst1); FP_FAIL: return LR_FAILED; } #ifdef CONFIG_RTL867X_KERNEL_MIPS16_NET __NOMIPS16 #endif enum LR_RESULT fastpath_addRoutedIp6NaptConnectionWithoutNFV6(struct ipv6hdr *ip6hdr) { struct IP6_FP_NAPT_entry napt; int course; //1-outbound 2-inbound if (LR_SUCCESS != ip6_fastpath_parseNaptconn(&napt, &course, ip6hdr)) return LR_FAILED; return (ip6_fastpath_addNaptConnectionWithoutNFV6(&napt, course)); } enum LR_RESULT fastpath_delIp6NaptConnectionWithoutNFV6(struct ipv6hdr *ip6hdr) { struct IP6_FP_NAPT_entry napt; int course; //1-outbound 2-inbound if (LR_SUCCESS != ip6_fastpath_parseNaptconn(&napt, &course, ip6hdr)) return LR_FAILED; return (ip6_fastpath_delNaptConnectionWithoutNFV6(&napt, course)); } #endif __IRAM_SYS_MIDDLE int ip6_finish_output3(void *pskb, unsigned int course) { struct sk_buff *skb = (struct sk_buff *)pskb; struct dst_entry *dst = skb_dst(skb); struct net_bridge_fdb_entry *fpdst; struct net_bridge *br; //suppose skb->dev is bridge unsigned char *dest; struct neighbour *neigh =NULL, *neigh2 = NULL; struct hh_cache *hh = NULL; neigh = dst_neigh_lookup_skb(dst, skb); if (!neigh){ printk("dst neighbour is NULL.dst %s \n", skb->dev->name); goto DROP; } hh = (struct hh_cache *)(&neigh->hh); if(!hh) { if(dst->dev->header_ops && dst->dev->header_ops->cache!=NULL) { neigh_hh_init(neigh, dst); neigh2 = dst_neigh_lookup_skb(dst, skb); if (neigh2){ hh = (struct hh_cache *)(&neigh2->hh); if (!hh) goto DROP1; } else goto DROP1; } else { if(dev_hard_header(skb, dst->dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len)<0) goto DROP1; } } if(hh) { unsigned seq; int hh_len; do { int hh_alen; seq = read_seqbegin(&hh->hh_lock); hh_len = hh->hh_len; hh_alen = HH_DATA_ALIGN(hh_len); memcpy(skb->data - hh_alen, hh->hh_data, hh_alen); } while (read_seqretry(&hh->hh_lock, seq)); skb_push(skb, hh_len); skb_set_mac_header(skb, 0); } //forwarding process if (course == 1) {//upstream IP6_DEBUGP_PKT("%s xmit dev %s (%x)\n", __func__, skb->dev->name, (unsigned int)skb->dev->hard_start_xmit); skb->dev->netdev_ops->ndo_start_xmit(skb,skb->dev); goto SUCCEED; } else { if(unlikely(!(skb->dev->priv_flags & IFF_EBRIDGE))) { printk("[IPoE FastPath Exception] %s(%d): skb->dev is %s, course is %d; drops this packet!\n", __func__, __LINE__, skb->dev->name, course); goto DROP1; } dest = eth_hdr(skb)->h_dest; br = netdev_priv(skb->dev); if( !br) { printk("%s %d br is NULL.\n", __FUNCTION__, __LINE__); kfree_skb(skb); goto SUCCEED; } rcu_read_lock(); if ((fpdst = __br_fdb_get(br, dest, 0)) != NULL) { rcu_read_unlock(); if (netif_running(fpdst->dst->dev)) { skb->dev = fpdst->dst->dev; fpdst->dst->dev->netdev_ops->ndo_start_xmit(skb, fpdst->dst->dev); } else{ kfree_skb(skb); } goto SUCCEED; } else{ rcu_read_unlock(); skb->dev->netdev_ops->ndo_start_xmit(skb,skb->dev); goto SUCCEED; } } SUCCEED: neigh_release(neigh); if (neigh2) neigh_release(neigh2); return 1; DROP1: neigh_release(neigh); if (neigh2) neigh_release(neigh2); DROP: printk( "ip_finish_output3: No header cache and no neighbour, course=%d!\n", course); kfree_skb(skb); return -EINVAL; } #ifdef CONFIG_RTL867X_KERNEL_MIPS16_NET __NOMIPS16 #endif __IRAM_SYS_MIDDLE int Ip6_FastPath_Enter(struct sk_buff *skb) { return (Ip6_FastPath_Process((void *)skb, ipv6_hdr(skb))); } static char digits_v6[] = "0123456789abcdef"; char *ip6_sprintf(char *ip6buf, const struct in6_addr *addr) { int i; char *cp; const u_int16_t *a = (const u_int16_t *)addr; const u_int8_t *d; int dcolon = 0, zero = 0; cp = ip6buf; for (i = 0; i < 8; i++) { if (dcolon == 1) { if (*a == 0) { if (i == 7) *cp++ = ':'; a++; continue; } else dcolon = 2; } if (*a == 0) { if (dcolon == 0 && *(a + 1) == 0) { if (i == 0) *cp++ = ':'; *cp++ = ':'; dcolon = 1; } else { *cp++ = '0'; *cp++ = ':'; } a++; continue; } d = (const u_char *)a; /* Try to eliminate leading zeros in printout like in :0001. */ zero = 1; *cp = digits_v6[*d >> 4]; if (*cp != '0') { zero = 0; cp++; } *cp = digits_v6[*d++ & 0xf]; if (zero == 0 || (*cp != '0')) { zero = 0; cp++; } *cp = digits_v6[*d >> 4]; if (zero == 0 || (*cp != '0')) { zero = 0; cp++; } *cp++ = digits_v6[*d & 0xf]; *cp++ = ':'; a++; } *--cp = '\0'; return (ip6buf); }