Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[powerpc.git] / net / core / neighbour.c
index 5490afd..6f3bb73 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/socket.h>
-#include <linux/sched.h>
 #include <linux/netdevice.h>
 #include <linux/proc_fs.h>
 #ifdef CONFIG_SYSCTL
@@ -63,7 +62,7 @@ void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev);
 
 static struct neigh_table *neigh_tables;
 #ifdef CONFIG_PROC_FS
-static struct file_operations neigh_stat_seq_fops;
+static const struct file_operations neigh_stat_seq_fops;
 #endif
 
 /*
@@ -141,6 +140,8 @@ static int neigh_forced_gc(struct neigh_table *tbl)
                                n->dead = 1;
                                shrunk  = 1;
                                write_unlock(&n->lock);
+                               if (n->parms->neigh_cleanup)
+                                       n->parms->neigh_cleanup(n);
                                neigh_release(n);
                                continue;
                        }
@@ -212,6 +213,8 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
                                NEIGH_PRINTK2("neigh %p is stray.\n", n);
                        }
                        write_unlock(&n->lock);
+                       if (n->parms->neigh_cleanup)
+                               n->parms->neigh_cleanup(n);
                        neigh_release(n);
                }
        }
@@ -251,12 +254,10 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl)
                        goto out_entries;
        }
 
-       n = kmem_cache_alloc(tbl->kmem_cachep, SLAB_ATOMIC);
+       n = kmem_cache_zalloc(tbl->kmem_cachep, GFP_ATOMIC);
        if (!n)
                goto out_entries;
 
-       memset(n, 0, tbl->entry_size);
-
        skb_queue_head_init(&n->arp_queue);
        rwlock_init(&n->lock);
        n->updated        = n->used = now;
@@ -344,12 +345,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) & tbl->hash_mask;
-       
+       u32 hash_val = tbl->hash(pkey, dev);
+
        NEIGH_CACHE_STAT_INC(tbl, lookups);
 
        read_lock_bh(&tbl->lock);
-       for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
+       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);
                        NEIGH_CACHE_STAT_INC(tbl, hits);
@@ -364,12 +365,12 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, const void *pkey)
 {
        struct neighbour *n;
        int key_len = tbl->key_len;
-       u32 hash_val = tbl->hash(pkey, NULL) & tbl->hash_mask;
+       u32 hash_val = tbl->hash(pkey, NULL);
 
        NEIGH_CACHE_STAT_INC(tbl, lookups);
 
        read_lock_bh(&tbl->lock);
-       for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
+       for (n = tbl->hash_buckets[hash_val & tbl->hash_mask]; n; n = n->next) {
                if (!memcmp(n->primary_key, pkey, key_len)) {
                        neigh_hold(n);
                        NEIGH_CACHE_STAT_INC(tbl, hits);
@@ -577,16 +578,14 @@ void neigh_destroy(struct neighbour *neigh)
        while ((hh = neigh->hh) != NULL) {
                neigh->hh = hh->hh_next;
                hh->hh_next = NULL;
-               write_lock_bh(&hh->hh_lock);
+
+               write_seqlock_bh(&hh->hh_lock);
                hh->hh_output = neigh_blackhole;
-               write_unlock_bh(&hh->hh_lock);
+               write_sequnlock_bh(&hh->hh_lock);
                if (atomic_dec_and_test(&hh->hh_refcnt))
                        kfree(hh);
        }
 
-       if (neigh->parms->neigh_destructor)
-               (neigh->parms->neigh_destructor)(neigh);
-
        skb_queue_purge(&neigh->arp_queue);
 
        dev_put(neigh->dev);
@@ -677,6 +676,8 @@ static void neigh_periodic_timer(unsigned long arg)
                        *np = n->next;
                        n->dead = 1;
                        write_unlock(&n->lock);
+                       if (n->parms->neigh_cleanup)
+                               n->parms->neigh_cleanup(n);
                        neigh_release(n);
                        continue;
                }
@@ -686,16 +687,19 @@ next_elt:
                np = &n->next;
        }
 
-       /* Cycle through all hash buckets every base_reachable_time/2 ticks.
-        * ARP entry timeouts range from 1/2 base_reachable_time to 3/2
-        * base_reachable_time.
+       /* Cycle through all hash buckets every base_reachable_time/2 ticks.
+        * ARP entry timeouts range from 1/2 base_reachable_time to 3/2
+        * base_reachable_time.
         */
        expire = tbl->parms.base_reachable_time >> 1;
        expire /= (tbl->hash_mask + 1);
        if (!expire)
                expire = 1;
 
-       mod_timer(&tbl->gc_timer, now + expire);
+       if (expire>HZ)
+               mod_timer(&tbl->gc_timer, round_jiffies(now + expire));
+       else
+               mod_timer(&tbl->gc_timer, now + expire);
 
        write_unlock(&tbl->lock);
 }
@@ -740,7 +744,7 @@ static void neigh_timer_handler(unsigned long arg)
        }
 
        if (state & NUD_REACHABLE) {
-               if (time_before_eq(now, 
+               if (time_before_eq(now,
                                   neigh->confirmed + neigh->parms->reachable_time)) {
                        NEIGH_PRINTK2("neigh %p is still alive.\n", neigh);
                        next = neigh->confirmed + neigh->parms->reachable_time;
@@ -759,7 +763,7 @@ static void neigh_timer_handler(unsigned long arg)
                        notify = 1;
                }
        } else if (state & NUD_DELAY) {
-               if (time_before_eq(now, 
+               if (time_before_eq(now,
                                   neigh->confirmed + neigh->parms->delay_probe_time)) {
                        NEIGH_PRINTK2("neigh %p is now reachable.\n", neigh);
                        neigh->nud_state = NUD_REACHABLE;
@@ -845,7 +849,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
                goto out_unlock_bh;
 
        now = jiffies;
-       
+
        if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) {
                if (neigh->parms->mcast_probes + neigh->parms->app_probes) {
                        atomic_set(&neigh->probes, neigh->parms->ucast_probes);
@@ -889,7 +893,7 @@ out_unlock_bh:
        return rc;
 }
 
-static __inline__ void neigh_update_hhs(struct neighbour *neigh)
+static void neigh_update_hhs(struct neighbour *neigh)
 {
        struct hh_cache *hh;
        void (*update)(struct hh_cache*, struct net_device*, unsigned char *) =
@@ -897,9 +901,9 @@ static __inline__ void neigh_update_hhs(struct neighbour *neigh)
 
        if (update) {
                for (hh = neigh->hh; hh; hh = hh->hh_next) {
-                       write_lock_bh(&hh->hh_lock);
+                       write_seqlock_bh(&hh->hh_lock);
                        update(hh, neigh->dev, neigh->ha);
-                       write_unlock_bh(&hh->hh_lock);
+                       write_sequnlock_bh(&hh->hh_lock);
                }
        }
 }
@@ -913,13 +917,13 @@ static __inline__ void neigh_update_hhs(struct neighbour *neigh)
        NEIGH_UPDATE_F_OVERRIDE allows to override existing lladdr,
                                if it is different.
        NEIGH_UPDATE_F_WEAK_OVERRIDE will suspect existing "connected"
-                               lladdr instead of overriding it 
+                               lladdr instead of overriding it
                                if it is different.
                                It also allows to retain current state
                                if lladdr is unchanged.
        NEIGH_UPDATE_F_ADMIN    means that the change is administrative.
 
-       NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing 
+       NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing
                                NTF_ROUTER flag.
        NEIGH_UPDATE_F_ISROUTER indicates if the neighbour is known as
                                a router.
@@ -942,7 +946,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
        old    = neigh->nud_state;
        err    = -EPERM;
 
-       if (!(flags & NEIGH_UPDATE_F_ADMIN) && 
+       if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
            (old & (NUD_NOARP | NUD_PERMANENT)))
                goto out;
 
@@ -966,7 +970,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
                   - compare new & old
                   - if they are different, check override flag
                 */
-               if ((old & NUD_VALID) && 
+               if ((old & NUD_VALID) &&
                    !memcmp(lladdr, neigh->ha, dev->addr_len))
                        lladdr = neigh->ha;
        } else {
@@ -1010,8 +1014,8 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
                neigh_del_timer(neigh);
                if (new & NUD_IN_TIMER) {
                        neigh_hold(neigh);
-                       neigh_add_timer(neigh, (jiffies + 
-                                               ((new & NUD_REACHABLE) ? 
+                       neigh_add_timer(neigh, (jiffies +
+                                               ((new & NUD_REACHABLE) ?
                                                 neigh->parms->reachable_time :
                                                 0)));
                }
@@ -1073,13 +1077,13 @@ struct neighbour *neigh_event_ns(struct neigh_table *tbl,
        struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev,
                                                 lladdr || !dev->addr_len);
        if (neigh)
-               neigh_update(neigh, lladdr, NUD_STALE, 
+               neigh_update(neigh, lladdr, NUD_STALE,
                             NEIGH_UPDATE_F_OVERRIDE);
        return neigh;
 }
 
 static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst,
-                         u16 protocol)
+                         __be16 protocol)
 {
        struct hh_cache *hh;
        struct net_device *dev = dst->dev;
@@ -1089,7 +1093,7 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst,
                        break;
 
        if (!hh && (hh = kzalloc(sizeof(*hh), GFP_ATOMIC)) != NULL) {
-               rwlock_init(&hh->hh_lock);
+               seqlock_init(&hh->hh_lock);
                hh->hh_type = protocol;
                atomic_set(&hh->hh_refcnt, 0);
                hh->hh_next = NULL;
@@ -1121,11 +1125,11 @@ int neigh_compat_output(struct sk_buff *skb)
 {
        struct net_device *dev = skb->dev;
 
-       __skb_pull(skb, skb->nh.raw - skb->data);
+       __skb_pull(skb, skb_network_offset(skb));
 
        if (dev->hard_header &&
            dev->hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL,
-                            skb->len) < 0 &&
+                            skb->len) < 0 &&
            dev->rebuild_header(skb))
                return 0;
 
@@ -1143,7 +1147,7 @@ int neigh_resolve_output(struct sk_buff *skb)
        if (!dst || !(neigh = dst->neighbour))
                goto discard;
 
-       __skb_pull(skb, skb->nh.raw - skb->data);
+       __skb_pull(skb, skb_network_offset(skb));
 
        if (!neigh_event_send(neigh, skb)) {
                int err;
@@ -1186,7 +1190,7 @@ int neigh_connected_output(struct sk_buff *skb)
        struct neighbour *neigh = dst->neighbour;
        struct net_device *dev = neigh->dev;
 
-       __skb_pull(skb, skb->nh.raw - skb->data);
+       __skb_pull(skb, skb_network_offset(skb));
 
        read_lock_bh(&neigh->lock);
        err = dev->hard_header(skb, dev, ntohs(skb->protocol),
@@ -1266,10 +1270,9 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
 struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
                                      struct neigh_table *tbl)
 {
-       struct neigh_parms *p = kmalloc(sizeof(*p), GFP_KERNEL);
+       struct neigh_parms *p = kmemdup(&tbl->parms, sizeof(*p), GFP_KERNEL);
 
        if (p) {
-               memcpy(p, &tbl->parms, sizeof(*p));
                p->tbl            = tbl;
                atomic_set(&p->refcnt, 1);
                INIT_RCU_HEAD(&p->rcu_head);
@@ -1328,6 +1331,8 @@ void neigh_parms_destroy(struct neigh_parms *parms)
        kfree(parms);
 }
 
+static struct lock_class_key neigh_table_proxy_queue_class;
+
 void neigh_table_init_no_netlink(struct neigh_table *tbl)
 {
        unsigned long now = jiffies;
@@ -1339,21 +1344,17 @@ void neigh_table_init_no_netlink(struct neigh_table *tbl)
                          neigh_rand_reach_time(tbl->parms.base_reachable_time);
 
        if (!tbl->kmem_cachep)
-               tbl->kmem_cachep = kmem_cache_create(tbl->id,
-                                                    tbl->entry_size,
-                                                    0, SLAB_HWCACHE_ALIGN,
-                                                    NULL, NULL);
-
-       if (!tbl->kmem_cachep)
-               panic("cannot create neighbour cache");
-
+               tbl->kmem_cachep =
+                       kmem_cache_create(tbl->id, tbl->entry_size, 0,
+                                         SLAB_HWCACHE_ALIGN|SLAB_PANIC,
+                                         NULL, NULL);
        tbl->stats = alloc_percpu(struct neigh_statistics);
        if (!tbl->stats)
                panic("cannot create neighbour cache statistics");
-       
+
 #ifdef CONFIG_PROC_FS
        tbl->pde = create_proc_entry(tbl->id, 0, proc_net_stat);
-       if (!tbl->pde) 
+       if (!tbl->pde)
                panic("cannot create neighbour proc dir entry");
        tbl->pde->proc_fops = &neigh_stat_seq_fops;
        tbl->pde->data = tbl;
@@ -1380,7 +1381,8 @@ void neigh_table_init_no_netlink(struct neigh_table *tbl)
        init_timer(&tbl->proxy_timer);
        tbl->proxy_timer.data     = (unsigned long)tbl;
        tbl->proxy_timer.function = neigh_proxy_process;
-       skb_queue_head_init(&tbl->proxy_queue);
+       skb_queue_head_init_class(&tbl->proxy_queue,
+                       &neigh_table_proxy_queue_class);
 
        tbl->last_flush = now;
        tbl->last_rand  = now + tbl->parms.reachable_time * 20;
@@ -1439,7 +1441,7 @@ int neigh_table_clear(struct neigh_table *tbl)
        return 0;
 }
 
-int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
        struct ndmsg *ndm;
        struct nlattr *dst_attr;
@@ -1504,7 +1506,7 @@ out:
        return err;
 }
 
-int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
        struct ndmsg *ndm;
        struct nlattr *tb[NDA_MAX+1];
@@ -1548,9 +1550,14 @@ int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL;
 
                if (ndm->ndm_flags & NTF_PROXY) {
-                       err = 0;
-                       if (pneigh_lookup(tbl, dst, dev, 1) == NULL)
-                               err = -ENOBUFS;
+                       struct pneigh_entry *pn;
+
+                       err = -ENOBUFS;
+                       pn = pneigh_lookup(tbl, dst, dev, 1);
+                       if (pn) {
+                               pn->flags = ndm->ndm_flags;
+                               err = 0;
+                       }
                        goto out_dev_put;
                }
 
@@ -1563,7 +1570,7 @@ int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                                err = -ENOENT;
                                goto out_dev_put;
                        }
-       
+
                        neigh = __neigh_lookup_errno(tbl, dst, dev);
                        if (IS_ERR(neigh)) {
                                err = PTR_ERR(neigh);
@@ -1597,56 +1604,59 @@ out:
 
 static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms)
 {
-       struct rtattr *nest = NULL;
-       
-       nest = RTA_NEST(skb, NDTA_PARMS);
+       struct nlattr *nest;
+
+       nest = nla_nest_start(skb, NDTA_PARMS);
+       if (nest == NULL)
+               return -ENOBUFS;
 
        if (parms->dev)
-               RTA_PUT_U32(skb, NDTPA_IFINDEX, parms->dev->ifindex);
-
-       RTA_PUT_U32(skb, NDTPA_REFCNT, atomic_read(&parms->refcnt));
-       RTA_PUT_U32(skb, NDTPA_QUEUE_LEN, parms->queue_len);
-       RTA_PUT_U32(skb, NDTPA_PROXY_QLEN, parms->proxy_qlen);
-       RTA_PUT_U32(skb, NDTPA_APP_PROBES, parms->app_probes);
-       RTA_PUT_U32(skb, NDTPA_UCAST_PROBES, parms->ucast_probes);
-       RTA_PUT_U32(skb, NDTPA_MCAST_PROBES, parms->mcast_probes);
-       RTA_PUT_MSECS(skb, NDTPA_REACHABLE_TIME, parms->reachable_time);
-       RTA_PUT_MSECS(skb, NDTPA_BASE_REACHABLE_TIME,
+               NLA_PUT_U32(skb, NDTPA_IFINDEX, parms->dev->ifindex);
+
+       NLA_PUT_U32(skb, NDTPA_REFCNT, atomic_read(&parms->refcnt));
+       NLA_PUT_U32(skb, NDTPA_QUEUE_LEN, parms->queue_len);
+       NLA_PUT_U32(skb, NDTPA_PROXY_QLEN, parms->proxy_qlen);
+       NLA_PUT_U32(skb, NDTPA_APP_PROBES, parms->app_probes);
+       NLA_PUT_U32(skb, NDTPA_UCAST_PROBES, parms->ucast_probes);
+       NLA_PUT_U32(skb, NDTPA_MCAST_PROBES, parms->mcast_probes);
+       NLA_PUT_MSECS(skb, NDTPA_REACHABLE_TIME, parms->reachable_time);
+       NLA_PUT_MSECS(skb, NDTPA_BASE_REACHABLE_TIME,
                      parms->base_reachable_time);
-       RTA_PUT_MSECS(skb, NDTPA_GC_STALETIME, parms->gc_staletime);
-       RTA_PUT_MSECS(skb, NDTPA_DELAY_PROBE_TIME, parms->delay_probe_time);
-       RTA_PUT_MSECS(skb, NDTPA_RETRANS_TIME, parms->retrans_time);
-       RTA_PUT_MSECS(skb, NDTPA_ANYCAST_DELAY, parms->anycast_delay);
-       RTA_PUT_MSECS(skb, NDTPA_PROXY_DELAY, parms->proxy_delay);
-       RTA_PUT_MSECS(skb, NDTPA_LOCKTIME, parms->locktime);
+       NLA_PUT_MSECS(skb, NDTPA_GC_STALETIME, parms->gc_staletime);
+       NLA_PUT_MSECS(skb, NDTPA_DELAY_PROBE_TIME, parms->delay_probe_time);
+       NLA_PUT_MSECS(skb, NDTPA_RETRANS_TIME, parms->retrans_time);
+       NLA_PUT_MSECS(skb, NDTPA_ANYCAST_DELAY, parms->anycast_delay);
+       NLA_PUT_MSECS(skb, NDTPA_PROXY_DELAY, parms->proxy_delay);
+       NLA_PUT_MSECS(skb, NDTPA_LOCKTIME, parms->locktime);
 
-       return RTA_NEST_END(skb, nest);
+       return nla_nest_end(skb, nest);
 
-rtattr_failure:
-       return RTA_NEST_CANCEL(skb, nest);
+nla_put_failure:
+       return nla_nest_cancel(skb, nest);
 }
 
-static int neightbl_fill_info(struct neigh_table *tbl, struct sk_buff *skb,
-                             struct netlink_callback *cb)
+static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl,
+                             u32 pid, u32 seq, int type, int flags)
 {
        struct nlmsghdr *nlh;
        struct ndtmsg *ndtmsg;
 
-       nlh = NLMSG_NEW_ANSWER(skb, cb, RTM_NEWNEIGHTBL, sizeof(struct ndtmsg),
-                              NLM_F_MULTI);
+       nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags);
+       if (nlh == NULL)
+               return -EMSGSIZE;
 
-       ndtmsg = NLMSG_DATA(nlh);
+       ndtmsg = nlmsg_data(nlh);
 
        read_lock_bh(&tbl->lock);
        ndtmsg->ndtm_family = tbl->family;
        ndtmsg->ndtm_pad1   = 0;
        ndtmsg->ndtm_pad2   = 0;
 
-       RTA_PUT_STRING(skb, NDTA_NAME, tbl->id);
-       RTA_PUT_MSECS(skb, NDTA_GC_INTERVAL, tbl->gc_interval);
-       RTA_PUT_U32(skb, NDTA_THRESH1, tbl->gc_thresh1);
-       RTA_PUT_U32(skb, NDTA_THRESH2, tbl->gc_thresh2);
-       RTA_PUT_U32(skb, NDTA_THRESH3, tbl->gc_thresh3);
+       NLA_PUT_STRING(skb, NDTA_NAME, tbl->id);
+       NLA_PUT_MSECS(skb, NDTA_GC_INTERVAL, tbl->gc_interval);
+       NLA_PUT_U32(skb, NDTA_THRESH1, tbl->gc_thresh1);
+       NLA_PUT_U32(skb, NDTA_THRESH2, tbl->gc_thresh2);
+       NLA_PUT_U32(skb, NDTA_THRESH3, tbl->gc_thresh3);
 
        {
                unsigned long now = jiffies;
@@ -1665,7 +1675,7 @@ static int neightbl_fill_info(struct neigh_table *tbl, struct sk_buff *skb,
                        .ndtc_proxy_qlen        = tbl->proxy_queue.qlen,
                };
 
-               RTA_PUT(skb, NDTA_CONFIG, sizeof(ndc), &ndc);
+               NLA_PUT(skb, NDTA_CONFIG, sizeof(ndc), &ndc);
        }
 
        {
@@ -1690,62 +1700,59 @@ static int neightbl_fill_info(struct neigh_table *tbl, struct sk_buff *skb,
                        ndst.ndts_forced_gc_runs        += st->forced_gc_runs;
                }
 
-               RTA_PUT(skb, NDTA_STATS, sizeof(ndst), &ndst);
+               NLA_PUT(skb, NDTA_STATS, sizeof(ndst), &ndst);
        }
 
        BUG_ON(tbl->parms.dev);
        if (neightbl_fill_parms(skb, &tbl->parms) < 0)
-               goto rtattr_failure;
+               goto nla_put_failure;
 
        read_unlock_bh(&tbl->lock);
-       return NLMSG_END(skb, nlh);
+       return nlmsg_end(skb, nlh);
 
-rtattr_failure:
+nla_put_failure:
        read_unlock_bh(&tbl->lock);
-       return NLMSG_CANCEL(skb, nlh);
-nlmsg_failure:
-       return -1;
+       nlmsg_cancel(skb, nlh);
+       return -EMSGSIZE;
 }
 
-static int neightbl_fill_param_info(struct neigh_table *tbl,
+static int neightbl_fill_param_info(struct sk_buff *skb,
+                                   struct neigh_table *tbl,
                                    struct neigh_parms *parms,
-                                   struct sk_buff *skb,
-                                   struct netlink_callback *cb)
+                                   u32 pid, u32 seq, int type,
+                                   unsigned int flags)
 {
        struct ndtmsg *ndtmsg;
        struct nlmsghdr *nlh;
 
-       nlh = NLMSG_NEW_ANSWER(skb, cb, RTM_NEWNEIGHTBL, sizeof(struct ndtmsg),
-                              NLM_F_MULTI);
+       nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags);
+       if (nlh == NULL)
+               return -EMSGSIZE;
 
-       ndtmsg = NLMSG_DATA(nlh);
+       ndtmsg = nlmsg_data(nlh);
 
        read_lock_bh(&tbl->lock);
        ndtmsg->ndtm_family = tbl->family;
        ndtmsg->ndtm_pad1   = 0;
        ndtmsg->ndtm_pad2   = 0;
-       RTA_PUT_STRING(skb, NDTA_NAME, tbl->id);
 
-       if (neightbl_fill_parms(skb, parms) < 0)
-               goto rtattr_failure;
+       if (nla_put_string(skb, NDTA_NAME, tbl->id) < 0 ||
+           neightbl_fill_parms(skb, parms) < 0)
+               goto errout;
 
        read_unlock_bh(&tbl->lock);
-       return NLMSG_END(skb, nlh);
-
-rtattr_failure:
+       return nlmsg_end(skb, nlh);
+errout:
        read_unlock_bh(&tbl->lock);
-       return NLMSG_CANCEL(skb, nlh);
-
-nlmsg_failure:
-       return -1;
+       nlmsg_cancel(skb, nlh);
+       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))
@@ -1754,149 +1761,194 @@ static inline struct neigh_parms *lookup_neigh_params(struct neigh_table *tbl,
        return NULL;
 }
 
-int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static struct nla_policy nl_neightbl_policy[NDTA_MAX+1] __read_mostly = {
+       [NDTA_NAME]             = { .type = NLA_STRING },
+       [NDTA_THRESH1]          = { .type = NLA_U32 },
+       [NDTA_THRESH2]          = { .type = NLA_U32 },
+       [NDTA_THRESH3]          = { .type = NLA_U32 },
+       [NDTA_GC_INTERVAL]      = { .type = NLA_U64 },
+       [NDTA_PARMS]            = { .type = NLA_NESTED },
+};
+
+static struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] __read_mostly = {
+       [NDTPA_IFINDEX]                 = { .type = NLA_U32 },
+       [NDTPA_QUEUE_LEN]               = { .type = NLA_U32 },
+       [NDTPA_PROXY_QLEN]              = { .type = NLA_U32 },
+       [NDTPA_APP_PROBES]              = { .type = NLA_U32 },
+       [NDTPA_UCAST_PROBES]            = { .type = NLA_U32 },
+       [NDTPA_MCAST_PROBES]            = { .type = NLA_U32 },
+       [NDTPA_BASE_REACHABLE_TIME]     = { .type = NLA_U64 },
+       [NDTPA_GC_STALETIME]            = { .type = NLA_U64 },
+       [NDTPA_DELAY_PROBE_TIME]        = { .type = NLA_U64 },
+       [NDTPA_RETRANS_TIME]            = { .type = NLA_U64 },
+       [NDTPA_ANYCAST_DELAY]           = { .type = NLA_U64 },
+       [NDTPA_PROXY_DELAY]             = { .type = NLA_U64 },
+       [NDTPA_LOCKTIME]                = { .type = NLA_U64 },
+};
+
+static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
        struct neigh_table *tbl;
-       struct ndtmsg *ndtmsg = NLMSG_DATA(nlh);
-       struct rtattr **tb = arg;
-       int err = -EINVAL;
+       struct ndtmsg *ndtmsg;
+       struct nlattr *tb[NDTA_MAX+1];
+       int err;
+
+       err = nlmsg_parse(nlh, sizeof(*ndtmsg), tb, NDTA_MAX,
+                         nl_neightbl_policy);
+       if (err < 0)
+               goto errout;
 
-       if (!tb[NDTA_NAME - 1] || !RTA_PAYLOAD(tb[NDTA_NAME - 1]))
-               return -EINVAL;
+       if (tb[NDTA_NAME] == NULL) {
+               err = -EINVAL;
+               goto errout;
+       }
 
+       ndtmsg = nlmsg_data(nlh);
        read_lock(&neigh_tbl_lock);
        for (tbl = neigh_tables; tbl; tbl = tbl->next) {
                if (ndtmsg->ndtm_family && tbl->family != ndtmsg->ndtm_family)
                        continue;
 
-               if (!rtattr_strcmp(tb[NDTA_NAME - 1], tbl->id))
+               if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0)
                        break;
        }
 
        if (tbl == NULL) {
                err = -ENOENT;
-               goto errout;
+               goto errout_locked;
        }
 
-       /* 
+       /*
         * We acquire tbl->lock to be nice to the periodic timers and
         * make sure they always see a consistent set of values.
         */
        write_lock_bh(&tbl->lock);
 
-       if (tb[NDTA_THRESH1 - 1])
-               tbl->gc_thresh1 = RTA_GET_U32(tb[NDTA_THRESH1 - 1]);
-
-       if (tb[NDTA_THRESH2 - 1])
-               tbl->gc_thresh2 = RTA_GET_U32(tb[NDTA_THRESH2 - 1]);
-
-       if (tb[NDTA_THRESH3 - 1])
-               tbl->gc_thresh3 = RTA_GET_U32(tb[NDTA_THRESH3 - 1]);
-
-       if (tb[NDTA_GC_INTERVAL - 1])
-               tbl->gc_interval = RTA_GET_MSECS(tb[NDTA_GC_INTERVAL - 1]);
-
-       if (tb[NDTA_PARMS - 1]) {
-               struct rtattr *tbp[NDTPA_MAX];
+       if (tb[NDTA_PARMS]) {
+               struct nlattr *tbp[NDTPA_MAX+1];
                struct neigh_parms *p;
-               u32 ifindex = 0;
+               int i, ifindex = 0;
 
-               if (rtattr_parse_nested(tbp, NDTPA_MAX, tb[NDTA_PARMS - 1]) < 0)
-                       goto rtattr_failure;
+               err = nla_parse_nested(tbp, NDTPA_MAX, tb[NDTA_PARMS],
+                                      nl_ntbl_parm_policy);
+               if (err < 0)
+                       goto errout_tbl_lock;
 
-               if (tbp[NDTPA_IFINDEX - 1])
-                       ifindex = RTA_GET_U32(tbp[NDTPA_IFINDEX - 1]);
+               if (tbp[NDTPA_IFINDEX])
+                       ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]);
 
                p = lookup_neigh_params(tbl, ifindex);
                if (p == NULL) {
                        err = -ENOENT;
-                       goto rtattr_failure;
+                       goto errout_tbl_lock;
                }
-       
-               if (tbp[NDTPA_QUEUE_LEN - 1])
-                       p->queue_len = RTA_GET_U32(tbp[NDTPA_QUEUE_LEN - 1]);
-
-               if (tbp[NDTPA_PROXY_QLEN - 1])
-                       p->proxy_qlen = RTA_GET_U32(tbp[NDTPA_PROXY_QLEN - 1]);
 
-               if (tbp[NDTPA_APP_PROBES - 1])
-                       p->app_probes = RTA_GET_U32(tbp[NDTPA_APP_PROBES - 1]);
-
-               if (tbp[NDTPA_UCAST_PROBES - 1])
-                       p->ucast_probes =
-                          RTA_GET_U32(tbp[NDTPA_UCAST_PROBES - 1]);
-
-               if (tbp[NDTPA_MCAST_PROBES - 1])
-                       p->mcast_probes =
-                          RTA_GET_U32(tbp[NDTPA_MCAST_PROBES - 1]);
-
-               if (tbp[NDTPA_BASE_REACHABLE_TIME - 1])
-                       p->base_reachable_time =
-                          RTA_GET_MSECS(tbp[NDTPA_BASE_REACHABLE_TIME - 1]);
-
-               if (tbp[NDTPA_GC_STALETIME - 1])
-                       p->gc_staletime =
-                          RTA_GET_MSECS(tbp[NDTPA_GC_STALETIME - 1]);
+               for (i = 1; i <= NDTPA_MAX; i++) {
+                       if (tbp[i] == NULL)
+                               continue;
 
-               if (tbp[NDTPA_DELAY_PROBE_TIME - 1])
-                       p->delay_probe_time =
-                          RTA_GET_MSECS(tbp[NDTPA_DELAY_PROBE_TIME - 1]);
+                       switch (i) {
+                       case NDTPA_QUEUE_LEN:
+                               p->queue_len = nla_get_u32(tbp[i]);
+                               break;
+                       case NDTPA_PROXY_QLEN:
+                               p->proxy_qlen = nla_get_u32(tbp[i]);
+                               break;
+                       case NDTPA_APP_PROBES:
+                               p->app_probes = nla_get_u32(tbp[i]);
+                               break;
+                       case NDTPA_UCAST_PROBES:
+                               p->ucast_probes = nla_get_u32(tbp[i]);
+                               break;
+                       case NDTPA_MCAST_PROBES:
+                               p->mcast_probes = nla_get_u32(tbp[i]);
+                               break;
+                       case NDTPA_BASE_REACHABLE_TIME:
+                               p->base_reachable_time = nla_get_msecs(tbp[i]);
+                               break;
+                       case NDTPA_GC_STALETIME:
+                               p->gc_staletime = nla_get_msecs(tbp[i]);
+                               break;
+                       case NDTPA_DELAY_PROBE_TIME:
+                               p->delay_probe_time = nla_get_msecs(tbp[i]);
+                               break;
+                       case NDTPA_RETRANS_TIME:
+                               p->retrans_time = nla_get_msecs(tbp[i]);
+                               break;
+                       case NDTPA_ANYCAST_DELAY:
+                               p->anycast_delay = nla_get_msecs(tbp[i]);
+                               break;
+                       case NDTPA_PROXY_DELAY:
+                               p->proxy_delay = nla_get_msecs(tbp[i]);
+                               break;
+                       case NDTPA_LOCKTIME:
+                               p->locktime = nla_get_msecs(tbp[i]);
+                               break;
+                       }
+               }
+       }
 
-               if (tbp[NDTPA_RETRANS_TIME - 1])
-                       p->retrans_time =
-                          RTA_GET_MSECS(tbp[NDTPA_RETRANS_TIME - 1]);
+       if (tb[NDTA_THRESH1])
+               tbl->gc_thresh1 = nla_get_u32(tb[NDTA_THRESH1]);
 
-               if (tbp[NDTPA_ANYCAST_DELAY - 1])
-                       p->anycast_delay =
-                          RTA_GET_MSECS(tbp[NDTPA_ANYCAST_DELAY - 1]);
+       if (tb[NDTA_THRESH2])
+               tbl->gc_thresh2 = nla_get_u32(tb[NDTA_THRESH2]);
 
-               if (tbp[NDTPA_PROXY_DELAY - 1])
-                       p->proxy_delay =
-                          RTA_GET_MSECS(tbp[NDTPA_PROXY_DELAY - 1]);
+       if (tb[NDTA_THRESH3])
+               tbl->gc_thresh3 = nla_get_u32(tb[NDTA_THRESH3]);
 
-               if (tbp[NDTPA_LOCKTIME - 1])
-                       p->locktime = RTA_GET_MSECS(tbp[NDTPA_LOCKTIME - 1]);
-       }
+       if (tb[NDTA_GC_INTERVAL])
+               tbl->gc_interval = nla_get_msecs(tb[NDTA_GC_INTERVAL]);
 
        err = 0;
 
-rtattr_failure:
+errout_tbl_lock:
        write_unlock_bh(&tbl->lock);
-errout:
+errout_locked:
        read_unlock(&neigh_tbl_lock);
+errout:
        return err;
 }
 
-int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
+static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
 {
-       int idx, family;
-       int s_idx = cb->args[0];
+       int family, tidx, nidx = 0;
+       int tbl_skip = cb->args[0];
+       int neigh_skip = cb->args[1];
        struct neigh_table *tbl;
 
-       family = ((struct rtgenmsg *)NLMSG_DATA(cb->nlh))->rtgen_family;
+       family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family;
 
        read_lock(&neigh_tbl_lock);
-       for (tbl = neigh_tables, idx = 0; tbl; tbl = tbl->next) {
+       for (tbl = neigh_tables, tidx = 0; tbl; tbl = tbl->next, tidx++) {
                struct neigh_parms *p;
 
-               if (idx < s_idx || (family && tbl->family != family))
+               if (tidx < tbl_skip || (family && tbl->family != family))
                        continue;
 
-               if (neightbl_fill_info(tbl, skb, cb) <= 0)
+               if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).pid,
+                                      cb->nlh->nlmsg_seq, RTM_NEWNEIGHTBL,
+                                      NLM_F_MULTI) <= 0)
                        break;
 
-               for (++idx, p = tbl->parms.next; p; p = p->next, idx++) {
-                       if (idx < s_idx)
+               for (nidx = 0, p = tbl->parms.next; p; p = p->next, nidx++) {
+                       if (nidx < neigh_skip)
                                continue;
 
-                       if (neightbl_fill_param_info(tbl, p, skb, cb) <= 0)
+                       if (neightbl_fill_param_info(skb, tbl, p,
+                                                    NETLINK_CB(cb->skb).pid,
+                                                    cb->nlh->nlmsg_seq,
+                                                    RTM_NEWNEIGHTBL,
+                                                    NLM_F_MULTI) <= 0)
                                goto out;
                }
 
+               neigh_skip = 0;
        }
 out:
        read_unlock(&neigh_tbl_lock);
-       cb->args[0] = idx;
+       cb->args[0] = tidx;
+       cb->args[1] = nidx;
 
        return skb->len;
 }
@@ -1911,7 +1963,7 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh,
 
        nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags);
        if (nlh == NULL)
-               return -ENOBUFS;
+               return -EMSGSIZE;
 
        ndm = nlmsg_data(nlh);
        ndm->ndm_family  = neigh->ops->family;
@@ -1943,7 +1995,8 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh,
        return nlmsg_end(skb, nlh);
 
 nla_put_failure:
-       return nlmsg_cancel(skb, nlh);
+       nlmsg_cancel(skb, nlh);
+       return -EMSGSIZE;
 }
 
 
@@ -1954,12 +2007,12 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
        int rc, h, s_h = cb->args[1];
        int idx, s_idx = idx = cb->args[2];
 
+       read_lock_bh(&tbl->lock);
        for (h = 0; h <= tbl->hash_mask; h++) {
                if (h < s_h)
                        continue;
                if (h > s_h)
                        s_idx = 0;
-               read_lock_bh(&tbl->lock);
                for (n = tbl->hash_buckets[h], idx = 0; n; n = n->next, idx++) {
                        if (idx < s_idx)
                                continue;
@@ -1972,8 +2025,8 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
                                goto out;
                        }
                }
-               read_unlock_bh(&tbl->lock);
        }
+       read_unlock_bh(&tbl->lock);
        rc = skb->len;
 out:
        cb->args[1] = h;
@@ -1981,7 +2034,7 @@ out:
        return rc;
 }
 
-int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
+static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct neigh_table *tbl;
        int t, family, s_t;
@@ -2041,8 +2094,11 @@ void __neigh_for_each_release(struct neigh_table *tbl,
                        } else
                                np = &n->next;
                        write_unlock(&n->lock);
-                       if (release)
+                       if (release) {
+                               if (n->parms->neigh_cleanup)
+                                       n->parms->neigh_cleanup(n);
                                neigh_release(n);
+                       }
                }
        }
 }
@@ -2273,7 +2329,7 @@ static void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos)
 
        if (*pos == 0)
                return SEQ_START_TOKEN;
-       
+
        for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
                if (!cpu_possible(cpu))
                        continue;
@@ -2337,7 +2393,7 @@ static int neigh_stat_seq_show(struct seq_file *seq, void *v)
        return 0;
 }
 
-static struct seq_operations neigh_stat_seq_ops = {
+static const struct seq_operations neigh_stat_seq_ops = {
        .start  = neigh_stat_seq_start,
        .next   = neigh_stat_seq_next,
        .stop   = neigh_stat_seq_stop,
@@ -2355,7 +2411,7 @@ static int neigh_stat_seq_open(struct inode *inode, struct file *file)
        return ret;
 };
 
-static struct file_operations neigh_stat_seq_fops = {
+static const struct file_operations neigh_stat_seq_fops = {
        .owner   = THIS_MODULE,
        .open    = neigh_stat_seq_open,
        .read    = seq_read,
@@ -2366,36 +2422,45 @@ static struct file_operations neigh_stat_seq_fops = {
 #endif /* CONFIG_PROC_FS */
 
 #ifdef CONFIG_ARPD
-void neigh_app_ns(struct neighbour *n)
+static inline size_t neigh_nlmsg_size(void)
+{
+       return NLMSG_ALIGN(sizeof(struct ndmsg))
+              + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */
+              + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */
+              + nla_total_size(sizeof(struct nda_cacheinfo))
+              + nla_total_size(4); /* NDA_PROBES */
+}
+
+static void __neigh_notify(struct neighbour *n, int type, int flags)
 {
        struct sk_buff *skb;
+       int err = -ENOBUFS;
 
-       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+       skb = nlmsg_new(neigh_nlmsg_size(), GFP_ATOMIC);
        if (skb == NULL)
-               return;
+               goto errout;
 
-       if (neigh_fill_info(skb, n, 0, 0, RTM_GETNEIGH, NLM_F_REQUEST) <= 0)
+       err = neigh_fill_info(skb, n, 0, 0, type, flags);
+       if (err < 0) {
+               /* -EMSGSIZE implies BUG in neigh_nlmsg_size() */
+               WARN_ON(err == -EMSGSIZE);
                kfree_skb(skb);
-       else {
-               NETLINK_CB(skb).dst_group  = RTNLGRP_NEIGH;
-               netlink_broadcast(rtnl, skb, 0, RTNLGRP_NEIGH, GFP_ATOMIC);
+               goto errout;
        }
+       err = rtnl_notify(skb, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
+errout:
+       if (err < 0)
+               rtnl_set_sk_err(RTNLGRP_NEIGH, err);
 }
 
-static void neigh_app_notify(struct neighbour *n)
+void neigh_app_ns(struct neighbour *n)
 {
-       struct sk_buff *skb;
-
-       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
-       if (skb == NULL)
-               return;
+       __neigh_notify(n, RTM_GETNEIGH, NLM_F_REQUEST);
+}
 
-       if (neigh_fill_info(skb, n, 0, 0, RTM_NEWNEIGH, 0) <= 0)
-               kfree_skb(skb);
-       else {
-               NETLINK_CB(skb).dst_group  = RTNLGRP_NEIGH;
-               netlink_broadcast(rtnl, skb, 0, RTNLGRP_NEIGH, GFP_ATOMIC);
-       }
+static void neigh_app_notify(struct neighbour *n)
+{
+       __neigh_notify(n, RTM_NEWNEIGH, 0);
 }
 
 #endif /* CONFIG_ARPD */
@@ -2409,7 +2474,7 @@ static struct neigh_sysctl_table {
        ctl_table               neigh_neigh_dir[2];
        ctl_table               neigh_proto_dir[2];
        ctl_table               neigh_root_dir[2];
-} neigh_sysctl_template = {
+} neigh_sysctl_template __read_mostly = {
        .neigh_vars = {
                {
                        .ctl_name       = NET_NEIGH_MCAST_SOLICIT,
@@ -2572,17 +2637,17 @@ static struct neigh_sysctl_table {
 };
 
 int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
-                         int p_id, int pdev_id, char *p_name, 
+                         int p_id, int pdev_id, char *p_name,
                          proc_handler *handler, ctl_handler *strategy)
 {
-       struct neigh_sysctl_table *t = kmalloc(sizeof(*t), GFP_KERNEL);
+       struct neigh_sysctl_table *t = kmemdup(&neigh_sysctl_template,
+                                              sizeof(*t), GFP_KERNEL);
        const char *dev_name_source = NULL;
        char *dev_name = NULL;
        int err = 0;
 
        if (!t)
                return -ENOBUFS;
-       memcpy(t, &neigh_sysctl_template, sizeof(*t));
        t->neigh_vars[0].data  = &p->mcast_probes;
        t->neigh_vars[1].data  = &p->ucast_probes;
        t->neigh_vars[2].data  = &p->app_probes;
@@ -2604,7 +2669,7 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
                t->neigh_vars[14].procname = NULL;
                t->neigh_vars[15].procname = NULL;
        } else {
-               dev_name_source = t->neigh_dev[0].procname;
+               dev_name_source = t->neigh_dev[0].procname;
                t->neigh_vars[12].data = (int *)(p + 1);
                t->neigh_vars[13].data = (int *)(p + 1) + 1;
                t->neigh_vars[14].data = (int *)(p + 1) + 2;
@@ -2639,7 +2704,7 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
                goto free;
        }
 
-       t->neigh_dev[0].procname = dev_name;
+       t->neigh_dev[0].procname = dev_name;
 
        t->neigh_neigh_dir[0].ctl_name = pdev_id;
 
@@ -2651,7 +2716,7 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
        t->neigh_proto_dir[0].child    = t->neigh_neigh_dir;
        t->neigh_root_dir[0].child     = t->neigh_proto_dir;
 
-       t->sysctl_header = register_sysctl_table(t->neigh_root_dir, 0);
+       t->sysctl_header = register_sysctl_table(t->neigh_root_dir);
        if (!t->sysctl_header) {
                err = -ENOBUFS;
                goto free_procname;
@@ -2681,15 +2746,26 @@ void neigh_sysctl_unregister(struct neigh_parms *p)
 
 #endif /* CONFIG_SYSCTL */
 
+static int __init neigh_init(void)
+{
+       rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL);
+       rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL);
+       rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info);
+
+       rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info);
+       rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL);
+
+       return 0;
+}
+
+subsys_initcall(neigh_init);
+
 EXPORT_SYMBOL(__neigh_event_send);
-EXPORT_SYMBOL(neigh_add);
 EXPORT_SYMBOL(neigh_changeaddr);
 EXPORT_SYMBOL(neigh_compat_output);
 EXPORT_SYMBOL(neigh_connected_output);
 EXPORT_SYMBOL(neigh_create);
-EXPORT_SYMBOL(neigh_delete);
 EXPORT_SYMBOL(neigh_destroy);
-EXPORT_SYMBOL(neigh_dump_info);
 EXPORT_SYMBOL(neigh_event_ns);
 EXPORT_SYMBOL(neigh_ifdown);
 EXPORT_SYMBOL(neigh_lookup);
@@ -2702,11 +2778,8 @@ EXPORT_SYMBOL(neigh_table_clear);
 EXPORT_SYMBOL(neigh_table_init);
 EXPORT_SYMBOL(neigh_table_init_no_netlink);
 EXPORT_SYMBOL(neigh_update);
-EXPORT_SYMBOL(neigh_update_hhs);
 EXPORT_SYMBOL(pneigh_enqueue);
 EXPORT_SYMBOL(pneigh_lookup);
-EXPORT_SYMBOL(neightbl_dump_info);
-EXPORT_SYMBOL(neightbl_set);
 
 #ifdef CONFIG_ARPD
 EXPORT_SYMBOL(neigh_app_ns);