Subject: [PATCH] nethost ipv6: Make ipv6 work by design. From: Eric W. Biederman Date: 1137526319 -0700 - This moves all of relevant global variables into struct ip6_host. - A generic bugfix in addrconf.c to flush routes only after I have freed all of the interfaces addresses otherwise the code leaks dst entries. --- include/net/ip6_fib.h | 7 + include/net/ip6_host.h | 11 +- include/net/ip6_route.h | 21 ++-- net/ipv6/addrconf.c | 59 ++++------ net/ipv6/af_inet6.c | 37 +++++- net/ipv6/anycast.c | 4 - net/ipv6/ip6_fib.c | 98 ++++++++++------- net/ipv6/ip6_output.c | 8 - net/ipv6/ip6_tunnel.c | 5 + net/ipv6/mcast.c | 4 - net/ipv6/ndisc.c | 6 + net/ipv6/raw.c | 2 net/ipv6/route.c | 279 ++++++++++++++++++++++++++++++----------------- net/ipv6/udp.c | 4 - net/ipv6/xfrm6_policy.c | 4 - 15 files changed, 337 insertions(+), 212 deletions(-) 0ec8d5b7dc10c08bbec3e2f34f6591a72a4c56b3 diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index fecda49..6dc1e3f 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -143,8 +143,6 @@ struct rt6_statistics { typedef void (*f_pnode)(struct fib6_node *fn, void *); -extern struct fib6_node ip6_routing_table; - /* * exported functions */ @@ -179,10 +177,13 @@ extern void inet6_rt_notify(int event, struct nlmsghdr *nlh, struct sk_buff *in_skb); -extern void fib6_run_gc(unsigned long dummy); +extern void fib6_run_gc(struct ip6_host *ihost, unsigned long expires); extern void fib6_gc_cleanup(void); extern void fib6_init(void); + +extern void fib6_gc_host_init(struct ip6_host *ihost); +extern void fib6_gc_host_cleanup(struct ip6_host *ihost); #endif #endif diff --git a/include/net/ip6_host.h b/include/net/ip6_host.h index 15b94f0..d67afc2 100644 --- a/include/net/ip6_host.h +++ b/include/net/ip6_host.h @@ -1,16 +1,19 @@ #ifndef _NET_IP6_HOST_H #define _NET_IP6_HOST_H +#include #include +#include struct ip6_host { struct nethost *host; + struct rt6_info ip6_null_entry; + struct fib6_node ip6_routing_table; + struct timer_list ip6_fib_timer; + struct rt6_info *rt6_dflt_pointer; }; -static inline struct ip_host *in6_host_get(struct nethost *host) -{ - return host->ip_host; -} +extern struct ip6_host *ip6_host_get(struct nethost *host); #endif /* _NET_IP6_HOST_H */ diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 248e920..c632f20 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -16,6 +16,8 @@ #include #include +struct ip6_host; + struct pol_chain { int type; int priority; @@ -23,8 +25,6 @@ struct pol_chain { struct pol_chain *next; }; -extern struct rt6_info ip6_null_entry; - extern int ip6_rt_gc_interval; extern void ip6_route_input(struct sk_buff *skb); @@ -37,6 +37,9 @@ extern int ip6_route_me_harder(struct extern void ip6_route_init(void); extern void ip6_route_cleanup(void); +extern void ip6_route_host_init(struct ip6_host *ihost); +extern void ip6_route_host_cleanup(struct ip6_host *ihost); + extern int ipv6_route_ioctl(unsigned int cmd, void __user *arg); extern int ip6_route_add(struct nethost *host, @@ -44,7 +47,8 @@ extern int ip6_route_add(struct nethos struct nlmsghdr *, void *rtattr, struct sk_buff *in_skb); -extern int ip6_ins_rt(struct rt6_info *, +extern int ip6_ins_rt(struct nethost *host, + struct rt6_info *, struct nlmsghdr *, void *rtattr, struct sk_buff *in_skb); @@ -67,7 +71,8 @@ extern void rt6_sndmsg(int type, struc int dstlen, int srclen, int metric, __u32 flags); -extern struct rt6_info *rt6_lookup(struct in6_addr *daddr, +extern struct rt6_info *rt6_lookup(struct nethost *host, + struct in6_addr *daddr, struct in6_addr *saddr, int oif, int flags); @@ -76,7 +81,7 @@ extern struct dst_entry *ndisc_dst_alloc struct in6_addr *addr, int (*output)(struct sk_buff *)); extern int ndisc_dst_gc(int *more); -extern void fib6_force_start_gc(void); +extern void fib6_force_start_gc(struct nethost *host); extern struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, const struct in6_addr *addr, @@ -91,9 +96,9 @@ extern struct rt6_info * rt6_get_dflt_ro extern struct rt6_info * rt6_add_dflt_router(struct in6_addr *gwaddr, struct net_device *dev); -extern void rt6_purge_dflt_routers(void); +extern void rt6_purge_dflt_routers(struct nethost *host); -extern void rt6_reset_dflt_pointer(struct rt6_info *rt); +extern void rt6_reset_dflt_pointer(struct nethost *host, struct rt6_info *rt); extern void rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr, @@ -113,7 +118,7 @@ extern int inet6_rtm_newroute(struct sk_ extern int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg); extern int inet6_rtm_getroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg); -extern void rt6_ifdown(struct net_device *dev); +extern void rt6_ifdown(struct nethost *host, struct net_device *dev); extern void rt6_mtu_change(struct net_device *dev, unsigned mtu); extern rwlock_t rt6_lock; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 587059c..08507e3 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -70,6 +70,7 @@ #include #include #include +#include #include #include @@ -692,7 +693,7 @@ static void ipv6_del_addr(struct inet6_i struct rt6_info *rt; ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len); - rt = rt6_lookup(&prefix, NULL, ifp->idev->dev->ifindex, 1); + rt = rt6_lookup(ifp->idev->dev->host, &prefix, NULL, ifp->idev->dev->ifindex, 1); if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) { if (onlink == 0) { @@ -1466,7 +1467,7 @@ void addrconf_prefix_rcv(struct net_devi if (pinfo->onlink) { struct rt6_info *rt; - rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, 1); + rt = rt6_lookup(dev->host, &pinfo->prefix, NULL, dev->ifindex, 1); if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) { if (rt->rt6i_flags&RTF_EXPIRES) { @@ -1992,8 +1993,21 @@ static int addrconf_notify(struct notifi { struct net_device *dev = (struct net_device *) data; struct inet6_dev *idev = __in6_dev_get(dev); + struct ip6_host *ihost = ip6_host_get(dev->host); + + /* If we don't have a support for this interface forget it */ + if (unlikely(!ihost)) + return NOTIFY_OK; switch(event) { + case NETDEV_REGISTER: + if (dev->flags & IFF_LOOPBACK) { + if (!idev) + idev = ipv6_find_idev(dev); + if (idev) + ihost->ip6_null_entry.rt6i_idev = idev; + } + break; case NETDEV_UP: switch(dev->type) { case ARPHRD_SIT: @@ -2084,22 +2098,6 @@ static int addrconf_ifdown(struct net_de if (dev == &init_host.loopback_dev && how == 1) how = 0; -#if 1 - idev = __in6_dev_get(dev); - if (idev) { - read_lock_bh(&idev->lock); - while ((ifa = idev->addr_list) != NULL) { - in6_ifa_hold(ifa); - read_unlock_bh(&idev->lock); - - ipv6_del_addr(ifa); - - read_lock_bh(&idev->lock); - } - read_unlock_bh(&idev->lock); - } -#endif - rt6_ifdown(dev); neigh_ifdown(&nd_tbl, dev); idev = __in6_dev_get(dev); @@ -2190,7 +2188,10 @@ static int addrconf_ifdown(struct net_de /* Step 5: netlink notification of this interface */ idev->tstamp = jiffies; inet6_ifinfo_notify(RTM_NEWLINK, idev); - + + /* Step 6: Flush any remaining routes for this device */ + rt6_ifdown(dev->host, dev); + /* Shot the device (if unregistered) */ if (how == 1) { @@ -3117,7 +3118,7 @@ static void __ipv6_ifa_notify(int event, switch (event) { case RTM_NEWADDR: dst_hold(&ifp->rt->u.dst); - if (ip6_ins_rt(ifp->rt, NULL, NULL, NULL)) + if (ip6_ins_rt(ifp->idev->dev->host, ifp->rt, NULL, NULL, NULL)) dst_release(&ifp->rt->u.dst); if (ifp->idev->cnf.forwarding) addrconf_join_anycast(ifp); @@ -3168,7 +3169,7 @@ int addrconf_sysctl_forward(ctl_table *c addrconf_forward_change(); } if (*valp) - rt6_purge_dflt_routers(); + rt6_purge_dflt_routers(current->host); } return ret; @@ -3222,7 +3223,7 @@ static int addrconf_sysctl_forward_strat } if (*valp) - rt6_purge_dflt_routers(); + rt6_purge_dflt_routers(current->host); } else *valp = new; @@ -3525,8 +3526,6 @@ int unregister_inet6addr_notifier(struct int __init addrconf_init(void) { - int err = 0; - /* The addrconf netdev notifier requires that loopback_dev * has it's ipv6 private information allocated and setup * before it can bring up and give link-local addresses @@ -3545,15 +3544,9 @@ int __init addrconf_init(void) * Longer term, all of the dependencies ipv6 has upon the loopback * device and it being up should be removed. */ - rtnl_lock(); - if (!ipv6_add_dev(&init_host.loopback_dev)) - err = -ENOMEM; - rtnl_unlock(); - if (err) - return err; - - ip6_null_entry.rt6i_idev = in6_dev_get(&init_host.loopback_dev); - + /* The loopback devices is now guaranteed to come first. + * Eric Biederman 16 Jan 2006. + */ register_netdevice_notifier(&ipv6_dev_notf); #ifdef CONFIG_IPV6_PRIVACY diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 42a5348..bb90513 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -89,13 +89,16 @@ static __inline__ struct ipv6_pinfo *ine */ static int inet6_host_init(struct nethost *host) { - /* FIXME flesh me out */ - struct ip6_host *ihost; - ihost = kzalloc(sizeof(*ihost), GFP_KERNEL); - if (!ihost) - return -ENOMEM; - ihost->host = host; - host->ip6_host = ihost; + if (!host->ip6_host) { + struct ip6_host *ihost; + ihost = kzalloc(sizeof(*ihost), GFP_KERNEL); + if (!ihost) + return -ENOMEM; + ihost->host = host; + host->ip6_host = ihost; + + ip6_route_host_init(ihost); + } return 0; } @@ -106,13 +109,28 @@ static int inet6_host_init(struct nethos static void inet6_host_fini(struct nethost *host) { struct ip6_host *ihost; + + ASSERT_RTNL(); + ihost = host->ip6_host; host->ip6_host = NULL; + + ip6_route_host_cleanup(ihost); + kfree(ihost); } +struct ip6_host *ip6_host_get(struct nethost *host) +{ + struct ip6_host *ihost = NULL; + if (inet6_host_init(host) == 0) + ihost = host->ip6_host; + return ihost; +} + static int inet6_create(struct socket *sock, int protocol) { + struct ip6_host *ihost; struct inet_sock *inet; struct ipv6_pinfo *np; struct sock *sk; @@ -123,6 +141,11 @@ static int inet6_create(struct socket *s char answer_no_check; int rc; + rc = -ESOCKTNOSUPPORT; + ihost = ip6_host_get(current->host); + if (!ihost) + goto out; + /* Look for the requested type/protocol pair. */ answer = NULL; rcu_read_lock(); diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 8d4fe4a..49b14cc 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -101,7 +101,7 @@ int ipv6_sock_ac_join(struct sock *sk, i if (ifindex == 0) { struct rt6_info *rt; - rt = rt6_lookup(addr, NULL, 0, 0); + rt = rt6_lookup(sk->sk_host, addr, NULL, 0, 0); if (rt) { dev = rt->rt6i_dev; dev_hold(dev); @@ -337,7 +337,7 @@ int ipv6_dev_ac_inc(struct net_device *d write_unlock_bh(&idev->lock); dst_hold(&rt->u.dst); - if (ip6_ins_rt(rt, NULL, NULL, NULL)) + if (ip6_ins_rt(dev->host, rt, NULL, NULL, NULL)) dst_release(&rt->u.dst); addrconf_join_solict(dev, &aca->aca_addr); diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index a0e15bf..e573bbb 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -38,6 +38,7 @@ #include #include +#include #define RT6_DEBUG 2 @@ -81,7 +82,7 @@ DEFINE_RWLOCK(fib6_walker_lock); #endif static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt); -static struct fib6_node * fib6_repair_tree(struct fib6_node *fn); +static struct fib6_node * fib6_repair_tree(struct ip6_host *ihost, struct fib6_node *fn); /* * A routing update causes an increase of the serial number on the @@ -92,8 +93,6 @@ static struct fib6_node * fib6_repair_tr static __u32 rt_sernum; -static DEFINE_TIMER(ip6_fib_timer, fib6_run_gc, 0, 0); - struct fib6_walker_t fib6_walker_list = { .prev = &fib6_walker_list, .next = &fib6_walker_list, @@ -393,7 +392,8 @@ insert_above: * Insert routing information in a node. */ -static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, +static int fib6_add_rt2node(struct ip6_host *ihost, + struct fib6_node *fn, struct rt6_info *rt, struct nlmsghdr *nlh, struct sk_buff *in_skb) { struct rt6_info *iter = NULL; @@ -402,7 +402,7 @@ static int fib6_add_rt2node(struct fib6_ ins = &fn->leaf; if (fn->fn_flags&RTN_TL_ROOT && - fn->leaf == &ip6_null_entry && + fn->leaf == &ihost->ip6_null_entry && !(rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ){ fn->leaf = rt; rt->u.next = NULL; @@ -460,17 +460,18 @@ out: return 0; } -static __inline__ void fib6_start_gc(struct rt6_info *rt) +static __inline__ void fib6_start_gc(struct ip6_host *ihost, struct rt6_info *rt) { - if (ip6_fib_timer.expires == 0 && + if (ihost->ip6_fib_timer.expires == 0 && (rt->rt6i_flags & (RTF_EXPIRES|RTF_CACHE))) - mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval); + mod_timer(&ihost->ip6_fib_timer, jiffies + ip6_rt_gc_interval); } -void fib6_force_start_gc(void) +void fib6_force_start_gc(struct nethost *host) { - if (ip6_fib_timer.expires == 0) - mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval); + struct ip6_host *ihost = host->ip6_host; + if (ihost->ip6_fib_timer.expires == 0) + mod_timer(&ihost->ip6_fib_timer, jiffies + ip6_rt_gc_interval); } /* @@ -482,6 +483,7 @@ void fib6_force_start_gc(void) int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nlmsghdr *nlh, void *_rtattr, struct sk_buff *in_skb) { + struct ip6_host *ihost = rt->rt6i_dev->host->ip6_host; struct fib6_node *fn; int err = -ENOMEM; @@ -513,8 +515,8 @@ int fib6_add(struct fib6_node *root, str if (sfn == NULL) goto st_failure; - sfn->leaf = &ip6_null_entry; - atomic_inc(&ip6_null_entry.rt6i_ref); + sfn->leaf = &ihost->ip6_null_entry; + atomic_inc(&ihost->ip6_null_entry.rt6i_ref); sfn->fn_flags = RTN_ROOT; sfn->fn_sernum = fib6_new_sernum(); @@ -553,10 +555,10 @@ int fib6_add(struct fib6_node *root, str } #endif - err = fib6_add_rt2node(fn, rt, nlh, in_skb); + err = fib6_add_rt2node(ihost, fn, rt, nlh, in_skb); if (err == 0) { - fib6_start_gc(rt); + fib6_start_gc(ihost, rt); if (!(rt->rt6i_flags&RTF_CACHE)) fib6_prune_clones(fn, rt); } @@ -572,7 +574,7 @@ out: */ st_failure: if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT))) - fib6_repair_tree(fn); + fib6_repair_tree(ihost, fn); dst_free(&rt->u.dst); return err; #endif @@ -738,10 +740,10 @@ struct fib6_node * fib6_locate(struct fi * */ -static struct rt6_info * fib6_find_prefix(struct fib6_node *fn) +static struct rt6_info * fib6_find_prefix(struct ip6_host *ihost, struct fib6_node *fn) { if (fn->fn_flags&RTN_ROOT) - return &ip6_null_entry; + return &ihost->ip6_null_entry; while(fn) { if(fn->left) @@ -760,7 +762,7 @@ static struct rt6_info * fib6_find_prefi * is the node we want to try and remove. */ -static struct fib6_node * fib6_repair_tree(struct fib6_node *fn) +static struct fib6_node * fib6_repair_tree(struct ip6_host *ihost, struct fib6_node *fn) { int children; int nstate; @@ -787,11 +789,11 @@ static struct fib6_node * fib6_repair_tr || (children && fn->fn_flags&RTN_ROOT) #endif ) { - fn->leaf = fib6_find_prefix(fn); + fn->leaf = fib6_find_prefix(ihost, fn); #if RT6_DEBUG >= 2 if (fn->leaf==NULL) { BUG_TRAP(fn->leaf); - fn->leaf = &ip6_null_entry; + fn->leaf = &ihost->ip6_null_entry; } #endif atomic_inc(&fn->leaf->rt6i_ref); @@ -859,7 +861,8 @@ static struct fib6_node * fib6_repair_tr } } -static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, +static void fib6_del_route(struct ip6_host *ihost, + struct fib6_node *fn, struct rt6_info **rtp, struct nlmsghdr *nlh, void *_rtattr, struct sk_buff *in_skb) { struct fib6_walker_t *w; @@ -888,13 +891,13 @@ static void fib6_del_route(struct fib6_n rt->u.next = NULL; if (fn->leaf == NULL && fn->fn_flags&RTN_TL_ROOT) - fn->leaf = &ip6_null_entry; + fn->leaf = &ihost->ip6_null_entry; /* If it was last route, expunge its radix tree node */ if (fn->leaf == NULL) { fn->fn_flags &= ~RTN_RTINFO; rt6_stats.fib_route_nodes--; - fn = fib6_repair_tree(fn); + fn = fib6_repair_tree(ihost, fn); } if (atomic_read(&rt->rt6i_ref) != 1) { @@ -906,7 +909,7 @@ static void fib6_del_route(struct fib6_n */ while (fn) { if (!(fn->fn_flags&RTN_RTINFO) && fn->leaf == rt) { - fn->leaf = fib6_find_prefix(fn); + fn->leaf = fib6_find_prefix(ihost, fn); atomic_inc(&fn->leaf->rt6i_ref); rt6_release(rt); } @@ -922,6 +925,7 @@ static void fib6_del_route(struct fib6_n int fib6_del(struct rt6_info *rt, struct nlmsghdr *nlh, void *_rtattr, struct sk_buff *in_skb) { + struct ip6_host *ihost = rt->rt6i_dev->host->ip6_host; struct fib6_node *fn = rt->rt6i_node; struct rt6_info **rtp; @@ -931,7 +935,7 @@ int fib6_del(struct rt6_info *rt, struct return -ENOENT; } #endif - if (fn == NULL || rt == &ip6_null_entry) + if (fn == NULL || rt == &ihost->ip6_null_entry) return -ENOENT; BUG_TRAP(fn->fn_flags&RTN_RTINFO); @@ -945,7 +949,7 @@ int fib6_del(struct rt6_info *rt, struct for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->u.next) { if (*rtp == rt) { - fib6_del_route(fn, rtp, nlh, _rtattr, in_skb); + fib6_del_route(ihost, fn, rtp, nlh, _rtattr, in_skb); return 0; } } @@ -1155,7 +1159,7 @@ static int fib6_age(struct rt6_info *rt, if (rt->rt6i_flags&RTF_EXPIRES && rt->rt6i_expires) { if (time_after(now, rt->rt6i_expires)) { RT6_TRACE("expiring %p\n", rt); - rt6_reset_dflt_pointer(rt); + rt6_reset_dflt_pointer(rt->rt6i_dev->host, rt); return -1; } gc_args.more++; @@ -1178,15 +1182,17 @@ static int fib6_age(struct rt6_info *rt, static DEFINE_SPINLOCK(fib6_gc_lock); -void fib6_run_gc(unsigned long dummy) +void fib6_run_gc(struct ip6_host *ihost, unsigned long expires) { - if (dummy != ~0UL) { + struct timer_list *ip6_fib_timer; + ip6_fib_timer = &ihost->ip6_fib_timer; + if (expires != ~0UL) { spin_lock_bh(&fib6_gc_lock); - gc_args.timeout = dummy ? (int)dummy : ip6_rt_gc_interval; + gc_args.timeout = expires ? (int)expires : ip6_rt_gc_interval; } else { local_bh_disable(); if (!spin_trylock(&fib6_gc_lock)) { - mod_timer(&ip6_fib_timer, jiffies + HZ); + mod_timer(ip6_fib_timer, jiffies + HZ); local_bh_enable(); return; } @@ -1197,18 +1203,37 @@ void fib6_run_gc(unsigned long dummy) write_lock_bh(&rt6_lock); ndisc_dst_gc(&gc_args.more); - fib6_clean_tree(&ip6_routing_table, fib6_age, 0, NULL); + fib6_clean_tree(&ihost->ip6_routing_table, fib6_age, 0, NULL); write_unlock_bh(&rt6_lock); if (gc_args.more) - mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval); + mod_timer(ip6_fib_timer, jiffies + ip6_rt_gc_interval); else { - del_timer(&ip6_fib_timer); - ip6_fib_timer.expires = 0; + del_timer(ip6_fib_timer); + ip6_fib_timer->expires = 0; } spin_unlock_bh(&fib6_gc_lock); } +static void fib6_timer_run_gc(unsigned long dummy) +{ + struct ip6_host *ihost; + ihost = (struct ip6_host *)dummy; + fib6_run_gc(ihost, 0); +} + +void fib6_gc_host_init(struct ip6_host *ihost) +{ + init_timer(&ihost->ip6_fib_timer); + ihost->ip6_fib_timer.data = (unsigned long)ihost; + ihost->ip6_fib_timer.function = fib6_timer_run_gc; +} + +void fib6_gc_host_cleanup(struct ip6_host *ihost) +{ + del_timer(&ihost->ip6_fib_timer); +} + void __init fib6_init(void) { fib6_node_kmem = kmem_cache_create("fib6_nodes", @@ -1221,6 +1246,5 @@ void __init fib6_init(void) void fib6_gc_cleanup(void) { - del_timer(&ip6_fib_timer); kmem_cache_destroy(fib6_node_kmem); } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 3cdba47..10f7a7c 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -711,14 +711,6 @@ int ip6_dst_lookup(struct sock *sk, stru { int err = 0; - /* If the destinaition is the loopback address, use iif to select my - * current loopback interface. The routing code isn't strict about - * matching the interface but matches it if it can, so there is - * no need to consider a requested output interface here. - */ - if (ipv6_addr_type(&fl->fl6_dst) & IPV6_ADDR_LOOPBACK) - fl->oif = fl->iif; - *dst = NULL; if (sk) { struct ipv6_pinfo *np = inet6_sk(sk); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 701ec1f..e085f41 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -467,6 +467,7 @@ ip6ip6_err(struct sk_buff *skb, struct i break; } if (rel_msg && pskb_may_pull(skb, offset + sizeof (*ipv6h))) { + struct nethost *host = ipv6_host(skb); struct rt6_info *rt; struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (!skb2) @@ -478,7 +479,7 @@ ip6ip6_err(struct sk_buff *skb, struct i skb2->nh.raw = skb2->data; /* Try to guess incoming interface */ - rt = rt6_lookup(&skb2->nh.ipv6h->saddr, NULL, 0, 0); + rt = rt6_lookup(host, &skb2->nh.ipv6h->saddr, NULL, 0, 0); if (rt && rt->rt6i_dev) skb2->dev = rt->rt6i_dev; @@ -846,7 +847,7 @@ static void ip6ip6_tnl_link_config(struc dev->iflink = p->link; if (p->flags & IP6_TNL_F_CAP_XMIT) { - struct rt6_info *rt = rt6_lookup(&p->raddr, &p->laddr, + struct rt6_info *rt = rt6_lookup(dev->host, &p->raddr, &p->laddr, p->link, 0); if (rt == NULL) diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 58602ce..290fa02 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -208,7 +208,7 @@ int ipv6_sock_mc_join(struct sock *sk, i if (ifindex == 0) { struct rt6_info *rt; - rt = rt6_lookup(addr, NULL, 0, 0); + rt = rt6_lookup(sk->sk_host, addr, NULL, 0, 0); if (rt) { dev = rt->rt6i_dev; dev_hold(dev); @@ -293,7 +293,7 @@ static struct inet6_dev *ip6_mc_find_dev if (ifindex == 0) { struct rt6_info *rt; - rt = rt6_lookup(group, NULL, 0, 0); + rt = rt6_lookup(host, group, NULL, 0, 0); if (rt) { dev = rt->rt6i_dev; dev_hold(dev); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index d77f9ec..c8a04c9 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -77,6 +77,7 @@ #include #include #include +#include #include #include @@ -1509,15 +1510,16 @@ int ndisc_rcv(struct sk_buff *skb) static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *dev = ptr; + struct ip6_host *ihost = ip6_host_get(dev->host); switch (event) { case NETDEV_CHANGEADDR: neigh_changeaddr(&nd_tbl, dev); - fib6_run_gc(~0UL); + fib6_run_gc(ihost, ~0UL); break; case NETDEV_DOWN: neigh_ifdown(&nd_tbl, dev); - fib6_run_gc(~0UL); + fib6_run_gc(ihost, ~0UL); break; default: break; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index e2bb377..623c706 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -107,7 +107,7 @@ struct sock *__raw_v6_lookup(struct sock goto found; continue; } - if (host && host != sk->sk_host) + if (host != sk->sk_host) continue; goto found; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 6668c05..f248949 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -54,6 +54,7 @@ #include #include #include +#include #include @@ -87,7 +88,7 @@ static struct dst_entry *ip6_negative_ad static void ip6_dst_destroy(struct dst_entry *); static void ip6_dst_ifdown(struct dst_entry *, struct net_device *dev, int how); -static int ip6_dst_gc(void); +static int ip6_dst_gc(struct nethost *host); static int ip6_pkt_discard(struct sk_buff *skb); static int ip6_pkt_discard_out(struct sk_buff *skb); @@ -108,41 +109,15 @@ static struct dst_ops ip6_dst_ops = { .entry_size = sizeof(struct rt6_info), }; -struct rt6_info ip6_null_entry = { - .u = { - .dst = { - .__refcnt = ATOMIC_INIT(1), - .__use = 1, - /* hardcoding the init_host loopback dev correct in the multihost case? */ - .dev = &init_host.loopback_dev, - .obsolete = -1, - .error = -ENETUNREACH, - .metrics = { [RTAX_HOPLIMIT - 1] = 255, }, - .input = ip6_pkt_discard, - .output = ip6_pkt_discard_out, - .ops = &ip6_dst_ops, - .path = (struct dst_entry*)&ip6_null_entry, - } - }, - .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), - .rt6i_metric = ~(u32) 0, - .rt6i_ref = ATOMIC_INIT(1), -}; - -struct fib6_node ip6_routing_table = { - .leaf = &ip6_null_entry, - .fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO, -}; - /* Protects all the ip6 fib */ DEFINE_RWLOCK(rt6_lock); /* allocate dst with ip6_dst_ops */ -static __inline__ struct rt6_info *ip6_dst_alloc(void) +static __inline__ struct rt6_info *ip6_dst_alloc(struct nethost *host) { - return (struct rt6_info *)dst_alloc(&ip6_dst_ops); + return (struct rt6_info *)dst_alloc(&ip6_dst_ops, host); } static void ip6_dst_destroy(struct dst_entry *dst) @@ -182,9 +157,10 @@ static __inline__ int rt6_check_expired( * Route lookup. Any rt6_lock is implied. */ -static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt, - int oif, - int strict) +static struct rt6_info *rt6_device_match(struct ip6_host *ihost, + struct rt6_info *rt, + int oif, + int strict) { struct rt6_info *local = NULL; struct rt6_info *sprt; @@ -211,7 +187,7 @@ static __inline__ struct rt6_info *rt6_d return local; if (strict) - return &ip6_null_entry; + return &ihost->ip6_null_entry; } return rt; } @@ -219,21 +195,21 @@ static __inline__ struct rt6_info *rt6_d /* * pointer to the last default router chosen. BH is disabled locally. */ -static struct rt6_info *rt6_dflt_pointer; static DEFINE_SPINLOCK(rt6_dflt_lock); -void rt6_reset_dflt_pointer(struct rt6_info *rt) +void rt6_reset_dflt_pointer(struct nethost *host, struct rt6_info *rt) { + struct ip6_host *ihost = host->ip6_host; spin_lock_bh(&rt6_dflt_lock); - if (rt == NULL || rt == rt6_dflt_pointer) { - RT6_TRACE("reset default router: %p->NULL\n", rt6_dflt_pointer); - rt6_dflt_pointer = NULL; + if (rt == NULL || rt == ihost->rt6_dflt_pointer) { + RT6_TRACE("reset default router: %p->NULL\n", ihost->rt6_dflt_pointer); + ihost->rt6_dflt_pointer = NULL; } spin_unlock_bh(&rt6_dflt_lock); } /* Default Router Selection (RFC 2461 6.3.6) */ -static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, int oif) +static struct rt6_info *rt6_best_dflt(struct ip6_host *ihost, struct rt6_info *rt, int oif) { struct rt6_info *match = NULL; struct rt6_info *sprt; @@ -251,7 +227,7 @@ static struct rt6_info *rt6_best_dflt(st if (rt6_check_expired(sprt)) continue; - if (sprt == rt6_dflt_pointer) + if (sprt == ihost->rt6_dflt_pointer) m += 4; if ((neigh = sprt->rt6i_nexthop) != NULL) { @@ -302,8 +278,8 @@ static struct rt6_info *rt6_best_dflt(st * No default routers are known to be reachable. * SHOULD round robin */ - if (rt6_dflt_pointer) { - for (sprt = rt6_dflt_pointer->u.next; + if (ihost->rt6_dflt_pointer) { + for (sprt = ihost->rt6_dflt_pointer->u.next; sprt; sprt = sprt->u.next) { if (sprt->u.dst.obsolete <= 0 && sprt->u.dst.error == 0 && @@ -321,17 +297,17 @@ static struct rt6_info *rt6_best_dflt(st match = sprt; break; } - if (sprt == rt6_dflt_pointer) + if (sprt == ihost->rt6_dflt_pointer) break; } } } if (match) { - if (rt6_dflt_pointer != match) + if (ihost->rt6_dflt_pointer != match) RT6_TRACE("changed default router: %p->%p\n", - rt6_dflt_pointer, match); - rt6_dflt_pointer = match; + ihost->rt6_dflt_pointer, match); + ihost->rt6_dflt_pointer = match; } spin_unlock(&rt6_dflt_lock); @@ -341,7 +317,7 @@ static struct rt6_info *rt6_best_dflt(st * use addrconf default route. * We don't record this route. */ - for (sprt = ip6_routing_table.leaf; + for (sprt = ihost->ip6_routing_table.leaf; sprt; sprt = sprt->u.next) { if (!rt6_check_expired(sprt) && (sprt->rt6i_flags & RTF_DEFAULT) && @@ -354,22 +330,28 @@ static struct rt6_info *rt6_best_dflt(st } if (!match) { /* no default route. give up. */ - match = &ip6_null_entry; + match = &ihost->ip6_null_entry; } } return match; } -struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr, +struct rt6_info *rt6_lookup(struct nethost *host, + struct in6_addr *daddr, struct in6_addr *saddr, int oif, int strict) { + struct ip6_host *ihost; struct fib6_node *fn; struct rt6_info *rt; + ihost = ip6_host_get(host); + if (!ihost) + return NULL; + read_lock_bh(&rt6_lock); - fn = fib6_lookup(&ip6_routing_table, daddr, saddr); - rt = rt6_device_match(fn->leaf, oif, strict); + fn = fib6_lookup(&ihost->ip6_routing_table, daddr, saddr); + rt = rt6_device_match(ihost, fn->leaf, oif, strict); dst_hold(&rt->u.dst); rt->u.dst.__use++; read_unlock_bh(&rt6_lock); @@ -387,13 +369,17 @@ struct rt6_info *rt6_lookup(struct in6_a be destroyed. */ -int ip6_ins_rt(struct rt6_info *rt, struct nlmsghdr *nlh, +int ip6_ins_rt(struct nethost *host, struct rt6_info *rt, struct nlmsghdr *nlh, void *_rtattr, struct sk_buff *in_skb) { + struct ip6_host *ihost; int err; + ihost = ip6_host_get(host); + if (!ihost) + return -ENOENT; write_lock_bh(&rt6_lock); - err = fib6_add(&ip6_routing_table, rt, nlh, _rtattr, in_skb); + err = fib6_add(&ihost->ip6_routing_table, rt, nlh, _rtattr, in_skb); write_unlock_bh(&rt6_lock); return err; @@ -403,7 +389,8 @@ int ip6_ins_rt(struct rt6_info *rt, stru with dst->error set to errno value. */ -static struct rt6_info *rt6_cow(struct rt6_info *ort, struct in6_addr *daddr, +static struct rt6_info *rt6_cow(struct nethost *host, + struct rt6_info *ort, struct in6_addr *daddr, struct in6_addr *saddr) { int err; @@ -436,7 +423,7 @@ static struct rt6_info *rt6_cow(struct r dst_hold(&rt->u.dst); - err = ip6_ins_rt(rt, NULL, NULL, NULL); + err = ip6_ins_rt(host, rt, NULL, NULL, NULL); if (err == 0) return rt; @@ -444,12 +431,12 @@ static struct rt6_info *rt6_cow(struct r return rt; } - dst_hold(&ip6_null_entry.u.dst); - return &ip6_null_entry; + dst_hold(&host->ip6_host->ip6_null_entry.u.dst); + return &host->ip6_host->ip6_null_entry; } #define BACKTRACK() \ -if (rt == &ip6_null_entry && strict) { \ +if (rt == &ihost->ip6_null_entry && strict) { \ while ((fn = fn->parent) != NULL) { \ if (fn->fn_flags & RTN_ROOT) { \ dst_hold(&rt->u.dst); \ @@ -463,30 +450,35 @@ if (rt == &ip6_null_entry && strict) { \ void ip6_route_input(struct sk_buff *skb) { + struct ip6_host *ihost; struct fib6_node *fn; struct rt6_info *rt; int strict; int attempts = 3; + ihost = ip6_host_get(skb->dev->host); + if (!ihost) + return; + strict = ipv6_addr_type(&skb->nh.ipv6h->daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL); relookup: read_lock_bh(&rt6_lock); - fn = fib6_lookup(&ip6_routing_table, &skb->nh.ipv6h->daddr, + fn = fib6_lookup(&ihost->ip6_routing_table, &skb->nh.ipv6h->daddr, &skb->nh.ipv6h->saddr); restart: rt = fn->leaf; if ((rt->rt6i_flags & RTF_CACHE)) { - rt = rt6_device_match(rt, skb->dev->ifindex, strict); + rt = rt6_device_match(ihost, rt, skb->dev->ifindex, strict); BACKTRACK(); dst_hold(&rt->u.dst); goto out; } - rt = rt6_device_match(rt, skb->dev->ifindex, 0); + rt = rt6_device_match(ihost, rt, skb->dev->ifindex, 0); BACKTRACK(); if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) { @@ -494,7 +486,7 @@ restart: dst_hold(&rt->u.dst); read_unlock_bh(&rt6_lock); - nrt = rt6_cow(rt, &skb->nh.ipv6h->daddr, + nrt = rt6_cow(ihost->host, rt, &skb->nh.ipv6h->daddr, &skb->nh.ipv6h->saddr); dst_release(&rt->u.dst); @@ -521,6 +513,7 @@ out2: struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl) { + struct ip6_host *ihost = fl->host->ip6_host; struct fib6_node *fn; struct rt6_info *rt; int strict; @@ -531,22 +524,22 @@ struct dst_entry * ip6_route_output(stru relookup: read_lock_bh(&rt6_lock); - fn = fib6_lookup(&ip6_routing_table, &fl->fl6_dst, &fl->fl6_src); + fn = fib6_lookup(&ihost->ip6_routing_table, &fl->fl6_dst, &fl->fl6_src); restart: rt = fn->leaf; if ((rt->rt6i_flags & RTF_CACHE)) { - rt = rt6_device_match(rt, fl->oif, strict); + rt = rt6_device_match(ihost, rt, fl->oif, strict); BACKTRACK(); dst_hold(&rt->u.dst); goto out; } if (rt->rt6i_flags & RTF_DEFAULT) { if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF) - rt = rt6_best_dflt(rt, fl->oif); + rt = rt6_best_dflt(ihost, rt, fl->oif); } else { - rt = rt6_device_match(rt, fl->oif, strict); + rt = rt6_device_match(ihost, rt, fl->oif, strict); BACKTRACK(); } @@ -555,7 +548,7 @@ restart: dst_hold(&rt->u.dst); read_unlock_bh(&rt6_lock); - nrt = rt6_cow(rt, &fl->fl6_dst, &fl->fl6_src); + nrt = rt6_cow(fl->host, rt, &fl->fl6_dst, &fl->fl6_src); dst_release(&rt->u.dst); rt = nrt; @@ -672,7 +665,7 @@ struct dst_entry *ndisc_dst_alloc(struct if (unlikely(idev == NULL)) return NULL; - rt = ip6_dst_alloc(); + rt = ip6_dst_alloc(dev->host); if (unlikely(rt == NULL)) { in6_dev_put(idev); goto out; @@ -706,7 +699,7 @@ struct dst_entry *ndisc_dst_alloc(struct ndisc_dst_gc_list = &rt->u.dst; write_unlock_bh(&rt6_lock); - fib6_force_start_gc(); + fib6_force_start_gc(dev->host); out: return (struct dst_entry *)rt; @@ -734,7 +727,7 @@ int ndisc_dst_gc(int *more) return freed; } -static int ip6_dst_gc(void) +static int ip6_dst_gc(struct nethost *host) { static unsigned expire = 30*HZ; static unsigned long last_gc; @@ -745,7 +738,7 @@ static int ip6_dst_gc(void) goto out; expire++; - fib6_run_gc(expire); + fib6_run_gc(host->ip6_host, expire); last_gc = now; if (atomic_read(&ip6_dst_ops.entries) < ip6_dst_ops.gc_thresh) expire = ip6_rt_gc_timeout>>1; @@ -795,6 +788,7 @@ int ip6_route_add(struct nethost *host, struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh, void *_rtattr, struct sk_buff *in_skb) { + struct ip6_host *ihost; int err; struct rtmsg *r; struct rtattr **rta; @@ -803,6 +797,10 @@ int ip6_route_add(struct nethost *host, struct inet6_dev *idev = NULL; int addr_type; + ihost = ip6_host_get(host); + if (!ihost) + return -ENODEV; + rta = (struct rtattr **) _rtattr; if (rtmsg->rtmsg_dst_len > 128 || rtmsg->rtmsg_src_len > 128) @@ -824,7 +822,7 @@ int ip6_route_add(struct nethost *host, if (rtmsg->rtmsg_metric == 0) rtmsg->rtmsg_metric = IP6_RT_PRIO_USER; - rt = ip6_dst_alloc(); + rt = ip6_dst_alloc(host); if (rt == NULL) { err = -ENOMEM; @@ -867,6 +865,20 @@ int ip6_route_add(struct nethost *host, */ if ((rtmsg->rtmsg_flags&RTF_REJECT) || (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) { + /* hold loopback dev/idev if we haven't done so. */ + if (dev != &host->loopback_dev) { + if (dev) { + dev_put(dev); + in6_dev_put(idev); + } + dev = &host->loopback_dev; + dev_hold(dev); + idev = in6_dev_get(dev); + if (!idev) { + err = -ENODEV; + goto out; + } + } rt->u.dst.output = ip6_pkt_discard_out; rt->u.dst.input = ip6_pkt_discard; rt->u.dst.error = -ENETUNREACH; @@ -896,7 +908,7 @@ int ip6_route_add(struct nethost *host, if (!(gwa_type&IPV6_ADDR_UNICAST)) goto out; - grt = rt6_lookup(gw_addr, NULL, rtmsg->rtmsg_ifindex, 1); + grt = rt6_lookup(host, gw_addr, NULL, rtmsg->rtmsg_ifindex, 1); err = -EHOSTUNREACH; if (grt == NULL) @@ -966,7 +978,7 @@ install_route: rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dst_mtu(&rt->u.dst)); rt->u.dst.dev = dev; rt->rt6i_idev = idev; - return ip6_ins_rt(rt, nlh, _rtattr, in_skb); + return ip6_ins_rt(host, rt, nlh, _rtattr, in_skb); out: if (dev) @@ -984,7 +996,7 @@ int ip6_del_rt(struct rt6_info *rt, stru write_lock_bh(&rt6_lock); - rt6_reset_dflt_pointer(NULL); + rt6_reset_dflt_pointer(rt->rt6i_dev->host, NULL); err = fib6_del(rt, nlh, _rtattr, in_skb); dst_release(&rt->u.dst); @@ -998,13 +1010,17 @@ static int ip6_route_del(struct nethost struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh, void *_rtattr, struct sk_buff *in_skb) { + struct ip6_host *ihost = host->ip6_host; struct fib6_node *fn; struct rt6_info *rt; int err = -ESRCH; + if (!ihost) + goto out; + read_lock_bh(&rt6_lock); - fn = fib6_locate(&ip6_routing_table, + fn = fib6_locate(&ihost->ip6_routing_table, &rtmsg->rtmsg_dst, rtmsg->rtmsg_dst_len, &rtmsg->rtmsg_src, rtmsg->rtmsg_src_len); @@ -1028,6 +1044,7 @@ static int ip6_route_del(struct nethost } read_unlock_bh(&rt6_lock); +out: return err; } @@ -1037,10 +1054,11 @@ static int ip6_route_del(struct nethost void rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr, struct neighbour *neigh, u8 *lladdr, int on_link) { + struct nethost *host = neigh->dev->host; struct rt6_info *rt, *nrt; /* Locate old route to this destination. */ - rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1); + rt = rt6_lookup(host, dest, NULL, neigh->dev->ifindex, 1); if (rt == NULL) return; @@ -1068,10 +1086,11 @@ void rt6_redirect(struct in6_addr *dest, */ if (!ipv6_addr_equal(saddr, &rt->rt6i_gateway)) { if (rt->rt6i_flags & RTF_DEFAULT) { + struct ip6_host *ihost = host->ip6_host; struct rt6_info *rt1; read_lock(&rt6_lock); - for (rt1 = ip6_routing_table.leaf; rt1; rt1 = rt1->u.next) { + for (rt1 = ihost->ip6_routing_table.leaf; rt1; rt1 = rt1->u.next) { if (ipv6_addr_equal(saddr, &rt1->rt6i_gateway)) { dst_hold(&rt1->u.dst); dst_release(&rt->u.dst); @@ -1130,7 +1149,7 @@ source_ok: nrt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(neigh->dev); nrt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dst_mtu(&nrt->u.dst)); - if (ip6_ins_rt(nrt, NULL, NULL, NULL)) + if (ip6_ins_rt(host, nrt, NULL, NULL, NULL)) goto out; if (rt->rt6i_flags&RTF_CACHE) { @@ -1154,7 +1173,7 @@ void rt6_pmtu_discovery(struct in6_addr struct rt6_info *rt, *nrt; int allfrag = 0; - rt = rt6_lookup(daddr, saddr, dev->ifindex, 0); + rt = rt6_lookup(dev->host, daddr, saddr, dev->ifindex, 0); if (rt == NULL) return; @@ -1198,7 +1217,7 @@ void rt6_pmtu_discovery(struct in6_addr 2. It is gatewayed route or NONEXTHOP route. Action: clone it. */ if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) { - nrt = rt6_cow(rt, daddr, saddr); + nrt = rt6_cow(dev->host, rt, daddr, saddr); if (!nrt->u.dst.error) { nrt->u.dst.metrics[RTAX_MTU-1] = pmtu; if (allfrag) @@ -1226,7 +1245,7 @@ void rt6_pmtu_discovery(struct in6_addr nrt->u.dst.metrics[RTAX_MTU-1] = pmtu; if (allfrag) nrt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG; - ip6_ins_rt(nrt, NULL, NULL, NULL); + ip6_ins_rt(dev->host, nrt, NULL, NULL, NULL); } out: @@ -1239,7 +1258,7 @@ out: static struct rt6_info * ip6_rt_copy(struct rt6_info *ort) { - struct rt6_info *rt = ip6_dst_alloc(); + struct rt6_info *rt = ip6_dst_alloc(ort->u.dst.dev->host); if (rt) { rt->u.dst.input = ort->u.dst.input; @@ -1269,10 +1288,15 @@ static struct rt6_info * ip6_rt_copy(str struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev) { + struct ip6_host *ihost; struct rt6_info *rt; struct fib6_node *fn; - fn = &ip6_routing_table; + ihost = dev->host->ip6_host; + if (!ihost) + return NULL; + + fn = &ihost->ip6_routing_table; write_lock_bh(&rt6_lock); for (rt = fn->leaf; rt; rt=rt->u.next) { @@ -1303,17 +1327,21 @@ struct rt6_info *rt6_add_dflt_router(str return rt6_get_dflt_router(gwaddr, dev); } -void rt6_purge_dflt_routers(void) +void rt6_purge_dflt_routers(struct nethost *host) { + struct ip6_host *ihost = host->ip6_host; struct rt6_info *rt; + if (!ihost) + return; + restart: read_lock_bh(&rt6_lock); - for (rt = ip6_routing_table.leaf; rt; rt = rt->u.next) { + for (rt = ihost->ip6_routing_table.leaf; rt; rt = rt->u.next) { if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { dst_hold(&rt->u.dst); - rt6_reset_dflt_pointer(NULL); + rt6_reset_dflt_pointer(host, NULL); read_unlock_bh(&rt6_lock); @@ -1387,7 +1415,7 @@ struct rt6_info *addrconf_dst_alloc(stru { struct nethost *host = idev->dev->host; struct net_device *loopback_dev = &host->loopback_dev; - struct rt6_info *rt = ip6_dst_alloc(); + struct rt6_info *rt = ip6_dst_alloc(host); if (rt == NULL) return ERR_PTR(-ENOMEM); @@ -1424,18 +1452,24 @@ struct rt6_info *addrconf_dst_alloc(stru static int fib6_ifdown(struct rt6_info *rt, void *arg) { - if (((void*)rt->rt6i_dev == arg || arg == NULL) && - rt != &ip6_null_entry) { - RT6_TRACE("deleted by ifdown %p\n", rt); - return -1; + if ((void*)rt->rt6i_dev == arg || arg == NULL) { + struct ip6_host *ihost = rt->rt6i_dev->host->ip6_host; + if (rt != &ihost->ip6_null_entry) { + RT6_TRACE("deleted by ifdown %p\n", rt); + return -1; + } } return 0; } -void rt6_ifdown(struct net_device *dev) +void rt6_ifdown(struct nethost *host, struct net_device *dev) { + struct ip6_host *ihost; + ihost = host->ip6_host; + if (!ihost) + return; write_lock_bh(&rt6_lock); - fib6_clean_tree(&ip6_routing_table, fib6_ifdown, 0, dev); + fib6_clean_tree(&ihost->ip6_routing_table, fib6_ifdown, 0, dev); write_unlock_bh(&rt6_lock); } @@ -1486,12 +1520,14 @@ static int rt6_mtu_change_route(struct r void rt6_mtu_change(struct net_device *dev, unsigned mtu) { + struct ip6_host *ihost; struct rt6_mtu_change_arg arg; + ihost = dev->host->ip6_host; arg.dev = dev; arg.mtu = mtu; read_lock_bh(&rt6_lock); - fib6_clean_tree(&ip6_routing_table, rt6_mtu_change_route, 0, &arg); + fib6_clean_tree(&ihost->ip6_routing_table, rt6_mtu_change_route, 0, &arg); read_unlock_bh(&rt6_lock); } @@ -1709,10 +1745,15 @@ static int fib6_dump_done(struct netlink int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) { + struct ip6_host *ihost; struct rt6_rtnl_dump_arg arg; struct fib6_walker_t *w; int res; + ihost = ip6_host_get(skb->sk->sk_host); + if (!ihost) + return 0; + arg.skb = skb; arg.cb = cb; @@ -1733,7 +1774,7 @@ int inet6_dump_fib(struct sk_buff *skb, return -ENOMEM; RT6_TRACE("dump<%p", w); memset(w, 0, sizeof(*w)); - w->root = &ip6_routing_table; + w->root = &ihost->ip6_routing_table; w->func = fib6_dump_node; w->args = &arg; cb->args[0] = (long)w; @@ -1932,7 +1973,13 @@ static int rt6_info_route(struct rt6_inf static int rt6_proc_info(char *buffer, char **start, off_t offset, int length) { + struct ip6_host *ihost; struct rt6_proc_arg arg; + + ihost = current->host->ip6_host; + if (!ihost) + return 0; + arg.buffer = buffer; arg.offset = offset; arg.length = length; @@ -1940,7 +1987,7 @@ static int rt6_proc_info(char *buffer, c arg.len = 0; read_lock_bh(&rt6_lock); - fib6_clean_tree(&ip6_routing_table, rt6_info_route, 0, &arg); + fib6_clean_tree(&ihost->ip6_routing_table, rt6_info_route, 0, &arg); read_unlock_bh(&rt6_lock); *start = buffer; @@ -1992,8 +2039,10 @@ int ipv6_sysctl_rtcache_flush(ctl_table void __user *buffer, size_t *lenp, loff_t *ppos) { if (write) { + struct ip6_host *ihost; + ihost = ip6_host_get(current->host); proc_dointvec(ctl, write, filp, buffer, lenp, ppos); - fib6_run_gc(flush_delay <= 0 ? ~0UL : (unsigned long)flush_delay); + fib6_run_gc(ihost, flush_delay <= 0 ? ~0UL : (unsigned long)flush_delay); return 0; } else return -EINVAL; @@ -2092,6 +2141,39 @@ ctl_table ipv6_route_table[] = { #endif +void ip6_route_host_init(struct ip6_host *ihost) +{ + struct rt6_info null_rt = { + .u = { + .dst = { + .__refcnt = ATOMIC_INIT(1), + .__use = 1, + .dev = &ihost->host->loopback_dev, + .obsolete = -1, + .error = -ENETUNREACH, + .metrics = { [RTAX_HOPLIMIT - 1] = 255, }, + .input = ip6_pkt_discard, + .output = ip6_pkt_discard_out, + .ops = &ip6_dst_ops, + .path = (struct dst_entry*)&ihost->ip6_null_entry, + } + }, + .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), + .rt6i_metric = ~(u32) 0, + .rt6i_ref = ATOMIC_INIT(1), + }; + ihost->ip6_null_entry = null_rt; + ihost->ip6_routing_table.leaf = &ihost->ip6_null_entry; + ihost->ip6_routing_table.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; + fib6_gc_host_init(ihost); +} + +void ip6_route_host_cleanup(struct ip6_host *ihost) +{ + /* Empty the routing table */ + fib6_gc_host_cleanup(ihost); +} + void __init ip6_route_init(void) { struct proc_dir_entry *p; @@ -2125,7 +2207,6 @@ void ip6_route_cleanup(void) #ifdef CONFIG_XFRM xfrm6_fini(); #endif - rt6_ifdown(NULL); fib6_gc_cleanup(); kmem_cache_destroy(ip6_dst_ops.kmem_cachep); } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 0033678..2adf8e3 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -190,7 +190,7 @@ static struct sock *udp_v6_lookup(struct continue; score++; } - if (host && host != sk->sk_host) + if (host != sk->sk_host) continue; if(score == 4) { result = sk; @@ -720,7 +720,7 @@ do_udp_sendmsg: ulen += sizeof(struct udphdr); memset(fl, 0, sizeof(*fl)); - fl->iif = sk->sk_host->loopback_dev.ifindex; + fl->host = sk->sk_host; if (sin6) { if (sin6->sin6_port == 0) diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 525cb07..81effdf 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -94,7 +94,7 @@ __xfrm6_bundle_create(struct xfrm_policy dst_hold(&rt->u.dst); for (i = 0; i < nx; i++) { - struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops); + struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops, fl->host); struct xfrm_dst *xdst; int tunnel = 0; @@ -246,7 +246,7 @@ _decode_session6(struct sk_buff *skb, st } } -static inline int xfrm6_garbage_collect(void) +static inline int xfrm6_garbage_collect(struct nethost *host) { read_lock(&xfrm6_policy_afinfo.lock); xfrm6_policy_afinfo.garbage_collect(); -- 1.0.GIT