diff --git a/include/linux/if_map.h b/include/linux/if_map.h index e1aa3ce..0024227 100644 --- a/include/linux/if_map.h +++ b/include/linux/if_map.h @@ -19,6 +19,11 @@ #define SIOCGETMAPNAPTNUM (SIOCDEVPRIVATE + 10) #define SIOCGETMAPNAPT (SIOCDEVPRIVATE + 11) +#define SIOCGETMAPPOOLS (SIOCDEVPRIVATE + 12) +#define SIOCADDMAPPOOLS (SIOCDEVPRIVATE + 13) +#define SIOCDELMAPPOOLS (SIOCDEVPRIVATE + 14) +#define SIOCCHGMAPPOOLS (SIOCDEVPRIVATE + 15) + #define MAP_ROLE_BR (1 << 0) #define MAP_ROLE_CE (1 << 1) @@ -50,6 +55,11 @@ struct map_rule_parm { __u8 forwarding_rule; }; +struct map_pool_parm { + __be32 pool_prefix; + __u8 pool_prefix_length; +}; + struct map_parm { char name[IFNAMSIZ]; int tunnel_source; @@ -63,12 +73,15 @@ struct map_parm { __u8 napt_always; __u8 napt_force_recycle; unsigned long rule_num; + unsigned long pool_num; struct map_rule_parm rule[0]; + struct map_pool_parm pool[0]; }; struct map_napt_node_parm { __be32 raddr, laddr, maddr; __be16 rport, lport, mport; + struct in6_addr laddr6; __u8 proto; __u8 flags; struct timespec last_used; diff --git a/include/net/map.h b/include/net/map.h index 9dfb9c6..1fc937c 100644 --- a/include/net/map.h +++ b/include/net/map.h @@ -55,6 +55,7 @@ struct map_defrag6_node { struct map_napt_node_parm { __be32 raddr, laddr, maddr; __be16 rport, lport, mport; + struct in6_addr laddr6; __u8 proto; __u8 flags; struct timespec last_used; @@ -76,6 +77,7 @@ struct map_napt_node { struct list_head nn_list, nn_gc_list; __be32 raddr, laddr, maddr; __be16 rport, lport, mport; + struct in6_addr laddr6; __u8 proto; __u8 flags; unsigned long last_used; @@ -112,6 +114,18 @@ struct map_rule { }; /* +struct map_pool_parm { + __be32 pool_prefix; + __u8 pool_prefix_length; +}; +*/ + +struct map_pool { + struct list_head list; + struct map_pool_parm p; +}; + +/* struct map_parm { char name[IFNAMSIZ]; int tunnel_source; @@ -124,8 +138,10 @@ struct map_parm { __u8 ipv4_fragment_inner; __u8 napt_always; __u8 napt_force_recycle; - __u8 rule_num; + unsigned long rule_num; + unsigned long pool_num; struct map_rule_parm rule[0]; + struct map_pool_parm pool[0]; }; */ @@ -137,6 +153,8 @@ struct map { struct mrtree_node *mrtn_root_ipv6addr; struct mrtree_node *mrtn_root_ipv4addrport; rwlock_t rule_lock; + struct list_head pool_list; + rwlock_t pool_lock; struct map_rule *bmr; struct in6_addr map_ipv6_address; __u8 map_ipv6_address_length; @@ -196,19 +214,19 @@ void mrtree_node_dump(struct mrtree_node *root); int map_rule_init(void); void map_rule_exit(void); -int map_trans_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4); +int map_trans_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4, int *fb); int map_trans_validate_dst(struct sk_buff *skb, struct map *m, __be32 *daddr4); -int map_trans_forward_v6v4(struct sk_buff *skb, struct map *m, __be32 *saddr4, __be32 *daddr4, int frag); -int map_trans_forward_v4v6(struct sk_buff *skb, struct map *m, struct map_rule *mr, int df); +int map_trans_forward_v6v4(struct sk_buff *skb, struct map *m, __be32 *saddr4, __be32 *daddr4, int fb, int frag); +int map_trans_forward_v4v6(struct sk_buff *skb, struct map *m, struct map_rule *mr, int fb, int df); -int map_encap_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4); +int map_encap_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4, int *fb); int map_encap_validate_dst(struct sk_buff *skb, struct map *m, __be32 *daddr4); -int map_encap_forward_v6v4(struct sk_buff *skb, struct map *m, __be32 *saddr4, __be32 *daddr4); -int map_encap_forward_v4v6(struct sk_buff *skb, struct map *m, struct map_rule *mr); +int map_encap_forward_v6v4(struct sk_buff *skb, struct map *m, __be32 *saddr4, __be32 *daddr4, int fb); +int map_encap_forward_v4v6(struct sk_buff *skb, struct map *m, struct map_rule *mr, int fb); -int map_napt_hairpin(struct sk_buff *skb, struct map *m, __be32 *daddrp, __be16 *dportp); +int map_napt_hairpin(struct sk_buff *skb, struct map *m, __be32 *daddrp, __be16 *dportp, struct in6_addr *saddr6, int fb); void map_napt_nn_gc(struct map *m); -int map_napt(struct iphdr *iph, int dir, struct map *m, __be32 **saddrpp, __be16 **sportpp, __sum16 **checkpp); +int map_napt(struct iphdr *iph, int dir, struct map *m, __be32 **saddrpp, __be16 **sportpp, __sum16 **checkpp, struct in6_addr *saddr6, int fb); int map_napt_init(void); void map_napt_exit(void); @@ -216,6 +234,8 @@ struct sk_buff *map_defrag6(struct sk_buff *skb, struct map *m); int map_defrag6_init(void); void map_defrag6_exit(void); +void map_napt_debug_pool(struct map *m); + static inline void map_debug_print_skb(const char *func, struct sk_buff *skb) { struct iphdr *iph = NULL; diff --git a/net/ipv6/map_encap.c b/net/ipv6/map_encap.c index 2bff284..384213a 100644 --- a/net/ipv6/map_encap.c +++ b/net/ipv6/map_encap.c @@ -22,7 +22,7 @@ /* XXX: */ int -map_encap_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4) +map_encap_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4, int *fb) { struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct map_rule *mr; @@ -60,14 +60,9 @@ map_encap_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4) return 0; } - mr = map_rule_find_by_ipv6addr(m, &ipv6h->saddr); - - if (!mr) - printk(KERN_NOTICE "map_encap_validate_src: " - "map rule not found.\n"); - saddr = iph->saddr; ptr += iph->ihl * 4; + switch (iph->protocol) { case IPPROTO_ICMP: icmph = (struct icmphdr *)ptr; @@ -121,19 +116,25 @@ map_encap_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4) goto err; } + mr = map_rule_find_by_ipv6addr(m, &ipv6h->saddr); if (!mr) { - mr = map_rule_find_by_ipv4addrport(m, &saddr, &sport, 0); - if (!mr) { + if (m->p.role == MAP_ROLE_BR) { + *fb = 1; + goto fallback; + } else { err = -1; goto err; } } if (map_gen_addr6(&addr6, saddr, sport, mr, 0)) { - printk(KERN_NOTICE "map_encap_validate_src: " - "map_gen_addr6 failed.\n"); - err = -1; - goto err; + if (m->p.role == MAP_ROLE_BR) { + *fb = 1; + goto fallback; + } else { + err = -1; + goto err; + } } if (!ipv6_addr_equal(&addr6, &ipv6h->saddr)) { @@ -143,6 +144,7 @@ map_encap_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4) goto err_icmpv6_send; } +fallback: *saddr4 = iph->saddr; return 0; @@ -321,7 +323,7 @@ err: int map_encap_forward_v6v4(struct sk_buff *skb, struct map *m, __be32 *saddr4, - __be32 *daddr4) + __be32 *daddr4, int fb) { struct ipv6hdr orig_ipv6h = {}, *ipv6h; struct frag_hdr orig_fragh = {}, *fragh; @@ -331,12 +333,14 @@ map_encap_forward_v6v4(struct sk_buff *skb, struct map *m, __be32 *saddr4, __be32 *saddrp = NULL; __be16 *sportp = NULL; __sum16 *checkp = NULL; + struct in6_addr *saddr6; u8 *ptr; int err = 0; ipv6h = ipv6_hdr(skb); memcpy(&orig_ipv6h, ipv6h, sizeof(orig_ipv6h)); + saddr6 = &orig_ipv6h.saddr; hsize = sizeof(orig_ipv6h); nexthdr = orig_ipv6h.nexthdr; if (orig_ipv6h.nexthdr == IPPROTO_FRAGMENT) { @@ -361,7 +365,15 @@ map_encap_forward_v6v4(struct sk_buff *skb, struct map *m, __be32 *saddr4, skb->protocol = htons(ETH_P_IP); iph = ip_hdr(skb); - err = map_napt(iph, 1, m, &saddrp, &sportp, &checkp); + if (m->p.role == MAP_ROLE_BR && fb) { + err = map_napt(iph, 0, m, &saddrp, &sportp, &checkp, saddr6, fb); + if (err) + goto err; + /* NAPT Hairpinning */ + if (map_napt_hairpin(skb, m, saddrp, sportp, saddr6, fb)) + goto out; + } else + err = map_napt(iph, 1, m, &saddrp, &sportp, &checkp, NULL, fb); if (err) { printk(KERN_NOTICE "map_encap_forward_v6v4: " "saddr:%d.%d.%d.%d daddr:%d.%d.%d.%d\n", @@ -391,6 +403,7 @@ map_encap_forward_v6v4(struct sk_buff *skb, struct map *m, __be32 *saddr4, err: map_debug_print_skb("map_encap_forward_v6v4", skb); +out: return err; } @@ -463,7 +476,7 @@ tx_err_dst_release: } int -map_encap_forward_v4v6(struct sk_buff *skb, struct map *m, struct map_rule *mr) +map_encap_forward_v4v6(struct sk_buff *skb, struct map *m, struct map_rule *mr, int fb) { struct flowi6 fl6; struct in6_addr saddr6, daddr6; @@ -480,12 +493,11 @@ map_encap_forward_v4v6(struct sk_buff *skb, struct map *m, struct map_rule *mr) iph = ip_hdr(skb); - err = map_napt(iph, 0, m, &daddrp, &dportp, &checkp); + err = map_napt(iph, 0, m, &daddrp, &dportp, &checkp, NULL, 0); if (err) goto err; - /* NAPT Hairpinning */ - if(map_napt_hairpin(skb, m, daddrp, dportp)) + if (map_napt_hairpin(skb, m, daddrp, dportp, NULL, 0)) goto out; iph->check = 0; @@ -505,6 +517,12 @@ map_encap_forward_v4v6(struct sk_buff *skb, struct map *m, struct map_rule *mr) daddr6.s6_addr32[3] = m->p.br_address.s6_addr32[3]; } + if (m->p.role == MAP_ROLE_BR && fb) { + err = map_napt(iph, 1, m, &daddrp, &dportp, &checkp, &daddr6, fb); + if (err) + goto err; + } + memset(&fl6, 0, sizeof(fl6)); ipv6_addr_copy(&fl6.saddr, &saddr6); ipv6_addr_copy(&fl6.daddr, &daddr6); diff --git a/net/ipv6/map_main.c b/net/ipv6/map_main.c index e6a68c8..36063dc 100644 --- a/net/ipv6/map_main.c +++ b/net/ipv6/map_main.c @@ -77,6 +77,65 @@ map_get_stats(struct net_device *dev) } int +map_pool_free(struct map *m, struct map_pool *mp) +{ + list_del(&mp->list); + kfree(mp); + return 0; +} + +int +map_pool_add(struct map *m, struct map_pool_parm *mpp) +{ + struct map_pool *mp; + + write_lock_bh(&m->pool_lock); + list_for_each_entry (mp, &m->pool_list, list) { + if (mp->p.pool_prefix == mpp->pool_prefix && + mp->p.pool_prefix_length == mpp->pool_prefix_length) { + write_unlock_bh(&m->pool_lock); + return -1; + } + } + mp = kmalloc(sizeof(*mp), GFP_KERNEL); + if (!mp) { + write_unlock_bh(&m->pool_lock); + return -1; + } + mp->p = *mpp; + list_add_tail(&mp->list, &m->pool_list); + m->p.pool_num += 1; + write_unlock_bh(&m->pool_lock); + + return 0; +} + +int +map_pool_change(struct map *m, struct map_pool_parm *mpp) +{ + return 0; +} + +int +map_pool_delete(struct map *m, struct map_pool_parm *mpp) +{ + struct map_pool *mp; + + write_lock_bh(&m->pool_lock); + list_for_each_entry (mp, &m->pool_list, list) { + if (mp->p.pool_prefix == mpp->pool_prefix && + mp->p.pool_prefix_length == mpp->pool_prefix_length) { + map_pool_free(m, mp); + break; + } + } + m->p.pool_num -= 1; + write_unlock_bh(&m->pool_lock); + + return 0; +} + +int map_get_addrport(struct iphdr *iph, __be32 *saddr4, __be32 *daddr4, __be16 *sport4, __be16 *dport4, __u8 *proto, int *icmperr) { @@ -245,6 +304,7 @@ map_v4v6(struct sk_buff *skb, struct map *m) __be16 sport4, dport4; __u8 proto; int icmperr; + int fb = 0; int df = 0; if (iph->frag_off & htons(IP_DF)) @@ -263,7 +323,11 @@ map_v4v6(struct sk_buff *skb, struct map *m) ntohl(iph->daddr) & 0xff); */ -retry: + if (ntohs(iph->frag_off) & IP_OFFSET) { + if (ip_defrag(skb, IP_DEFRAG_MAP46)) + return 0; + iph = ip_hdr(skb); + } map_get_addrport(iph, &saddr4, &daddr4, &sport4, &dport4, &proto, &icmperr); /* @@ -286,17 +350,10 @@ retry: mr = map_rule_find_by_ipv4addrport(m, &daddr4, &dport4, 1); if (mr) forwarding_mode = mr->p.forwarding_mode; - else if (m->p.role == MAP_ROLE_CE) + else { forwarding_mode = m->p.default_forwarding_mode; - else if (ntohs(iph->frag_off) & IP_OFFSET) { - if (ip_defrag(skb, IP_DEFRAG_MAP46)) - return 0; - iph = ip_hdr(skb); - goto retry; - } else { - printk(KERN_NOTICE "map_v4v6: forwarding rule miss.\n"); - err = -1; - goto drp; + if (m->p.role == MAP_ROLE_BR) + fb = 1; } if ((forwarding_mode != MAP_FORWARDING_MODE_T) && @@ -331,10 +388,10 @@ retry: switch (forwarding_mode) { case MAP_FORWARDING_MODE_T: - err = map_trans_forward_v4v6(skb, m, mr, df); + err = map_trans_forward_v4v6(skb, m, mr, fb, df); break; case MAP_FORWARDING_MODE_E: - err = map_encap_forward_v4v6(skb, m, mr); + err = map_encap_forward_v4v6(skb, m, mr, fb); break; } @@ -418,6 +475,7 @@ map_v6v4(struct sk_buff *skb, struct map *m) __be32 saddr4, daddr4; struct pcpu_tstats *tstats = this_cpu_ptr(m->dev->tstats); int rx_bytes = skb->len; + int fb = 0; int frag = 0; int err = 0; @@ -458,11 +516,11 @@ map_v6v4(struct sk_buff *skb, struct map *m) if (err) { goto drp; } - err = map_trans_validate_src(skb, m, &saddr4); - if (err) { + err = map_trans_validate_src(skb, m, &saddr4, &fb); + if (err && !fb) { goto drp; } - err = map_trans_forward_v6v4(skb, m, &saddr4, &daddr4, frag); + err = map_trans_forward_v6v4(skb, m, &saddr4, &daddr4, fb, frag); if (err) { printk(KERN_NOTICE "map_v6v4: " "map_trans_forward_v6v4 error.\n"); @@ -474,11 +532,11 @@ map_v6v4(struct sk_buff *skb, struct map *m) if (err) { goto drp; } - err = map_encap_validate_src(skb, m, &saddr4); - if (err) { + err = map_encap_validate_src(skb, m, &saddr4, &fb); + if (err && !fb) { goto drp; } - err = map_encap_forward_v6v4(skb, m, &saddr4, &daddr4); + err = map_encap_forward_v6v4(skb, m, &saddr4, &daddr4, fb); if (err) { printk(KERN_NOTICE "map_v6v4: " "map_encap_forward_v6v4 error.\n"); @@ -1062,8 +1120,10 @@ map_update(struct map *m) write_lock_bh(&m->port_range_lock); if (m->port_range) kfree(m->port_range); - m->port_range = NULL; - m->port_range_length = 0; + m->port_range = kmalloc(sizeof(struct map_napt_block), GFP_KERNEL); + m->port_range[0].min = 0x1000; + m->port_range[0].max = 0xffff; + m->port_range_length = 1; write_unlock_bh(&m->port_range_lock); } write_unlock_bh(&m->rule_lock); @@ -1088,7 +1148,8 @@ map_free(struct net_device *dev) { struct map *m = netdev_priv(dev); struct map_net *mapn = net_generic(dev_net(dev), map_net_id); - struct map_rule *mr, *q; + struct map_rule *mr, *mrq; + struct map_pool *mp, *mpq; struct map_defrag6_node *dn, *dn_node; printk(KERN_NOTICE "map_free: %s\n", m->dev->name); @@ -1105,7 +1166,7 @@ map_free(struct net_device *dev) write_unlock_bh(&m->port_range_lock); write_lock_bh(&m->rule_lock); - list_for_each_entry_safe (mr, q, &m->rule_list, list) { + list_for_each_entry_safe (mr, mrq, &m->rule_list, list) { /* list_del(&mr->list); kfree(mr); @@ -1114,9 +1175,16 @@ map_free(struct net_device *dev) m->p.rule_num -= 1; } write_unlock_bh(&m->rule_lock); - m->p.rule_num = 0; + write_lock_bh(&m->pool_lock); + list_for_each_entry_safe (mp, mpq, &m->pool_list, list) { + map_pool_free(m, mp); + m->p.pool_num -= 1; + } + write_unlock_bh(&m->pool_lock); + m->p.pool_num = 0; + write_lock_bh(&mapn->map_list_lock); list_del(&m->list); write_unlock_bh(&mapn->map_list_lock); @@ -1148,12 +1216,15 @@ map_init(struct net_device *dev, struct map_parm *p) if (p) m->p = *p; m->p.rule_num = 0; + m->p.pool_num = 0; m->dev = dev; INIT_LIST_HEAD(&m->rule_list); m->mrtn_root_ipv6addr = NULL; m->mrtn_root_ipv4addrport = NULL; + INIT_LIST_HEAD(&m->pool_list); /* */ rwlock_init(&m->rule_lock); + rwlock_init(&m->pool_lock); rwlock_init(&m->port_range_lock); for (h = 0; h < MAP_NAPT_HASH_LOOKUP_SIZE; ++h) @@ -1286,6 +1357,7 @@ map_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) struct map_parm p; struct map_parm *pp; struct map_rule_parm *rpp; + struct map_pool_parm *ppp; struct map_current_parm *cpp; struct map_napt_block *nbp; struct map_napt_parm *npp; @@ -1295,6 +1367,7 @@ map_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) int i, j; struct map *m = NULL; struct map_rule *mr = NULL; + struct map_pool *mp = NULL; struct net *net = dev_net(dev); struct map_net *mapn = net_generic(net, map_net_id); unsigned long current_time; @@ -1577,6 +1650,7 @@ map_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) npp->napt_node[j].rport = nn->rport; npp->napt_node[j].lport = nn->lport; npp->napt_node[j].mport = nn->mport; + npp->napt_node[j].laddr6 = nn->laddr6; npp->napt_node[j].proto = nn->proto; npp->napt_node[j].flags = nn->flags; // npp->napt_node[j].last_used = nn->last_used; @@ -1598,6 +1672,76 @@ map_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) err = -EFAULT; kfree(npp); break; + case SIOCGETMAPPOOLS: + if (dev == mapn->map_fb_dev) { + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, + sizeof(p))) { + err = -EFAULT; + break; + } + m = map_find_or_create(net, &p, 0); + } + if (m == NULL) + m = netdev_priv(dev); + size = sizeof(*pp) + sizeof(*ppp) * m->p.pool_num; + pp = kmalloc(size, GFP_KERNEL); + if (!pp) { + err = -EFAULT; + break; + } + *pp = m->p; + ppp = pp->pool; + read_lock_bh(&m->pool_lock); + list_for_each_entry (mp, &m->pool_list, list) { + *ppp = mp->p; + ++ppp; + } + read_unlock_bh(&m->pool_lock); + if (copy_to_user(ifr->ifr_ifru.ifru_data, pp, size)) + err = -EFAULT; + kfree(pp); + break; + case SIOCADDMAPPOOLS: + case SIOCDELMAPPOOLS: + case SIOCCHGMAPPOOLS: + if (!capable(CAP_NET_ADMIN)) { + err = -EPERM; + break; + } + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) { + err = -EFAULT; + break; + } + if (dev == mapn->map_fb_dev) + m = map_find_or_create(net, &p, 0); + if (m == NULL) + m = netdev_priv(dev); + size = sizeof(*pp) + sizeof(*ppp) * p.pool_num; + pp = kmalloc(size, GFP_KERNEL); + if (!pp) { + err = -EFAULT; + break; + } + if (copy_from_user(pp, ifr->ifr_ifru.ifru_data, size)) { + kfree(pp); + err = -EFAULT; + break; + } + for (i = 0; i < p.pool_num; i++) { + ppp = &pp->pool[i]; + if (cmd == SIOCADDMAPPOOLS) + if (map_pool_add(m, ppp) < 0) + err = -EFAULT; + if (cmd == SIOCCHGMAPPOOLS) + if (map_pool_change(m, ppp) < 0) + err = -EFAULT; + if (cmd == SIOCDELMAPPOOLS) + if (map_pool_delete(m, ppp) < 0) + err = -EFAULT; + } + kfree(pp); + map_napt_debug_pool(m); /* XXX: */ + break; default: printk(KERN_NOTICE "map_ioctl: ???\n"); } diff --git a/net/ipv6/map_napt.c b/net/ipv6/map_napt.c index 24a2f84..2e9287b 100644 --- a/net/ipv6/map_napt.c +++ b/net/ipv6/map_napt.c @@ -41,7 +41,7 @@ static int nn_kmem_alloced = 0; int map_napt_hairpin(struct sk_buff *skb, struct map *m, __be32 *daddrp, - __be16 *dportp) + __be16 *dportp, struct in6_addr *saddr6, int fb) { struct iphdr *iph; __be32 *saddrp = NULL; @@ -80,7 +80,7 @@ map_napt_hairpin(struct sk_buff *skb, struct map *m, __be32 *daddrp, psid <<= 16 - psid_offset - m->psid_length; if ((port & mask) == psid) { printk(KERN_NOTICE "map_napt_hairpin: hairpinning!\n"); - if (!map_napt(iph, 1, m, &saddrp, &sportp, &checkp)) { + if (!map_napt(iph, 1, m, &saddrp, &sportp, &checkp, saddr6, fb)) { /* XXX: */ skb->rxhash = 0; skb_set_queue_mapping(skb, 0); @@ -165,12 +165,20 @@ map_napt_nn_hash_create(__be32 addr, __u8 proto) static inline void map_napt_nn_debug(char *h, char *f, struct map_napt_node *nn) { - printk(KERN_INFO "%s: proto = %d " + printk(KERN_INFO "%s: proto = %d laddr6 = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x " "laddr = %d.%d.%d.%d lport = %d maddr = %d.%d.%d.%d mport = %d " "raddr = %d.%d.%d.%d rport = %d last_used = %lu " "nn_kmem_alloced = %d %s\n", h, nn->proto, + ntohs(nn->laddr6.s6_addr16[0]), + ntohs(nn->laddr6.s6_addr16[1]), + ntohs(nn->laddr6.s6_addr16[2]), + ntohs(nn->laddr6.s6_addr16[3]), + ntohs(nn->laddr6.s6_addr16[4]), + ntohs(nn->laddr6.s6_addr16[5]), + ntohs(nn->laddr6.s6_addr16[6]), + ntohs(nn->laddr6.s6_addr16[7]), ntohl(nn->laddr) >> 24, (ntohl(nn->laddr) >> 16) & 0xff, (ntohl(nn->laddr) >> 8) & 0xff, @@ -271,7 +279,7 @@ map_napt_nn_gc(struct map *m) static struct map_napt_node* map_napt_nn_create(__be32 saddr, __be16 sport, __be32 daddr, __be16 dport, - __u8 proto, struct map *m) + __u8 proto, struct in6_addr *saddr6, __be32 paddr, struct map *m) { struct map_napt_node *nn, *nn_node; struct hlist_node *node; @@ -286,7 +294,8 @@ map_napt_nn_create(__be32 saddr, __be16 sport, __be32 daddr, __be16 dport, hlist_for_each_entry (nn, node, &m->napt_hash_crat[h], nn_hash_crat) if (nn->proto == proto && nn->raddr == daddr && nn->rport == dport - && nn->laddr == saddr && nn->lport == sport) { + && nn->laddr == saddr && nn->lport == sport + && (!saddr6 || ipv6_addr_equal(&nn->laddr6, saddr6))) { printk(KERN_INFO "map_napt_nn_create: " "napt node found.\n"); goto out; @@ -295,7 +304,7 @@ map_napt_nn_create(__be32 saddr, __be16 sport, __be32 daddr, __be16 dport, hl0 = map_napt_nn_hash_lookup(saddr, sport, proto); hlist_for_each_entry (nn, node, &m->napt_hash_lup0[hl0], nn_hash_lup0) { if (nn->proto == proto && nn->laddr == saddr && - nn->lport == sport) { + nn->lport == sport && (!saddr6 || ipv6_addr_equal(&nn->laddr6, saddr6))) { printk(KERN_NOTICE "map_napt_nn_create: " "Endpoint-Independent Mapping:\n"); map_napt_nn_debug("map_napt_nn_create: " @@ -363,8 +372,12 @@ recycle: nn->rport = dport; nn->laddr = saddr; nn->lport = sport; - nn->maddr = m->laddr4; + nn->maddr = paddr; nn->mport = p; + if (saddr6) + memcpy(&nn->laddr6, saddr6, sizeof(*saddr6)); + else + memset(&nn->laddr6, 0, sizeof(*saddr6)); nn->flags = 0; nn->last_used = jiffies; hl0 = map_napt_nn_hash_lookup(nn->laddr, nn->lport, proto); @@ -392,7 +405,7 @@ out: static struct map_napt_node* map_napt_nn_lookup(__be32 saddr, __be16 sport, __be32 waddr, __be16 wport, - __u8 proto, int dir, struct map *m) + __u8 proto, struct in6_addr *saddr6, int dir, struct map *m) { struct map_napt_node *nn; struct hlist_node *node; @@ -401,7 +414,7 @@ map_napt_nn_lookup(__be32 saddr, __be16 sport, __be32 waddr, __be16 wport, u32 h; /* - printk(KERN_NOTICE "map_napt_nn_lookup(%s): \n", dir ? "in" : "out"); + printk(KERN_NOTICE "map_napt_nn_lookup (%s): \n", dir ? "in" : "out"); */ h = map_napt_nn_hash_lookup(saddr, sport, proto); @@ -416,7 +429,7 @@ map_napt_nn_lookup(__be32 saddr, __be16 sport, __be32 waddr, __be16 wport, && sa == saddr && sp == sport && !map_napt_nn_expired(nn)) { /* - printk(KERN_NOTICE "map_napt_nn_lookup(%s): " + printk(KERN_NOTICE "map_napt_nn_lookup (%s): " "match!\n", dir ? "in" : "out"); */ return nn; @@ -430,9 +443,10 @@ map_napt_nn_lookup(__be32 saddr, __be16 sport, __be32 waddr, __be16 wport, if (nn->proto == proto && wa == waddr && wp == wport && sa == saddr && sp == sport + && (!saddr6 || ipv6_addr_equal(&nn->laddr6, saddr6)) && !map_napt_nn_expired(nn)) { /* - printk(KERN_NOTICE "map_napt_nn_lookup(%s): " + printk(KERN_NOTICE "map_napt_nn_lookup (%s): " "match!\n", dir ? "in" : "out"); */ return nn; @@ -440,7 +454,7 @@ map_napt_nn_lookup(__be32 saddr, __be16 sport, __be32 waddr, __be16 wport, } /* - printk(KERN_NOTICE "map_napt_nn_lookup(%s): miss!\n", + printk(KERN_NOTICE "map_napt_nn_lookup (%s): miss!\n", dir ? "in" : "out"); */ @@ -514,7 +528,7 @@ map_napt_set_flags(struct map_napt_node *nn, __u8 flags, int dir) static int map_napt_update(__be32 *saddrp, __be16 *sportp, __be32 waddr, __be16 wport, - __u8 proto, __sum16 *checkp, __be32 *icmpaddr, int dir, + __u8 proto, struct in6_addr *saddr6, __be32 paddr, __sum16 *checkp, __be32 *icmpaddr, int dir, __u8 flags, int nested_icmp, struct map *m) { __be32 naddr = 0; @@ -523,7 +537,7 @@ map_napt_update(__be32 *saddrp, __be16 *sportp, __be32 waddr, __be16 wport, __u8 orig_flags; /* - printk(KERN_NOTICE "map_napt_update(%s):\n", dir ? "in" : "out"); + printk(KERN_NOTICE "map_napt_update (%s):\n", dir ? "in" : "out"); */ if (proto == IPPROTO_ICMP) @@ -545,12 +559,14 @@ map_napt_update(__be32 *saddrp, __be16 *sportp, __be32 waddr, __be16 wport, */ write_lock_bh(&m->napt_lock); - nn = map_napt_nn_lookup(*saddrp, *sportp, waddr, wport, proto, dir, m); + nn = map_napt_nn_lookup(*saddrp, *sportp, waddr, wport, proto, saddr6, dir, m); if (nn) { orig_flags = nn->flags; map_napt_set_flags(nn, flags, dir); if (dir) { naddr = nn->laddr; nport = nn->lport; + if (saddr6) + memcpy(saddr6, &nn->laddr6, sizeof(nn->laddr6)); } else { naddr = nn->maddr; nport = nn->mport; } @@ -585,7 +601,7 @@ map_napt_update(__be32 *saddrp, __be16 *sportp, __be32 waddr, __be16 wport, } */ nn = map_napt_nn_create(*saddrp, *sportp, waddr, wport, - proto, m); + proto, saddr6, paddr, m); if (!nn) { /* printk(KERN_NOTICE "map_napt: " @@ -626,18 +642,97 @@ map_napt_update(__be32 *saddrp, __be16 *sportp, __be32 waddr, __be16 wport, return 0; } +static inline int +map_napt_first_pool(__be32 *first, struct map *m) +{ + struct map_pool *mp; + if (m->p.role == MAP_ROLE_CE) { + *first = m->laddr4; + return 0; + } + if (m->p.pool_num > 0) { + mp = list_first_entry(&m->pool_list, struct map_pool, list); + *first = mp->p.pool_prefix; + return 0; + } + return -1; +} + +static inline int +map_napt_next_pool(__be32 cur, __be32 *next, struct map *m) +{ + struct map_pool *mp; + __u32 mask; + if (m->p.role == MAP_ROLE_CE || m->p.pool_num == 0) + return -1; + read_lock_bh(&m->pool_lock); + list_for_each_entry (mp, &m->pool_list, list) { + mask = 0xffffffff << (32 - mp->p.pool_prefix_length); + if ((ntohl(cur) & mask) == ntohl(mp->p.pool_prefix)) { + if (((ntohl(cur) + 1) & mask) == ntohl(mp->p.pool_prefix)) { + *next = htonl(ntohl(cur) + 1); + read_unlock_bh(&m->pool_lock); + return 0; + } + if (!list_is_last(&mp->list, &m->pool_list)) { + mp = list_entry(mp->list.next, struct map_pool, list); + *next = mp->p.pool_prefix; + read_unlock_bh(&m->pool_lock); + return 0; + } + } + } + read_unlock_bh(&m->pool_lock); + return -1; +} + +void +map_napt_debug_pool(struct map *m) +{ + __be32 paddr; + if (!map_napt_first_pool(&paddr, m)) { + printk(KERN_NOTICE "map_napt_debug_pool: %d.%d.%d.%d\n", + ntohl(paddr) >> 24, + (ntohl(paddr) >> 16 & 0xff), + (ntohl(paddr) >> 8 & 0xff), + (ntohl(paddr) & 0xff)); + } else { + printk(KERN_NOTICE "map_napt_debug_pool: error\n"); + return; + } + while (!map_napt_next_pool(paddr, &paddr, m)) { + printk(KERN_NOTICE "map_napt_debug_pool: %d.%d.%d.%d\n", + ntohl(paddr) >> 24, + (ntohl(paddr) >> 16 & 0xff), + (ntohl(paddr) >> 8 & 0xff), + (ntohl(paddr) & 0xff)); + } +} + +static inline int +map_napt_needed(struct map *m, int fb) +{ + if (m->p.role == MAP_ROLE_BR && m->p.pool_num > 0 && fb) + return MAP_ROLE_BR; + if (m->p.role == MAP_ROLE_CE && m->bmr && + (m->psid_length > 0 || m->p.napt_always == MAP_NAPT_ALWAYS_T)) + return MAP_ROLE_CE; + return 0; +} + /** * @dir: 1 = in; 0 = out; **/ int map_napt(struct iphdr *iph, int dir, struct map *m, __be32 **waddrpp, - __be16 **wportpp, __sum16 **checkpp) + __be16 **wportpp, __sum16 **checkpp, struct in6_addr *saddr6, int fb) { __be32 *saddrp = NULL; __be16 *sportp = NULL; __u8 proto; __be32 *icmpaddr = NULL; + __be32 paddr; u8 flags = 0; u8 *ptr; int err = 0; @@ -771,52 +866,57 @@ map_napt(struct iphdr *iph, int dir, struct map *m, __be32 **waddrpp, goto out; } - if (m->p.role == MAP_ROLE_CE && m->bmr && - (m->psid_length > 0 || m->p.napt_always == MAP_NAPT_ALWAYS_T)) { - if (saddrp && sportp) { - err = map_napt_update(saddrp, sportp, **waddrpp, - **wportpp, proto, *checkpp, icmpaddr, dir, - flags, nested_icmp, m); - if (err) { - printk(KERN_NOTICE "map_napt: " - "map_napt_update failed. " - "dir = %d err = %d\n", dir, err); - printk(KERN_NOTICE "map_napt: " - "s=%d.%d.%d.%d:%d(%04x) " - "w=%d.%d.%d.%d:%d(%04x) proto=%d\n", - ((ntohl(*saddrp) >> 24) & 0xff), - ((ntohl(*saddrp) >> 16) & 0xff), - ((ntohl(*saddrp) >> 8) & 0xff), - ((ntohl(*saddrp)) & 0xff), - ntohs(*sportp), - ntohs(*sportp), - ((ntohl(**waddrpp) >> 24) & 0xff), - ((ntohl(**waddrpp) >> 16) & 0xff), - ((ntohl(**waddrpp) >> 8) & 0xff), - ((ntohl(**waddrpp)) & 0xff), - ntohs(**wportpp), - ntohs(**wportpp), - proto); - goto out; - } - /* XXX: */ - if (icmpiph) { - __sum16 ocheck, ncheck; - long csum; - ocheck = icmpiph->check; - icmpiph->check = 0; - icmpiph->check = ip_fast_csum( - (unsigned char *)icmpiph, icmpiph->ihl); - ncheck = icmpiph->check; - csum = ntohs(**checkpp); - csum = ~csum & 0xffff; - csum -= ntohs(ocheck) & 0xffff; - if (csum <= 0) { --csum; csum &= 0xffff; } - csum += ntohs(ncheck) & 0xffff; - if (csum & 0x10000) { ++csum; csum &= 0xffff; } - csum = ~csum & 0xffff; - **checkpp = htons(csum); - } + if (saddrp && sportp && map_napt_needed(m, fb)) { + err = map_napt_first_pool(&paddr, m); + if (err) { + printk(KERN_NOTICE "map_napt: map_napt_first_pool err.\n"); + goto out; + } +retry: + err = map_napt_update(saddrp, sportp, **waddrpp, + **wportpp, proto, saddr6, paddr, *checkpp, icmpaddr, dir, + flags, nested_icmp, m); + if (err) { + if (!map_napt_next_pool(paddr, &paddr, m)) + goto retry; + printk(KERN_NOTICE "map_napt: " + "map_napt_update failed(2). " + "dir = %d err = %d\n", dir, err); + printk(KERN_NOTICE "map_napt: " + "s=%d.%d.%d.%d:%d(%04x) " + "w=%d.%d.%d.%d:%d(%04x) proto=%d\n", + ((ntohl(*saddrp) >> 24) & 0xff), + ((ntohl(*saddrp) >> 16) & 0xff), + ((ntohl(*saddrp) >> 8) & 0xff), + ((ntohl(*saddrp)) & 0xff), + ntohs(*sportp), + ntohs(*sportp), + ((ntohl(**waddrpp) >> 24) & 0xff), + ((ntohl(**waddrpp) >> 16) & 0xff), + ((ntohl(**waddrpp) >> 8) & 0xff), + ((ntohl(**waddrpp)) & 0xff), + ntohs(**wportpp), + ntohs(**wportpp), + proto); + goto out; + } + /* XXX: */ + if (icmpiph) { + __sum16 ocheck, ncheck; + long csum; + ocheck = icmpiph->check; + icmpiph->check = 0; + icmpiph->check = ip_fast_csum( + (unsigned char *)icmpiph, icmpiph->ihl); + ncheck = icmpiph->check; + csum = ntohs(**checkpp); + csum = ~csum & 0xffff; + csum -= ntohs(ocheck) & 0xffff; + if (csum <= 0) { --csum; csum &= 0xffff; } + csum += ntohs(ncheck) & 0xffff; + if (csum & 0x10000) { ++csum; csum &= 0xffff; } + csum = ~csum & 0xffff; + **checkpp = htons(csum); } } diff --git a/net/ipv6/map_trans.c b/net/ipv6/map_trans.c index 0ad6adb..828cbfe 100644 --- a/net/ipv6/map_trans.c +++ b/net/ipv6/map_trans.c @@ -666,7 +666,7 @@ map_trans_icmp_v4v6(struct sk_buff **skb, struct icmp6hdr *icmp6h, /* XXX: */ int -map_trans_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4) +map_trans_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4, int *fb) { struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct map_rule *mr; @@ -712,11 +712,6 @@ map_trans_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4) return 0; } - mr = map_rule_find_by_ipv6addr(m, &ipv6h->saddr); - - if (!mr) - printk(KERN_NOTICE "map_trans_validate_src: " - "map rule not found.\n"); saddr = htonl(ntohl(ipv6h->saddr.s6_addr32[2]) << 8); saddr |= htonl(ntohl(ipv6h->saddr.s6_addr32[3]) >> 24); @@ -772,19 +767,25 @@ map_trans_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4) goto err; } + mr = map_rule_find_by_ipv6addr(m, &ipv6h->saddr); if (!mr) { - mr = map_rule_find_by_ipv4addrport(m, &saddr, &sport, 0); - if (!mr) { + if (m->p.role == MAP_ROLE_BR) { + *fb = 1; + goto fallback; + } else { err = -1; goto err; } } if (map_gen_addr6(&addr6, saddr, sport, mr, 1)) { - printk(KERN_NOTICE "map_trans_validate_src: " - "map_gen_addr6 failed.\n"); - err = -1; - goto err; + if (m->p.role == MAP_ROLE_BR) { + *fb = 1; + goto fallback; + } else { + err = -1; + goto err; + } } if (mr->p.ipv4_prefix_length + mr->p.ea_length < 32) @@ -799,6 +800,7 @@ map_trans_validate_src(struct sk_buff *skb, struct map *m, __be32 *saddr4) goto err_icmpv6_send; } +fallback: *saddr4 = htonl(ntohl(ipv6h->saddr.s6_addr32[2]) << 8); *saddr4 |= htonl(ntohl(ipv6h->saddr.s6_addr32[3]) >> 24); @@ -996,7 +998,7 @@ err: int map_trans_forward_v6v4(struct sk_buff *skb, struct map *m, __be32 *saddr4, - __be32 *daddr4, int frag) + __be32 *daddr4, int fb, int frag) { struct ipv6hdr orig_ipv6h = {}, *ipv6h; struct frag_hdr orig_fragh = {}, *fragh; @@ -1006,12 +1008,14 @@ map_trans_forward_v6v4(struct sk_buff *skb, struct map *m, __be32 *saddr4, __be32 *saddrp = NULL; __be16 *sportp = NULL; __sum16 *checkp = NULL; + struct in6_addr *saddr6; u8 *ptr; int err = 0; ipv6h = ipv6_hdr(skb); memcpy(&orig_ipv6h, ipv6h, sizeof(orig_ipv6h)); + saddr6 = &orig_ipv6h.saddr; hsize = sizeof(orig_ipv6h); nexthdr = orig_ipv6h.nexthdr; if (orig_ipv6h.nexthdr == IPPROTO_FRAGMENT) { @@ -1072,7 +1076,15 @@ map_trans_forward_v6v4(struct sk_buff *skb, struct map *m, __be32 *saddr4, iph->saddr = *saddr4; iph->daddr = *daddr4; - err = map_napt(iph, 1, m, &saddrp, &sportp, &checkp); + if (m->p.role == MAP_ROLE_BR && fb) { + err = map_napt(iph, 0, m, &saddrp, &sportp, &checkp, saddr6, fb); + if (err) + goto err; + /* NAPT Hairpinning */ + if (map_napt_hairpin(skb, m, saddrp, sportp, saddr6, fb)) + goto out; + } else + err = map_napt(iph, 1, m, &saddrp, &sportp, &checkp, NULL, fb); if (err) { printk(KERN_NOTICE "map_trans_forward_v6v4: " "saddr:%d.%d.%d.%d daddr:%d.%d.%d.%d\n", @@ -1106,11 +1118,12 @@ map_trans_forward_v6v4(struct sk_buff *skb, struct map *m, __be32 *saddr4, err: map_debug_print_skb("map_trans_forward_v6v4", skb); +out: return err; } int -map_trans_forward_v4v6(struct sk_buff *skb, struct map *m, struct map_rule *mr, int df) +map_trans_forward_v4v6(struct sk_buff *skb, struct map *m, struct map_rule *mr, int fb, int df) { struct flowi6 fl6; struct in6_addr saddr6, daddr6; @@ -1146,12 +1159,11 @@ map_trans_forward_v4v6(struct sk_buff *skb, struct map *m, struct map_rule *mr, iph = ip_hdr(skb); - err = map_napt(iph, 0, m, &daddrp, &dportp, &checkp); + err = map_napt(iph, 0, m, &daddrp, &dportp, &checkp, NULL, 0); if (err) goto err; - /* NAPT Hairpinning */ - if(map_napt_hairpin(skb, m, daddrp, dportp)) + if (map_napt_hairpin(skb, m, daddrp, dportp, NULL, 0)) goto out; iph->check = 0; @@ -1187,6 +1199,12 @@ map_trans_forward_v4v6(struct sk_buff *skb, struct map *m, struct map_rule *mr, } } + if (m->p.role == MAP_ROLE_BR && fb) { + err = map_napt(iph, 1, m, &daddrp, &dportp, &checkp, &daddr6, fb); + if (err) + goto err; + } + if (iph->protocol != IPPROTO_ICMP) *checkp = map_trans_update_csum_v4v6(*checkp, iph->saddr, iph->daddr, &saddr6, &daddr6);