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
{
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);
return n;
}
-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 == n->dev->nd_net)) {
neigh_hold(n);
NEIGH_CACHE_STAT_INC(tbl, hits);
break;
goto out;
}
-struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey,
+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;
for (n = tbl->phash_buckets[hash_val]; n; n = n->next) {
if (!memcmp(n->key, pkey, key_len) &&
+ (n->net == net) &&
(n->dev == dev || !n->dev)) {
read_unlock_bh(&tbl->lock);
goto out;
if (!n)
goto out;
+ n->net = hold_net(net);
memcpy(n->key, pkey, key_len);
n->dev = dev;
if (dev)
if (tbl->pconstructor && tbl->pconstructor(n)) {
if (dev)
dev_put(dev);
+ release_net(net);
kfree(n);
n = NULL;
goto out;
}
-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;
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 &&
+ (n->net == net)) {
*np = n->next;
write_unlock_bh(&tbl->lock);
if (tbl->pdestructor)
tbl->pdestructor(n);
if (n->dev)
dev_put(n->dev);
+ release_net(n->net);
kfree(n);
return 0;
}
tbl->pdestructor(n);
if (n->dev)
dev_put(n->dev);
+ release_net(n->net);
kfree(n);
continue;
}
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;
spin_unlock(&tbl->proxy_queue.lock);
}
+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->net != net)
+ continue;
+ if ((p->dev && p->dev->ifindex == ifindex) ||
+ (!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->nd_net;
+ 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;
+ p->net = hold_net(net);
p->sysctl_table = NULL;
write_lock_bh(&tbl->lock);
p->next = tbl->parms.next;
NEIGH_PRINTK1("neigh_parms_release: not found\n");
}
-void neigh_parms_destroy(struct neigh_parms *parms)
+static void neigh_parms_destroy(struct neigh_parms *parms)
{
+ release_net(parms->net);
kfree(parms);
}
unsigned long now = jiffies;
unsigned long phsize;
+ tbl->parms.net = &init_net;
atomic_set(&tbl->parms.refcnt, 1);
INIT_RCU_HEAD(&tbl->parms.rcu_head);
tbl->parms.reachable_time =
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(tbl->id, 0, init_net.proc_net_stat,
+ &neigh_stat_seq_fops);
if (!tbl->pde)
panic("cannot create neighbour proc dir entry");
- tbl->pde->proc_fops = &neigh_stat_seq_fops;
tbl->pde->data = tbl;
#endif
struct net_device *dev = NULL;
int err = -EINVAL;
- if (net != &init_net)
- return -EINVAL;
-
if (nlmsg_len(nlh) < sizeof(*ndm))
goto out;
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;
}
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;
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;
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 },
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)
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;
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);
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 != p->net)
+ continue;
+
+ if (nidx++ < neigh_skip)
continue;
if (neightbl_fill_param_info(skb, tbl, p,
static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
struct netlink_callback *cb)
{
+ struct net * net = skb->sk->sk_net;
struct neighbour *n;
int rc, h, s_h = cb->args[1];
int idx, s_idx = idx = cb->args[2];
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 (n->dev->nd_net != net)
+ continue;
+ lidx = idx++;
+ if (lidx < s_idx)
continue;
if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq,
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];
static struct neighbour *neigh_get_first(struct seq_file *seq)
{
struct neigh_seq_state *state = seq->private;
+ struct net *net = state->p.net;
struct neigh_table *tbl = state->tbl;
struct neighbour *n = NULL;
int bucket = state->bucket;
n = tbl->hash_buckets[bucket];
while (n) {
+ if (n->dev->nd_net != net)
+ goto next;
if (state->neigh_sub_iter) {
loff_t fakep = 0;
void *v;
loff_t *pos)
{
struct neigh_seq_state *state = seq->private;
+ struct net *net = state->p.net;
struct neigh_table *tbl = state->tbl;
if (state->neigh_sub_iter) {
while (1) {
while (n) {
+ if (n->dev->nd_net != net)
+ goto next;
if (state->neigh_sub_iter) {
void *v = state->neigh_sub_iter(state, n, pos);
if (v)
static struct pneigh_entry *pneigh_get_first(struct seq_file *seq)
{
struct neigh_seq_state *state = seq->private;
+ struct net * net = state->p.net;
struct neigh_table *tbl = state->tbl;
struct pneigh_entry *pn = NULL;
int bucket = state->bucket;
state->flags |= NEIGH_SEQ_IS_PNEIGH;
for (bucket = 0; bucket <= PNEIGH_HASHMASK; bucket++) {
pn = tbl->phash_buckets[bucket];
+ while (pn && (pn->net != net))
+ pn = pn->next;
if (pn)
break;
}
loff_t *pos)
{
struct neigh_seq_state *state = seq->private;
+ struct net * net = state->p.net;
struct neigh_table *tbl = state->tbl;
pn = pn->next;
if (++state->bucket > PNEIGH_HASHMASK)
break;
pn = tbl->phash_buckets[state->bucket];
+ while (pn && (pn->net != net))
+ pn = pn->next;
if (pn)
break;
}
}
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;
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;
static void __neigh_notify(struct neighbour *n, int type, int flags)
{
+ struct net *net = n->dev->nd_net;
struct sk_buff *skb;
int err = -ENOBUFS;
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