neigh: remove dynamic neigh table registration support
Currently there are only three neigh tables in the whole kernel: arp table, ndisc table and decnet neigh table. What's more, we don't support registering multiple tables per family. Therefore we can just make these tables statically built-in. Cc: David S. Miller <davem@davemloft.net> Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
b2e2f0c779
commit
d7480fd3b1
|
@ -220,6 +220,13 @@ struct neigh_table {
|
|||
struct pneigh_entry **phash_buckets;
|
||||
};
|
||||
|
||||
enum {
|
||||
NEIGH_ARP_TABLE = 0,
|
||||
NEIGH_ND_TABLE = 1,
|
||||
NEIGH_DN_TABLE = 2,
|
||||
NEIGH_NR_TABLES,
|
||||
};
|
||||
|
||||
static inline int neigh_parms_family(struct neigh_parms *p)
|
||||
{
|
||||
return p->tbl->family;
|
||||
|
@ -240,8 +247,8 @@ static inline void *neighbour_priv(const struct neighbour *n)
|
|||
#define NEIGH_UPDATE_F_ISROUTER 0x40000000
|
||||
#define NEIGH_UPDATE_F_ADMIN 0x80000000
|
||||
|
||||
void neigh_table_init(struct neigh_table *tbl);
|
||||
int neigh_table_clear(struct neigh_table *tbl);
|
||||
void neigh_table_init(int index, struct neigh_table *tbl);
|
||||
int neigh_table_clear(int index, struct neigh_table *tbl);
|
||||
struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
|
||||
struct net_device *dev);
|
||||
struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
|
||||
|
|
|
@ -56,7 +56,6 @@ 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);
|
||||
|
||||
static struct neigh_table *neigh_tables;
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static const struct file_operations neigh_stat_seq_fops;
|
||||
#endif
|
||||
|
@ -87,13 +86,8 @@ static const struct file_operations neigh_stat_seq_fops;
|
|||
the most complicated procedure, which we allow is dev->hard_header.
|
||||
It is supposed, that dev->hard_header is simplistic and does
|
||||
not make callbacks to neighbour tables.
|
||||
|
||||
The last lock is neigh_tbl_lock. It is pure SMP lock, protecting
|
||||
list of neighbour tables. This list is used only in process context,
|
||||
*/
|
||||
|
||||
static DEFINE_RWLOCK(neigh_tbl_lock);
|
||||
|
||||
static int neigh_blackhole(struct neighbour *neigh, struct sk_buff *skb)
|
||||
{
|
||||
kfree_skb(skb);
|
||||
|
@ -1520,7 +1514,9 @@ static void neigh_parms_destroy(struct neigh_parms *parms)
|
|||
|
||||
static struct lock_class_key neigh_table_proxy_queue_class;
|
||||
|
||||
static void neigh_table_init_no_netlink(struct neigh_table *tbl)
|
||||
static struct neigh_table *neigh_tables[NEIGH_NR_TABLES] __read_mostly;
|
||||
|
||||
void neigh_table_init(int index, struct neigh_table *tbl)
|
||||
{
|
||||
unsigned long now = jiffies;
|
||||
unsigned long phsize;
|
||||
|
@ -1566,34 +1562,14 @@ static void neigh_table_init_no_netlink(struct neigh_table *tbl)
|
|||
|
||||
tbl->last_flush = now;
|
||||
tbl->last_rand = now + tbl->parms.reachable_time * 20;
|
||||
}
|
||||
|
||||
void neigh_table_init(struct neigh_table *tbl)
|
||||
{
|
||||
struct neigh_table *tmp;
|
||||
|
||||
neigh_table_init_no_netlink(tbl);
|
||||
write_lock(&neigh_tbl_lock);
|
||||
for (tmp = neigh_tables; tmp; tmp = tmp->next) {
|
||||
if (tmp->family == tbl->family)
|
||||
break;
|
||||
}
|
||||
tbl->next = neigh_tables;
|
||||
neigh_tables = tbl;
|
||||
write_unlock(&neigh_tbl_lock);
|
||||
|
||||
if (unlikely(tmp)) {
|
||||
pr_err("Registering multiple tables for family %d\n",
|
||||
tbl->family);
|
||||
dump_stack();
|
||||
}
|
||||
neigh_tables[index] = tbl;
|
||||
}
|
||||
EXPORT_SYMBOL(neigh_table_init);
|
||||
|
||||
int neigh_table_clear(struct neigh_table *tbl)
|
||||
int neigh_table_clear(int index, struct neigh_table *tbl)
|
||||
{
|
||||
struct neigh_table **tp;
|
||||
|
||||
neigh_tables[index] = NULL;
|
||||
/* It is not clean... Fix it to unload IPv6 module safely */
|
||||
cancel_delayed_work_sync(&tbl->gc_work);
|
||||
del_timer_sync(&tbl->proxy_timer);
|
||||
|
@ -1601,14 +1577,6 @@ int neigh_table_clear(struct neigh_table *tbl)
|
|||
neigh_ifdown(tbl, NULL);
|
||||
if (atomic_read(&tbl->entries))
|
||||
pr_crit("neighbour leakage\n");
|
||||
write_lock(&neigh_tbl_lock);
|
||||
for (tp = &neigh_tables; *tp; tp = &(*tp)->next) {
|
||||
if (*tp == tbl) {
|
||||
*tp = tbl->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
write_unlock(&neigh_tbl_lock);
|
||||
|
||||
call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu,
|
||||
neigh_hash_free_rcu);
|
||||
|
@ -1626,12 +1594,32 @@ int neigh_table_clear(struct neigh_table *tbl)
|
|||
}
|
||||
EXPORT_SYMBOL(neigh_table_clear);
|
||||
|
||||
static struct neigh_table *neigh_find_table(int family)
|
||||
{
|
||||
struct neigh_table *tbl = NULL;
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
tbl = neigh_tables[NEIGH_ARP_TABLE];
|
||||
break;
|
||||
case AF_INET6:
|
||||
tbl = neigh_tables[NEIGH_ND_TABLE];
|
||||
break;
|
||||
case AF_DECnet:
|
||||
tbl = neigh_tables[NEIGH_DN_TABLE];
|
||||
break;
|
||||
}
|
||||
|
||||
return tbl;
|
||||
}
|
||||
|
||||
static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
{
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct ndmsg *ndm;
|
||||
struct nlattr *dst_attr;
|
||||
struct neigh_table *tbl;
|
||||
struct neighbour *neigh;
|
||||
struct net_device *dev = NULL;
|
||||
int err = -EINVAL;
|
||||
|
||||
|
@ -1652,39 +1640,31 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|||
}
|
||||
}
|
||||
|
||||
read_lock(&neigh_tbl_lock);
|
||||
for (tbl = neigh_tables; tbl; tbl = tbl->next) {
|
||||
struct neighbour *neigh;
|
||||
tbl = neigh_find_table(ndm->ndm_family);
|
||||
if (tbl == NULL)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
if (tbl->family != ndm->ndm_family)
|
||||
continue;
|
||||
read_unlock(&neigh_tbl_lock);
|
||||
if (nla_len(dst_attr) < tbl->key_len)
|
||||
goto out;
|
||||
|
||||
if (nla_len(dst_attr) < tbl->key_len)
|
||||
goto out;
|
||||
|
||||
if (ndm->ndm_flags & NTF_PROXY) {
|
||||
err = pneigh_delete(tbl, net, nla_data(dst_attr), dev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dev == NULL)
|
||||
goto out;
|
||||
|
||||
neigh = neigh_lookup(tbl, nla_data(dst_attr), dev);
|
||||
if (neigh == NULL) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = neigh_update(neigh, NULL, NUD_FAILED,
|
||||
NEIGH_UPDATE_F_OVERRIDE |
|
||||
NEIGH_UPDATE_F_ADMIN);
|
||||
neigh_release(neigh);
|
||||
if (ndm->ndm_flags & NTF_PROXY) {
|
||||
err = pneigh_delete(tbl, net, nla_data(dst_attr), dev);
|
||||
goto out;
|
||||
}
|
||||
read_unlock(&neigh_tbl_lock);
|
||||
err = -EAFNOSUPPORT;
|
||||
|
||||
if (dev == NULL)
|
||||
goto out;
|
||||
|
||||
neigh = neigh_lookup(tbl, nla_data(dst_attr), dev);
|
||||
if (neigh == NULL) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = neigh_update(neigh, NULL, NUD_FAILED,
|
||||
NEIGH_UPDATE_F_OVERRIDE |
|
||||
NEIGH_UPDATE_F_ADMIN);
|
||||
neigh_release(neigh);
|
||||
|
||||
out:
|
||||
return err;
|
||||
|
@ -1692,11 +1672,14 @@ out:
|
|||
|
||||
static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
{
|
||||
int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE;
|
||||
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;
|
||||
struct neighbour *neigh;
|
||||
void *dst, *lladdr;
|
||||
int err;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
@ -1720,70 +1703,60 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|||
goto out;
|
||||
}
|
||||
|
||||
read_lock(&neigh_tbl_lock);
|
||||
for (tbl = neigh_tables; tbl; tbl = tbl->next) {
|
||||
int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE;
|
||||
struct neighbour *neigh;
|
||||
void *dst, *lladdr;
|
||||
tbl = neigh_find_table(ndm->ndm_family);
|
||||
if (tbl == NULL)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
if (tbl->family != ndm->ndm_family)
|
||||
continue;
|
||||
read_unlock(&neigh_tbl_lock);
|
||||
if (nla_len(tb[NDA_DST]) < tbl->key_len)
|
||||
goto out;
|
||||
dst = nla_data(tb[NDA_DST]);
|
||||
lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL;
|
||||
|
||||
if (nla_len(tb[NDA_DST]) < tbl->key_len)
|
||||
goto out;
|
||||
dst = nla_data(tb[NDA_DST]);
|
||||
lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL;
|
||||
if (ndm->ndm_flags & NTF_PROXY) {
|
||||
struct pneigh_entry *pn;
|
||||
|
||||
if (ndm->ndm_flags & NTF_PROXY) {
|
||||
struct pneigh_entry *pn;
|
||||
|
||||
err = -ENOBUFS;
|
||||
pn = pneigh_lookup(tbl, net, dst, dev, 1);
|
||||
if (pn) {
|
||||
pn->flags = ndm->ndm_flags;
|
||||
err = 0;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dev == NULL)
|
||||
goto out;
|
||||
|
||||
neigh = neigh_lookup(tbl, dst, dev);
|
||||
if (neigh == NULL) {
|
||||
if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
neigh = __neigh_lookup_errno(tbl, dst, dev);
|
||||
if (IS_ERR(neigh)) {
|
||||
err = PTR_ERR(neigh);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (nlh->nlmsg_flags & NLM_F_EXCL) {
|
||||
err = -EEXIST;
|
||||
neigh_release(neigh);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(nlh->nlmsg_flags & NLM_F_REPLACE))
|
||||
flags &= ~NEIGH_UPDATE_F_OVERRIDE;
|
||||
}
|
||||
|
||||
if (ndm->ndm_flags & NTF_USE) {
|
||||
neigh_event_send(neigh, NULL);
|
||||
err = -ENOBUFS;
|
||||
pn = pneigh_lookup(tbl, net, dst, dev, 1);
|
||||
if (pn) {
|
||||
pn->flags = ndm->ndm_flags;
|
||||
err = 0;
|
||||
} else
|
||||
err = neigh_update(neigh, lladdr, ndm->ndm_state, flags);
|
||||
neigh_release(neigh);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
read_unlock(&neigh_tbl_lock);
|
||||
err = -EAFNOSUPPORT;
|
||||
if (dev == NULL)
|
||||
goto out;
|
||||
|
||||
neigh = neigh_lookup(tbl, dst, dev);
|
||||
if (neigh == NULL) {
|
||||
if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
neigh = __neigh_lookup_errno(tbl, dst, dev);
|
||||
if (IS_ERR(neigh)) {
|
||||
err = PTR_ERR(neigh);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (nlh->nlmsg_flags & NLM_F_EXCL) {
|
||||
err = -EEXIST;
|
||||
neigh_release(neigh);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(nlh->nlmsg_flags & NLM_F_REPLACE))
|
||||
flags &= ~NEIGH_UPDATE_F_OVERRIDE;
|
||||
}
|
||||
|
||||
if (ndm->ndm_flags & NTF_USE) {
|
||||
neigh_event_send(neigh, NULL);
|
||||
err = 0;
|
||||
} else
|
||||
err = neigh_update(neigh, lladdr, ndm->ndm_state, flags);
|
||||
neigh_release(neigh);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
@ -1982,7 +1955,8 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|||
struct neigh_table *tbl;
|
||||
struct ndtmsg *ndtmsg;
|
||||
struct nlattr *tb[NDTA_MAX+1];
|
||||
int err;
|
||||
bool found = false;
|
||||
int err, tidx;
|
||||
|
||||
err = nlmsg_parse(nlh, sizeof(*ndtmsg), tb, NDTA_MAX,
|
||||
nl_neightbl_policy);
|
||||
|
@ -1995,19 +1969,21 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|||
}
|
||||
|
||||
ndtmsg = nlmsg_data(nlh);
|
||||
read_lock(&neigh_tbl_lock);
|
||||
for (tbl = neigh_tables; tbl; tbl = tbl->next) {
|
||||
|
||||
for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) {
|
||||
tbl = neigh_tables[tidx];
|
||||
if (!tbl)
|
||||
continue;
|
||||
if (ndtmsg->ndtm_family && tbl->family != ndtmsg->ndtm_family)
|
||||
continue;
|
||||
|
||||
if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0)
|
||||
if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tbl == NULL) {
|
||||
err = -ENOENT;
|
||||
goto errout_locked;
|
||||
}
|
||||
if (!found)
|
||||
return -ENOENT;
|
||||
|
||||
/*
|
||||
* We acquire tbl->lock to be nice to the periodic timers and
|
||||
|
@ -2118,8 +2094,6 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|||
|
||||
errout_tbl_lock:
|
||||
write_unlock_bh(&tbl->lock);
|
||||
errout_locked:
|
||||
read_unlock(&neigh_tbl_lock);
|
||||
errout:
|
||||
return err;
|
||||
}
|
||||
|
@ -2134,10 +2108,13 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
|
||||
family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family;
|
||||
|
||||
read_lock(&neigh_tbl_lock);
|
||||
for (tbl = neigh_tables, tidx = 0; tbl; tbl = tbl->next, tidx++) {
|
||||
for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) {
|
||||
struct neigh_parms *p;
|
||||
|
||||
tbl = neigh_tables[tidx];
|
||||
if (!tbl)
|
||||
continue;
|
||||
|
||||
if (tidx < tbl_skip || (family && tbl->family != family))
|
||||
continue;
|
||||
|
||||
|
@ -2168,7 +2145,6 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
neigh_skip = 0;
|
||||
}
|
||||
out:
|
||||
read_unlock(&neigh_tbl_lock);
|
||||
cb->args[0] = tidx;
|
||||
cb->args[1] = nidx;
|
||||
|
||||
|
@ -2351,7 +2327,6 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
int proxy = 0;
|
||||
int err;
|
||||
|
||||
read_lock(&neigh_tbl_lock);
|
||||
family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family;
|
||||
|
||||
/* check for full ndmsg structure presence, family member is
|
||||
|
@ -2363,8 +2338,11 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
|
||||
s_t = cb->args[0];
|
||||
|
||||
for (tbl = neigh_tables, t = 0; tbl;
|
||||
tbl = tbl->next, t++) {
|
||||
for (t = 0; t < NEIGH_NR_TABLES; t++) {
|
||||
tbl = neigh_tables[t];
|
||||
|
||||
if (!tbl)
|
||||
continue;
|
||||
if (t < s_t || (family && tbl->family != family))
|
||||
continue;
|
||||
if (t > s_t)
|
||||
|
@ -2377,7 +2355,6 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
if (err < 0)
|
||||
break;
|
||||
}
|
||||
read_unlock(&neigh_tbl_lock);
|
||||
|
||||
cb->args[0] = t;
|
||||
return skb->len;
|
||||
|
|
|
@ -591,7 +591,7 @@ static const struct file_operations dn_neigh_seq_fops = {
|
|||
|
||||
void __init dn_neigh_init(void)
|
||||
{
|
||||
neigh_table_init(&dn_neigh_table);
|
||||
neigh_table_init(NEIGH_DN_TABLE, &dn_neigh_table);
|
||||
proc_create("decnet_neigh", S_IRUGO, init_net.proc_net,
|
||||
&dn_neigh_seq_fops);
|
||||
}
|
||||
|
@ -599,5 +599,5 @@ void __init dn_neigh_init(void)
|
|||
void __exit dn_neigh_cleanup(void)
|
||||
{
|
||||
remove_proc_entry("decnet_neigh", init_net.proc_net);
|
||||
neigh_table_clear(&dn_neigh_table);
|
||||
neigh_table_clear(NEIGH_DN_TABLE, &dn_neigh_table);
|
||||
}
|
||||
|
|
|
@ -1292,7 +1292,7 @@ static int arp_proc_init(void);
|
|||
|
||||
void __init arp_init(void)
|
||||
{
|
||||
neigh_table_init(&arp_tbl);
|
||||
neigh_table_init(NEIGH_ARP_TABLE, &arp_tbl);
|
||||
|
||||
dev_add_pack(&arp_packet_type);
|
||||
arp_proc_init();
|
||||
|
|
|
@ -1763,7 +1763,7 @@ int __init ndisc_init(void)
|
|||
/*
|
||||
* Initialize the neighbour table
|
||||
*/
|
||||
neigh_table_init(&nd_tbl);
|
||||
neigh_table_init(NEIGH_ND_TABLE, &nd_tbl);
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
err = neigh_sysctl_register(NULL, &nd_tbl.parms,
|
||||
|
@ -1796,6 +1796,6 @@ void ndisc_cleanup(void)
|
|||
#ifdef CONFIG_SYSCTL
|
||||
neigh_sysctl_unregister(&nd_tbl.parms);
|
||||
#endif
|
||||
neigh_table_clear(&nd_tbl);
|
||||
neigh_table_clear(NEIGH_ND_TABLE, &nd_tbl);
|
||||
unregister_pernet_subsys(&ndisc_net_ops);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue