netdev: Move next_sched into struct netdev_queue.
[safe/jmp/linux-2.6] / net / core / neighbour.c
index 4b6dd1e..65f01f7 100644 (file)
@@ -59,7 +59,6 @@ static void neigh_timer_handler(unsigned long arg);
 static void __neigh_notify(struct neighbour *n, int type, int flags);
 static void neigh_update_notify(struct neighbour *neigh);
 static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev);
-void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev);
 
 static struct neigh_table *neigh_tables;
 #ifdef CONFIG_PROC_FS
@@ -124,6 +123,7 @@ unsigned long neigh_rand_reach_time(unsigned long base)
 {
        return (base ? (net_random() % base) + (base >> 1) : 0);
 }
+EXPORT_SYMBOL(neigh_rand_reach_time);
 
 
 static int neigh_forced_gc(struct neigh_table *tbl)
@@ -165,6 +165,16 @@ static int neigh_forced_gc(struct neigh_table *tbl)
        return shrunk;
 }
 
+static void neigh_add_timer(struct neighbour *n, unsigned long when)
+{
+       neigh_hold(n);
+       if (unlikely(mod_timer(&n->timer, when))) {
+               printk("NEIGH: BUG, double timer add, state is %x\n",
+                      n->nud_state);
+               dump_stack();
+       }
+}
+
 static int neigh_del_timer(struct neighbour *n)
 {
        if ((n->nud_state & NUD_IN_TIMER) &&
@@ -232,6 +242,7 @@ void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev)
        neigh_flush_dev(tbl, dev);
        write_unlock_bh(&tbl->lock);
 }
+EXPORT_SYMBOL(neigh_changeaddr);
 
 int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
 {
@@ -244,6 +255,7 @@ int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
        pneigh_queue_purge(&tbl->proxy_queue);
        return 0;
 }
+EXPORT_SYMBOL(neigh_ifdown);
 
 static struct neighbour *neigh_alloc(struct neigh_table *tbl)
 {
@@ -349,11 +361,12 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
 {
        struct neighbour *n;
        int key_len = tbl->key_len;
-       u32 hash_val = tbl->hash(pkey, dev);
+       u32 hash_val;
 
        NEIGH_CACHE_STAT_INC(tbl, lookups);
 
        read_lock_bh(&tbl->lock);
+       hash_val = tbl->hash(pkey, dev);
        for (n = tbl->hash_buckets[hash_val & tbl->hash_mask]; n; n = n->next) {
                if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {
                        neigh_hold(n);
@@ -364,18 +377,22 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
        read_unlock_bh(&tbl->lock);
        return n;
 }
+EXPORT_SYMBOL(neigh_lookup);
 
-struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, const void *pkey)
+struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
+                                    const void *pkey)
 {
        struct neighbour *n;
        int key_len = tbl->key_len;
-       u32 hash_val = tbl->hash(pkey, NULL);
+       u32 hash_val;
 
        NEIGH_CACHE_STAT_INC(tbl, lookups);
 
        read_lock_bh(&tbl->lock);
+       hash_val = tbl->hash(pkey, NULL);
        for (n = tbl->hash_buckets[hash_val & tbl->hash_mask]; n; n = n->next) {
-               if (!memcmp(n->primary_key, pkey, key_len)) {
+               if (!memcmp(n->primary_key, pkey, key_len) &&
+                   net_eq(dev_net(n->dev), net)) {
                        neigh_hold(n);
                        NEIGH_CACHE_STAT_INC(tbl, hits);
                        break;
@@ -384,6 +401,7 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, const void *pkey)
        read_unlock_bh(&tbl->lock);
        return n;
 }
+EXPORT_SYMBOL(neigh_lookup_nodev);
 
 struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
                               struct net_device *dev)
@@ -452,31 +470,59 @@ out_neigh_release:
        neigh_release(n);
        goto out;
 }
+EXPORT_SYMBOL(neigh_create);
 
-struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey,
-                                   struct net_device *dev, int creat)
+static u32 pneigh_hash(const void *pkey, int key_len)
 {
-       struct pneigh_entry *n;
-       int key_len = tbl->key_len;
        u32 hash_val = *(u32 *)(pkey + key_len - 4);
-
        hash_val ^= (hash_val >> 16);
        hash_val ^= hash_val >> 8;
        hash_val ^= hash_val >> 4;
        hash_val &= PNEIGH_HASHMASK;
+       return hash_val;
+}
 
-       read_lock_bh(&tbl->lock);
-
-       for (n = tbl->phash_buckets[hash_val]; n; n = n->next) {
+static struct pneigh_entry *__pneigh_lookup_1(struct pneigh_entry *n,
+                                             struct net *net,
+                                             const void *pkey,
+                                             int key_len,
+                                             struct net_device *dev)
+{
+       while (n) {
                if (!memcmp(n->key, pkey, key_len) &&
-                   (n->dev == dev || !n->dev)) {
-                       read_unlock_bh(&tbl->lock);
-                       goto out;
-               }
+                   net_eq(pneigh_net(n), net) &&
+                   (n->dev == dev || !n->dev))
+                       return n;
+               n = n->next;
        }
+       return NULL;
+}
+
+struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl,
+               struct net *net, const void *pkey, struct net_device *dev)
+{
+       int key_len = tbl->key_len;
+       u32 hash_val = pneigh_hash(pkey, key_len);
+
+       return __pneigh_lookup_1(tbl->phash_buckets[hash_val],
+                                net, pkey, key_len, dev);
+}
+EXPORT_SYMBOL_GPL(__pneigh_lookup);
+
+struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl,
+                                   struct net *net, const void *pkey,
+                                   struct net_device *dev, int creat)
+{
+       struct pneigh_entry *n;
+       int key_len = tbl->key_len;
+       u32 hash_val = pneigh_hash(pkey, key_len);
+
+       read_lock_bh(&tbl->lock);
+       n = __pneigh_lookup_1(tbl->phash_buckets[hash_val],
+                             net, pkey, key_len, dev);
        read_unlock_bh(&tbl->lock);
-       n = NULL;
-       if (!creat)
+
+       if (n || !creat)
                goto out;
 
        ASSERT_RTNL();
@@ -485,6 +531,9 @@ struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey,
        if (!n)
                goto out;
 
+#ifdef CONFIG_NET_NS
+       n->net = hold_net(net);
+#endif
        memcpy(n->key, pkey, key_len);
        n->dev = dev;
        if (dev)
@@ -493,6 +542,7 @@ struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey,
        if (tbl->pconstructor && tbl->pconstructor(n)) {
                if (dev)
                        dev_put(dev);
+               release_net(net);
                kfree(n);
                n = NULL;
                goto out;
@@ -505,30 +555,28 @@ struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey,
 out:
        return n;
 }
+EXPORT_SYMBOL(pneigh_lookup);
 
 
-int pneigh_delete(struct neigh_table *tbl, const void *pkey,
+int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
                  struct net_device *dev)
 {
        struct pneigh_entry *n, **np;
        int key_len = tbl->key_len;
-       u32 hash_val = *(u32 *)(pkey + key_len - 4);
-
-       hash_val ^= (hash_val >> 16);
-       hash_val ^= hash_val >> 8;
-       hash_val ^= hash_val >> 4;
-       hash_val &= PNEIGH_HASHMASK;
+       u32 hash_val = pneigh_hash(pkey, key_len);
 
        write_lock_bh(&tbl->lock);
        for (np = &tbl->phash_buckets[hash_val]; (n = *np) != NULL;
             np = &n->next) {
-               if (!memcmp(n->key, pkey, key_len) && n->dev == dev) {
+               if (!memcmp(n->key, pkey, key_len) && n->dev == dev &&
+                   net_eq(pneigh_net(n), net)) {
                        *np = n->next;
                        write_unlock_bh(&tbl->lock);
                        if (tbl->pdestructor)
                                tbl->pdestructor(n);
                        if (n->dev)
                                dev_put(n->dev);
+                       release_net(pneigh_net(n));
                        kfree(n);
                        return 0;
                }
@@ -551,6 +599,7 @@ static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
                                        tbl->pdestructor(n);
                                if (n->dev)
                                        dev_put(n->dev);
+                               release_net(pneigh_net(n));
                                kfree(n);
                                continue;
                        }
@@ -560,6 +609,13 @@ static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
        return -ENOENT;
 }
 
+static void neigh_parms_destroy(struct neigh_parms *parms);
+
+static inline void neigh_parms_put(struct neigh_parms *parms)
+{
+       if (atomic_dec_and_test(&parms->refcnt))
+               neigh_parms_destroy(parms);
+}
 
 /*
  *     neighbour must already be out of the table;
@@ -602,6 +658,7 @@ void neigh_destroy(struct neighbour *neigh)
        atomic_dec(&neigh->tbl->entries);
        kmem_cache_free(neigh->tbl->kmem_cachep, neigh);
 }
+EXPORT_SYMBOL(neigh_destroy);
 
 /* Neighbour state is suspicious;
    disable fast path.
@@ -716,15 +773,6 @@ static __inline__ int neigh_max_probes(struct neighbour *n)
                p->ucast_probes + p->app_probes + p->mcast_probes);
 }
 
-static inline void neigh_add_timer(struct neighbour *n, unsigned long when)
-{
-       if (unlikely(mod_timer(&n->timer, when))) {
-               printk("NEIGH: BUG, double timer add, state is %x\n",
-                      n->nud_state);
-               dump_stack();
-       }
-}
-
 /* Called when a timer expires for a neighbour entry. */
 
 static void neigh_timer_handler(unsigned long arg)
@@ -821,7 +869,7 @@ static void neigh_timer_handler(unsigned long arg)
                struct sk_buff *skb = skb_peek(&neigh->arp_queue);
                /* keep skb alive even if arp_queue overflows */
                if (skb)
-                       skb_get(skb);
+                       skb = skb_copy(skb, GFP_ATOMIC);
                write_unlock(&neigh->lock);
                neigh->ops->solicit(neigh, skb);
                atomic_inc(&neigh->probes);
@@ -856,7 +904,6 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
                        atomic_set(&neigh->probes, neigh->parms->ucast_probes);
                        neigh->nud_state     = NUD_INCOMPLETE;
                        neigh->updated = jiffies;
-                       neigh_hold(neigh);
                        neigh_add_timer(neigh, now + 1);
                } else {
                        neigh->nud_state = NUD_FAILED;
@@ -869,7 +916,6 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
                }
        } else if (neigh->nud_state & NUD_STALE) {
                NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
-               neigh_hold(neigh);
                neigh->nud_state = NUD_DELAY;
                neigh->updated = jiffies;
                neigh_add_timer(neigh,
@@ -893,6 +939,7 @@ out_unlock_bh:
        write_unlock_bh(&neigh->lock);
        return rc;
 }
+EXPORT_SYMBOL(__neigh_event_send);
 
 static void neigh_update_hhs(struct neighbour *neigh)
 {
@@ -1013,13 +1060,11 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
 
        if (new != old) {
                neigh_del_timer(neigh);
-               if (new & NUD_IN_TIMER) {
-                       neigh_hold(neigh);
+               if (new & NUD_IN_TIMER)
                        neigh_add_timer(neigh, (jiffies +
                                                ((new & NUD_REACHABLE) ?
                                                 neigh->parms->reachable_time :
                                                 0)));
-               }
                neigh->nud_state = new;
        }
 
@@ -1067,6 +1112,7 @@ out:
 
        return err;
 }
+EXPORT_SYMBOL(neigh_update);
 
 struct neighbour *neigh_event_ns(struct neigh_table *tbl,
                                 u8 *lladdr, void *saddr,
@@ -1079,6 +1125,7 @@ struct neighbour *neigh_event_ns(struct neigh_table *tbl,
                             NEIGH_UPDATE_F_OVERRIDE);
        return neigh;
 }
+EXPORT_SYMBOL(neigh_event_ns);
 
 static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst,
                          __be16 protocol)
@@ -1133,6 +1180,7 @@ int neigh_compat_output(struct sk_buff *skb)
 
        return dev_queue_xmit(skb);
 }
+EXPORT_SYMBOL(neigh_compat_output);
 
 /* Slow and careful. */
 
@@ -1178,6 +1226,7 @@ out_kfree_skb:
        kfree_skb(skb);
        goto out;
 }
+EXPORT_SYMBOL(neigh_resolve_output);
 
 /* As fast as possible without hh cache */
 
@@ -1202,6 +1251,7 @@ int neigh_connected_output(struct sk_buff *skb)
        }
        return err;
 }
+EXPORT_SYMBOL(neigh_connected_output);
 
 static void neigh_proxy_process(unsigned long arg)
 {
@@ -1263,28 +1313,51 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
        mod_timer(&tbl->proxy_timer, sched_next);
        spin_unlock(&tbl->proxy_queue.lock);
 }
+EXPORT_SYMBOL(pneigh_enqueue);
+
+static inline struct neigh_parms *lookup_neigh_params(struct neigh_table *tbl,
+                                                     struct net *net, int ifindex)
+{
+       struct neigh_parms *p;
+
+       for (p = &tbl->parms; p; p = p->next) {
+               if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) ||
+                   (!p->dev && !ifindex))
+                       return p;
+       }
 
+       return NULL;
+}
 
 struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
                                      struct neigh_table *tbl)
 {
-       struct neigh_parms *p = kmemdup(&tbl->parms, sizeof(*p), GFP_KERNEL);
+       struct neigh_parms *p, *ref;
+       struct net *net;
+
+       net = dev_net(dev);
+       ref = lookup_neigh_params(tbl, net, 0);
+       if (!ref)
+               return NULL;
 
+       p = kmemdup(ref, sizeof(*p), GFP_KERNEL);
        if (p) {
                p->tbl            = tbl;
                atomic_set(&p->refcnt, 1);
                INIT_RCU_HEAD(&p->rcu_head);
                p->reachable_time =
                                neigh_rand_reach_time(p->base_reachable_time);
-               if (dev) {
-                       if (dev->neigh_setup && dev->neigh_setup(dev, p)) {
-                               kfree(p);
-                               return NULL;
-                       }
 
-                       dev_hold(dev);
-                       p->dev = dev;
+               if (dev->neigh_setup && dev->neigh_setup(dev, p)) {
+                       kfree(p);
+                       return NULL;
                }
+
+               dev_hold(dev);
+               p->dev = dev;
+#ifdef CONFIG_NET_NS
+               p->net = hold_net(net);
+#endif
                p->sysctl_table = NULL;
                write_lock_bh(&tbl->lock);
                p->next         = tbl->parms.next;
@@ -1293,6 +1366,7 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
        }
        return p;
 }
+EXPORT_SYMBOL(neigh_parms_alloc);
 
 static void neigh_rcu_free_parms(struct rcu_head *head)
 {
@@ -1323,9 +1397,11 @@ void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
        write_unlock_bh(&tbl->lock);
        NEIGH_PRINTK1("neigh_parms_release: not found\n");
 }
+EXPORT_SYMBOL(neigh_parms_release);
 
-void neigh_parms_destroy(struct neigh_parms *parms)
+static void neigh_parms_destroy(struct neigh_parms *parms)
 {
+       release_net(neigh_parms_net(parms));
        kfree(parms);
 }
 
@@ -1336,6 +1412,9 @@ void neigh_table_init_no_netlink(struct neigh_table *tbl)
        unsigned long now = jiffies;
        unsigned long phsize;
 
+#ifdef CONFIG_NET_NS
+       tbl->parms.net = &init_net;
+#endif
        atomic_set(&tbl->parms.refcnt, 1);
        INIT_RCU_HEAD(&tbl->parms.rcu_head);
        tbl->parms.reachable_time =
@@ -1351,11 +1430,10 @@ void neigh_table_init_no_netlink(struct neigh_table *tbl)
                panic("cannot create neighbour cache statistics");
 
 #ifdef CONFIG_PROC_FS
-       tbl->pde = create_proc_entry(tbl->id, 0, init_net.proc_net_stat);
+       tbl->pde = proc_create_data(tbl->id, 0, init_net.proc_net_stat,
+                                   &neigh_stat_seq_fops, tbl);
        if (!tbl->pde)
                panic("cannot create neighbour proc dir entry");
-       tbl->pde->proc_fops = &neigh_stat_seq_fops;
-       tbl->pde->data = tbl;
 #endif
 
        tbl->hash_mask = 1;
@@ -1381,6 +1459,7 @@ void neigh_table_init_no_netlink(struct neigh_table *tbl)
        tbl->last_flush = now;
        tbl->last_rand  = now + tbl->parms.reachable_time * 20;
 }
+EXPORT_SYMBOL(neigh_table_init_no_netlink);
 
 void neigh_table_init(struct neigh_table *tbl)
 {
@@ -1402,6 +1481,7 @@ void neigh_table_init(struct neigh_table *tbl)
                dump_stack();
        }
 }
+EXPORT_SYMBOL(neigh_table_init);
 
 int neigh_table_clear(struct neigh_table *tbl)
 {
@@ -1439,19 +1519,17 @@ int neigh_table_clear(struct neigh_table *tbl)
 
        return 0;
 }
+EXPORT_SYMBOL(neigh_table_clear);
 
 static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
-       struct net *net = skb->sk->sk_net;
+       struct net *net = sock_net(skb->sk);
        struct ndmsg *ndm;
        struct nlattr *dst_attr;
        struct neigh_table *tbl;
        struct net_device *dev = NULL;
        int err = -EINVAL;
 
-       if (net != &init_net)
-               return -EINVAL;
-
        if (nlmsg_len(nlh) < sizeof(*ndm))
                goto out;
 
@@ -1480,7 +1558,7 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                        goto out_dev_put;
 
                if (ndm->ndm_flags & NTF_PROXY) {
-                       err = pneigh_delete(tbl, nla_data(dst_attr), dev);
+                       err = pneigh_delete(tbl, net, nla_data(dst_attr), dev);
                        goto out_dev_put;
                }
 
@@ -1511,16 +1589,13 @@ out:
 
 static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
-       struct net *net = skb->sk->sk_net;
+       struct net *net = sock_net(skb->sk);
        struct ndmsg *ndm;
        struct nlattr *tb[NDA_MAX+1];
        struct neigh_table *tbl;
        struct net_device *dev = NULL;
        int err;
 
-       if (net != &init_net)
-               return -EINVAL;
-
        err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
        if (err < 0)
                goto out;
@@ -1560,7 +1635,7 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                        struct pneigh_entry *pn;
 
                        err = -ENOBUFS;
-                       pn = pneigh_lookup(tbl, dst, dev, 1);
+                       pn = pneigh_lookup(tbl, net, dst, dev, 1);
                        if (pn) {
                                pn->flags = ndm->ndm_flags;
                                err = 0;
@@ -1639,7 +1714,8 @@ static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms)
        return nla_nest_end(skb, nest);
 
 nla_put_failure:
-       return nla_nest_cancel(skb, nest);
+       nla_nest_cancel(skb, nest);
+       return -EMSGSIZE;
 }
 
 static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl,
@@ -1755,19 +1831,6 @@ errout:
        return -EMSGSIZE;
 }
 
-static inline struct neigh_parms *lookup_neigh_params(struct neigh_table *tbl,
-                                                     int ifindex)
-{
-       struct neigh_parms *p;
-
-       for (p = &tbl->parms; p; p = p->next)
-               if ((p->dev && p->dev->ifindex == ifindex) ||
-                   (!p->dev && !ifindex))
-                       return p;
-
-       return NULL;
-}
-
 static const struct nla_policy nl_neightbl_policy[NDTA_MAX+1] = {
        [NDTA_NAME]             = { .type = NLA_STRING },
        [NDTA_THRESH1]          = { .type = NLA_U32 },
@@ -1795,15 +1858,12 @@ static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = {
 
 static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
-       struct net *net = skb->sk->sk_net;
+       struct net *net = sock_net(skb->sk);
        struct neigh_table *tbl;
        struct ndtmsg *ndtmsg;
        struct nlattr *tb[NDTA_MAX+1];
        int err;
 
-       if (net != &init_net)
-               return -EINVAL;
-
        err = nlmsg_parse(nlh, sizeof(*ndtmsg), tb, NDTA_MAX,
                          nl_neightbl_policy);
        if (err < 0)
@@ -1848,7 +1908,7 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                if (tbp[NDTPA_IFINDEX])
                        ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]);
 
-               p = lookup_neigh_params(tbl, ifindex);
+               p = lookup_neigh_params(tbl, net, ifindex);
                if (p == NULL) {
                        err = -ENOENT;
                        goto errout_tbl_lock;
@@ -1923,15 +1983,12 @@ errout:
 
 static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
 {
-       struct net *net = skb->sk->sk_net;
+       struct net *net = sock_net(skb->sk);
        int family, tidx, nidx = 0;
        int tbl_skip = cb->args[0];
        int neigh_skip = cb->args[1];
        struct neigh_table *tbl;
 
-       if (net != &init_net)
-               return 0;
-
        family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family;
 
        read_lock(&neigh_tbl_lock);
@@ -1946,8 +2003,11 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
                                       NLM_F_MULTI) <= 0)
                        break;
 
-               for (nidx = 0, p = tbl->parms.next; p; p = p->next, nidx++) {
-                       if (nidx < neigh_skip)
+               for (nidx = 0, p = tbl->parms.next; p; p = p->next) {
+                       if (!net_eq(neigh_parms_net(p), net))
+                               continue;
+
+                       if (nidx++ < neigh_skip)
                                continue;
 
                        if (neightbl_fill_param_info(skb, tbl, p,
@@ -1998,9 +2058,9 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh,
                goto nla_put_failure;
        }
 
-       ci.ndm_used      = now - neigh->used;
-       ci.ndm_confirmed = now - neigh->confirmed;
-       ci.ndm_updated   = now - neigh->updated;
+       ci.ndm_used      = jiffies_to_clock_t(now - neigh->used);
+       ci.ndm_confirmed = jiffies_to_clock_t(now - neigh->confirmed);
+       ci.ndm_updated   = jiffies_to_clock_t(now - neigh->updated);
        ci.ndm_refcnt    = atomic_read(&neigh->refcnt) - 1;
        read_unlock_bh(&neigh->lock);
 
@@ -2023,6 +2083,7 @@ static void neigh_update_notify(struct neighbour *neigh)
 static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
                            struct netlink_callback *cb)
 {
+       struct net * net = sock_net(skb->sk);
        struct neighbour *n;
        int rc, h, s_h = cb->args[1];
        int idx, s_idx = idx = cb->args[2];
@@ -2033,8 +2094,12 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
                        continue;
                if (h > s_h)
                        s_idx = 0;
-               for (n = tbl->hash_buckets[h], idx = 0; n; n = n->next, idx++) {
-                       if (idx < s_idx)
+               for (n = tbl->hash_buckets[h], idx = 0; n; n = n->next) {
+                       int lidx;
+                       if (dev_net(n->dev) != net)
+                               continue;
+                       lidx = idx++;
+                       if (lidx < s_idx)
                                continue;
                        if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid,
                                            cb->nlh->nlmsg_seq,
@@ -2056,13 +2121,9 @@ out:
 
 static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
 {
-       struct net *net = skb->sk->sk_net;
        struct neigh_table *tbl;
        int t, family, s_t;
 
-       if (net != &init_net)
-               return 0;
-
        read_lock(&neigh_tbl_lock);
        family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family;
        s_t = cb->args[0];
@@ -2130,6 +2191,7 @@ EXPORT_SYMBOL(__neigh_for_each_release);
 static struct neighbour *neigh_get_first(struct seq_file *seq)
 {
        struct neigh_seq_state *state = seq->private;
+       struct net *net = seq_file_net(seq);
        struct neigh_table *tbl = state->tbl;
        struct neighbour *n = NULL;
        int bucket = state->bucket;
@@ -2139,6 +2201,8 @@ static struct neighbour *neigh_get_first(struct seq_file *seq)
                n = tbl->hash_buckets[bucket];
 
                while (n) {
+                       if (!net_eq(dev_net(n->dev), net))
+                               goto next;
                        if (state->neigh_sub_iter) {
                                loff_t fakep = 0;
                                void *v;
@@ -2168,6 +2232,7 @@ static struct neighbour *neigh_get_next(struct seq_file *seq,
                                        loff_t *pos)
 {
        struct neigh_seq_state *state = seq->private;
+       struct net *net = seq_file_net(seq);
        struct neigh_table *tbl = state->tbl;
 
        if (state->neigh_sub_iter) {
@@ -2179,6 +2244,8 @@ static struct neighbour *neigh_get_next(struct seq_file *seq,
 
        while (1) {
                while (n) {
+                       if (!net_eq(dev_net(n->dev), net))
+                               goto next;
                        if (state->neigh_sub_iter) {
                                void *v = state->neigh_sub_iter(state, n, pos);
                                if (v)
@@ -2225,6 +2292,7 @@ static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos)
 static struct pneigh_entry *pneigh_get_first(struct seq_file *seq)
 {
        struct neigh_seq_state *state = seq->private;
+       struct net *net = seq_file_net(seq);
        struct neigh_table *tbl = state->tbl;
        struct pneigh_entry *pn = NULL;
        int bucket = state->bucket;
@@ -2232,6 +2300,8 @@ static struct pneigh_entry *pneigh_get_first(struct seq_file *seq)
        state->flags |= NEIGH_SEQ_IS_PNEIGH;
        for (bucket = 0; bucket <= PNEIGH_HASHMASK; bucket++) {
                pn = tbl->phash_buckets[bucket];
+               while (pn && !net_eq(pneigh_net(pn), net))
+                       pn = pn->next;
                if (pn)
                        break;
        }
@@ -2245,6 +2315,7 @@ static struct pneigh_entry *pneigh_get_next(struct seq_file *seq,
                                            loff_t *pos)
 {
        struct neigh_seq_state *state = seq->private;
+       struct net *net = seq_file_net(seq);
        struct neigh_table *tbl = state->tbl;
 
        pn = pn->next;
@@ -2252,6 +2323,8 @@ static struct pneigh_entry *pneigh_get_next(struct seq_file *seq,
                if (++state->bucket > PNEIGH_HASHMASK)
                        break;
                pn = tbl->phash_buckets[state->bucket];
+               while (pn && !net_eq(pneigh_net(pn), net))
+                       pn = pn->next;
                if (pn)
                        break;
        }
@@ -2289,6 +2362,7 @@ static void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos)
 }
 
 void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags)
+       __acquires(tbl->lock)
 {
        struct neigh_seq_state *state = seq->private;
        loff_t pos_minus_one;
@@ -2332,6 +2406,7 @@ out:
 EXPORT_SYMBOL(neigh_seq_next);
 
 void neigh_seq_stop(struct seq_file *seq, void *v)
+       __releases(tbl->lock)
 {
        struct neigh_seq_state *state = seq->private;
        struct neigh_table *tbl = state->tbl;
@@ -2453,6 +2528,7 @@ static inline size_t neigh_nlmsg_size(void)
 
 static void __neigh_notify(struct neighbour *n, int type, int flags)
 {
+       struct net *net = dev_net(n->dev);
        struct sk_buff *skb;
        int err = -ENOBUFS;
 
@@ -2467,10 +2543,10 @@ static void __neigh_notify(struct neighbour *n, int type, int flags)
                kfree_skb(skb);
                goto errout;
        }
-       err = rtnl_notify(skb, &init_net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
+       err = rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
 errout:
        if (err < 0)
-               rtnl_set_sk_err(&init_net, RTNLGRP_NEIGH, err);
+               rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
 }
 
 #ifdef CONFIG_ARPD
@@ -2478,6 +2554,7 @@ void neigh_app_ns(struct neighbour *n)
 {
        __neigh_notify(n, RTM_GETNEIGH, NLM_F_REQUEST);
 }
+EXPORT_SYMBOL(neigh_app_ns);
 #endif /* CONFIG_ARPD */
 
 #ifdef CONFIG_SYSCTL
@@ -2709,7 +2786,8 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
        neigh_path[NEIGH_CTL_PATH_PROTO].procname = p_name;
        neigh_path[NEIGH_CTL_PATH_PROTO].ctl_name = p_id;
 
-       t->sysctl_header = register_sysctl_paths(neigh_path, t->neigh_vars);
+       t->sysctl_header =
+               register_net_sysctl_table(neigh_parms_net(p), neigh_path, t->neigh_vars);
        if (!t->sysctl_header)
                goto free_procname;
 
@@ -2723,6 +2801,7 @@ free:
 err:
        return -ENOBUFS;
 }
+EXPORT_SYMBOL(neigh_sysctl_register);
 
 void neigh_sysctl_unregister(struct neigh_parms *p)
 {
@@ -2734,6 +2813,7 @@ void neigh_sysctl_unregister(struct neigh_parms *p)
                kfree(t);
        }
 }
+EXPORT_SYMBOL(neigh_sysctl_unregister);
 
 #endif /* CONFIG_SYSCTL */
 
@@ -2751,31 +2831,3 @@ static int __init neigh_init(void)
 
 subsys_initcall(neigh_init);
 
-EXPORT_SYMBOL(__neigh_event_send);
-EXPORT_SYMBOL(neigh_changeaddr);
-EXPORT_SYMBOL(neigh_compat_output);
-EXPORT_SYMBOL(neigh_connected_output);
-EXPORT_SYMBOL(neigh_create);
-EXPORT_SYMBOL(neigh_destroy);
-EXPORT_SYMBOL(neigh_event_ns);
-EXPORT_SYMBOL(neigh_ifdown);
-EXPORT_SYMBOL(neigh_lookup);
-EXPORT_SYMBOL(neigh_lookup_nodev);
-EXPORT_SYMBOL(neigh_parms_alloc);
-EXPORT_SYMBOL(neigh_parms_release);
-EXPORT_SYMBOL(neigh_rand_reach_time);
-EXPORT_SYMBOL(neigh_resolve_output);
-EXPORT_SYMBOL(neigh_table_clear);
-EXPORT_SYMBOL(neigh_table_init);
-EXPORT_SYMBOL(neigh_table_init_no_netlink);
-EXPORT_SYMBOL(neigh_update);
-EXPORT_SYMBOL(pneigh_enqueue);
-EXPORT_SYMBOL(pneigh_lookup);
-
-#ifdef CONFIG_ARPD
-EXPORT_SYMBOL(neigh_app_ns);
-#endif
-#ifdef CONFIG_SYSCTL
-EXPORT_SYMBOL(neigh_sysctl_register);
-EXPORT_SYMBOL(neigh_sysctl_unregister);
-#endif