Merge branch 'neighbour-convert-rtm_getneigh-to-rcu-and-make-pneigh-rtnl-free'

Kuniyuki Iwashima says:

====================
neighbour: Convert RTM_GETNEIGH to RCU and make pneigh RTNL-free.

This is kind of v3 of the series below [0] but without NEIGHTBL patches.

Patch 1 ~ 4 and 9 come from the series to convert RTM_GETNEIGH to RCU.

Other patches clean up pneigh_lookup() and convert the pneigh code to
RCU + private mutex so that we can easily remove RTNL from RTM_NEWNEIGH
in the later series.

[0]: https://lore.kernel.org/netdev/20250418012727.57033-1-kuniyu@amazon.com/

v2: https://lore.kernel.org/20250712203515.4099110-1-kuniyu@google.com
v1: https://lore.kernel.org/20250711191007.3591938-1-kuniyu@google.com
====================

Link: https://patch.msgid.link/20250716221221.442239-1-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2025-07-17 16:25:23 -07:00
5 changed files with 210 additions and 213 deletions

View File

@@ -176,10 +176,14 @@ struct neigh_ops {
};
struct pneigh_entry {
struct pneigh_entry *next;
struct pneigh_entry __rcu *next;
possible_net_t net;
struct net_device *dev;
netdevice_tracker dev_tracker;
union {
struct list_head free_node;
struct rcu_head rcu;
};
u32 flags;
u8 protocol;
bool permanent;
@@ -236,7 +240,8 @@ struct neigh_table {
unsigned long last_rand;
struct neigh_statistics __percpu *stats;
struct neigh_hash_table __rcu *nht;
struct pneigh_entry **phash_buckets;
struct mutex phash_lock;
struct pneigh_entry __rcu **phash_buckets;
};
static inline int neigh_parms_family(struct neigh_parms *p)
@@ -376,10 +381,10 @@ unsigned long neigh_rand_reach_time(unsigned long base);
void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
struct sk_buff *skb);
struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl, struct net *net,
const void *key, struct net_device *dev,
int creat);
struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl, struct net *net,
const void *key, struct net_device *dev);
const void *key, struct net_device *dev);
int pneigh_create(struct neigh_table *tbl, struct net *net, const void *key,
struct net_device *dev, u32 flags, u8 protocol,
bool permanent);
int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *key,
struct net_device *dev);

View File

@@ -28,6 +28,7 @@
#include <net/neighbour.h>
#include <net/arp.h>
#include <net/dst.h>
#include <net/ip.h>
#include <net/sock.h>
#include <net/netevent.h>
#include <net/netlink.h>
@@ -53,9 +54,8 @@ static void neigh_timer_handler(struct timer_list *t);
static void __neigh_notify(struct neighbour *n, int type, int flags,
u32 pid);
static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid);
static int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
struct net_device *dev,
bool skip_perm);
static void pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
bool skip_perm);
#ifdef CONFIG_PROC_FS
static const struct seq_operations neigh_stat_seq_ops;
@@ -436,7 +436,9 @@ static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
{
write_lock_bh(&tbl->lock);
neigh_flush_dev(tbl, dev, skip_perm);
pneigh_ifdown_and_unlock(tbl, dev, skip_perm);
write_unlock_bh(&tbl->lock);
pneigh_ifdown(tbl, dev, skip_perm);
pneigh_queue_purge(&tbl->proxy_queue, dev ? dev_net(dev) : NULL,
tbl->family);
if (skb_queue_empty_lockless(&tbl->proxy_queue))
@@ -719,54 +721,53 @@ static u32 pneigh_hash(const void *pkey, unsigned int key_len)
return hash_val;
}
static struct pneigh_entry *__pneigh_lookup_1(struct pneigh_entry *n,
struct net *net,
const void *pkey,
unsigned int key_len,
struct net_device *dev)
struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,
struct net *net, const void *pkey,
struct net_device *dev)
{
struct pneigh_entry *n;
unsigned int key_len;
u32 hash_val;
key_len = tbl->key_len;
hash_val = pneigh_hash(pkey, key_len);
n = rcu_dereference_check(tbl->phash_buckets[hash_val],
lockdep_is_held(&tbl->phash_lock));
while (n) {
if (!memcmp(n->key, pkey, key_len) &&
net_eq(pneigh_net(n), net) &&
(n->dev == dev || !n->dev))
return n;
n = n->next;
n = rcu_dereference_check(n->next, lockdep_is_held(&tbl->phash_lock));
}
return NULL;
}
EXPORT_IPV6_MOD(pneigh_lookup);
struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl,
struct net *net, const void *pkey, struct net_device *dev)
{
unsigned 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)
int pneigh_create(struct neigh_table *tbl, struct net *net,
const void *pkey, struct net_device *dev,
u32 flags, u8 protocol, bool permanent)
{
struct pneigh_entry *n;
unsigned int key_len = tbl->key_len;
u32 hash_val = pneigh_hash(pkey, key_len);
unsigned int key_len;
u32 hash_val;
int err = 0;
read_lock_bh(&tbl->lock);
n = __pneigh_lookup_1(tbl->phash_buckets[hash_val],
net, pkey, key_len, dev);
read_unlock_bh(&tbl->lock);
mutex_lock(&tbl->phash_lock);
if (n || !creat)
goto out;
ASSERT_RTNL();
n = pneigh_lookup(tbl, net, pkey, dev);
if (n)
goto update;
key_len = tbl->key_len;
n = kzalloc(sizeof(*n) + key_len, GFP_KERNEL);
if (!n)
if (!n) {
err = -ENOBUFS;
goto out;
}
write_pnet(&n->net, net);
memcpy(n->key, pkey, key_len);
@@ -776,77 +777,98 @@ struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl,
if (tbl->pconstructor && tbl->pconstructor(n)) {
netdev_put(dev, &n->dev_tracker);
kfree(n);
n = NULL;
err = -ENOBUFS;
goto out;
}
write_lock_bh(&tbl->lock);
hash_val = pneigh_hash(pkey, key_len);
n->next = tbl->phash_buckets[hash_val];
tbl->phash_buckets[hash_val] = n;
write_unlock_bh(&tbl->lock);
rcu_assign_pointer(tbl->phash_buckets[hash_val], n);
update:
WRITE_ONCE(n->flags, flags);
n->permanent = permanent;
WRITE_ONCE(n->protocol, protocol);
out:
return n;
mutex_unlock(&tbl->phash_lock);
return err;
}
EXPORT_SYMBOL(pneigh_lookup);
static void pneigh_destroy(struct rcu_head *rcu)
{
struct pneigh_entry *n = container_of(rcu, struct pneigh_entry, rcu);
netdev_put(n->dev, &n->dev_tracker);
kfree(n);
}
int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
struct net_device *dev)
{
struct pneigh_entry *n, **np;
unsigned int key_len = tbl->key_len;
u32 hash_val = pneigh_hash(pkey, key_len);
struct pneigh_entry *n, __rcu **np;
unsigned int key_len;
u32 hash_val;
write_lock_bh(&tbl->lock);
for (np = &tbl->phash_buckets[hash_val]; (n = *np) != NULL;
key_len = tbl->key_len;
hash_val = pneigh_hash(pkey, key_len);
mutex_lock(&tbl->phash_lock);
for (np = &tbl->phash_buckets[hash_val];
(n = rcu_dereference_protected(*np, 1)) != NULL;
np = &n->next) {
if (!memcmp(n->key, pkey, key_len) && n->dev == dev &&
net_eq(pneigh_net(n), net)) {
*np = n->next;
write_unlock_bh(&tbl->lock);
rcu_assign_pointer(*np, n->next);
mutex_unlock(&tbl->phash_lock);
if (tbl->pdestructor)
tbl->pdestructor(n);
netdev_put(n->dev, &n->dev_tracker);
kfree(n);
call_rcu(&n->rcu, pneigh_destroy);
return 0;
}
}
write_unlock_bh(&tbl->lock);
mutex_unlock(&tbl->phash_lock);
return -ENOENT;
}
static int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
struct net_device *dev,
bool skip_perm)
static void pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
bool skip_perm)
{
struct pneigh_entry *n, **np, *freelist = NULL;
struct pneigh_entry *n, __rcu **np;
LIST_HEAD(head);
u32 h;
mutex_lock(&tbl->phash_lock);
for (h = 0; h <= PNEIGH_HASHMASK; h++) {
np = &tbl->phash_buckets[h];
while ((n = *np) != NULL) {
while ((n = rcu_dereference_protected(*np, 1)) != NULL) {
if (skip_perm && n->permanent)
goto skip;
if (!dev || n->dev == dev) {
*np = n->next;
n->next = freelist;
freelist = n;
rcu_assign_pointer(*np, n->next);
list_add(&n->free_node, &head);
continue;
}
skip:
np = &n->next;
}
}
write_unlock_bh(&tbl->lock);
while ((n = freelist)) {
freelist = n->next;
n->next = NULL;
mutex_unlock(&tbl->phash_lock);
while (!list_empty(&head)) {
n = list_first_entry(&head, typeof(*n), free_node);
list_del(&n->free_node);
if (tbl->pdestructor)
tbl->pdestructor(n);
netdev_put(n->dev, &n->dev_tracker);
kfree(n);
call_rcu(&n->rcu, pneigh_destroy);
}
return -ENOENT;
}
static inline void neigh_parms_put(struct neigh_parms *parms)
@@ -1783,6 +1805,7 @@ void neigh_table_init(int index, struct neigh_table *tbl)
WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN);
rwlock_init(&tbl->lock);
mutex_init(&tbl->phash_lock);
INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work);
queue_delayed_work(system_power_efficient_wq, &tbl->gc_work,
@@ -1999,22 +2022,13 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
if (tb[NDA_PROTOCOL])
protocol = nla_get_u8(tb[NDA_PROTOCOL]);
if (ndm_flags & NTF_PROXY) {
struct pneigh_entry *pn;
if (ndm_flags & (NTF_MANAGED | NTF_EXT_VALIDATED)) {
NL_SET_ERR_MSG(extack, "Invalid NTF_* flag combination");
goto out;
}
err = -ENOBUFS;
pn = pneigh_lookup(tbl, net, dst, dev, 1);
if (pn) {
pn->flags = ndm_flags;
pn->permanent = !!(ndm->ndm_state & NUD_PERMANENT);
if (protocol)
pn->protocol = protocol;
err = 0;
}
err = pneigh_create(tbl, net, dst, dev, ndm_flags, protocol,
!!(ndm->ndm_state & NUD_PERMANENT));
goto out;
}
@@ -2643,13 +2657,15 @@ static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn,
u32 neigh_flags, neigh_flags_ext;
struct nlmsghdr *nlh;
struct ndmsg *ndm;
u8 protocol;
nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags);
if (nlh == NULL)
return -EMSGSIZE;
neigh_flags_ext = pn->flags >> NTF_EXT_SHIFT;
neigh_flags = pn->flags & NTF_OLD_MASK;
neigh_flags = READ_ONCE(pn->flags);
neigh_flags_ext = neigh_flags >> NTF_EXT_SHIFT;
neigh_flags &= NTF_OLD_MASK;
ndm = nlmsg_data(nlh);
ndm->ndm_family = tbl->family;
@@ -2663,7 +2679,8 @@ static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn,
if (nla_put(skb, NDA_DST, tbl->key_len, pn->key))
goto nla_put_failure;
if (pn->protocol && nla_put_u8(skb, NDA_PROTOCOL, pn->protocol))
protocol = READ_ONCE(pn->protocol);
if (protocol && nla_put_u8(skb, NDA_PROTOCOL, protocol))
goto nla_put_failure;
if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext))
goto nla_put_failure;
@@ -2770,12 +2787,12 @@ static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
if (filter->dev_idx || filter->master_idx)
flags |= NLM_F_DUMP_FILTERED;
read_lock_bh(&tbl->lock);
for (h = s_h; h <= PNEIGH_HASHMASK; h++) {
if (h > s_h)
s_idx = 0;
for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) {
for (n = rcu_dereference(tbl->phash_buckets[h]), idx = 0;
n;
n = rcu_dereference(n->next)) {
if (idx < s_idx || pneigh_net(n) != net)
goto next;
if (neigh_ifindex_filtered(n->dev, filter->dev_idx) ||
@@ -2784,16 +2801,13 @@ static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
err = pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
RTM_NEWNEIGH, flags, tbl);
if (err < 0) {
read_unlock_bh(&tbl->lock);
if (err < 0)
goto out;
}
next:
idx++;
}
}
read_unlock_bh(&tbl->lock);
out:
cb->args[3] = h;
cb->args[4] = idx;
@@ -2910,64 +2924,58 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
return err;
}
static int neigh_valid_get_req(const struct nlmsghdr *nlh,
struct neigh_table **tbl,
void **dst, int *dev_idx, u8 *ndm_flags,
struct netlink_ext_ack *extack)
static struct ndmsg *neigh_valid_get_req(const struct nlmsghdr *nlh,
struct nlattr **tb,
struct netlink_ext_ack *extack)
{
struct nlattr *tb[NDA_MAX + 1];
struct ndmsg *ndm;
int err, i;
ndm = nlmsg_payload(nlh, sizeof(*ndm));
if (!ndm) {
NL_SET_ERR_MSG(extack, "Invalid header for neighbor get request");
return -EINVAL;
return ERR_PTR(-EINVAL);
}
if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_state ||
ndm->ndm_type) {
NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor get request");
return -EINVAL;
return ERR_PTR(-EINVAL);
}
if (ndm->ndm_flags & ~NTF_PROXY) {
NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor get request");
return -EINVAL;
return ERR_PTR(-EINVAL);
}
if (!(ndm->ndm_flags & NTF_PROXY) && !ndm->ndm_ifindex) {
NL_SET_ERR_MSG(extack, "No device specified");
return ERR_PTR(-EINVAL);
}
err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), tb,
NDA_MAX, nda_policy, extack);
if (err < 0)
return err;
*ndm_flags = ndm->ndm_flags;
*dev_idx = ndm->ndm_ifindex;
*tbl = neigh_find_table(ndm->ndm_family);
if (*tbl == NULL) {
NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request");
return -EAFNOSUPPORT;
}
return ERR_PTR(err);
for (i = 0; i <= NDA_MAX; ++i) {
if (!tb[i])
continue;
switch (i) {
case NDA_DST:
if (nla_len(tb[i]) != (int)(*tbl)->key_len) {
NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request");
return -EINVAL;
if (!tb[i]) {
NL_SET_ERR_ATTR_MISS(extack, NULL, NDA_DST);
return ERR_PTR(-EINVAL);
}
*dst = nla_data(tb[i]);
break;
default:
if (!tb[i])
continue;
NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor get request");
return -EINVAL;
return ERR_PTR(-EINVAL);
}
}
return 0;
return ndm;
}
static inline size_t neigh_nlmsg_size(void)
@@ -2981,27 +2989,6 @@ static inline size_t neigh_nlmsg_size(void)
+ nla_total_size(1); /* NDA_PROTOCOL */
}
static int neigh_get_reply(struct net *net, struct neighbour *neigh,
u32 pid, u32 seq)
{
struct sk_buff *skb;
int err = 0;
skb = nlmsg_new(neigh_nlmsg_size(), GFP_KERNEL);
if (!skb)
return -ENOBUFS;
err = neigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0);
if (err) {
kfree_skb(skb);
goto errout;
}
err = rtnl_unicast(skb, net, pid);
errout:
return err;
}
static inline size_t pneigh_nlmsg_size(void)
{
return NLMSG_ALIGN(sizeof(struct ndmsg))
@@ -3010,85 +2997,91 @@ static inline size_t pneigh_nlmsg_size(void)
+ nla_total_size(1); /* NDA_PROTOCOL */
}
static int pneigh_get_reply(struct net *net, struct pneigh_entry *neigh,
u32 pid, u32 seq, struct neigh_table *tbl)
{
struct sk_buff *skb;
int err = 0;
skb = nlmsg_new(pneigh_nlmsg_size(), GFP_KERNEL);
if (!skb)
return -ENOBUFS;
err = pneigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0, tbl);
if (err) {
kfree_skb(skb);
goto errout;
}
err = rtnl_unicast(skb, net, pid);
errout:
return err;
}
static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct net *net = sock_net(in_skb->sk);
u32 pid = NETLINK_CB(in_skb).portid;
struct nlattr *tb[NDA_MAX + 1];
struct net_device *dev = NULL;
struct neigh_table *tbl = NULL;
u32 seq = nlh->nlmsg_seq;
struct neigh_table *tbl;
struct neighbour *neigh;
void *dst = NULL;
u8 ndm_flags = 0;
int dev_idx = 0;
struct sk_buff *skb;
struct ndmsg *ndm;
void *dst;
int err;
err = neigh_valid_get_req(nlh, &tbl, &dst, &dev_idx, &ndm_flags,
extack);
if (err < 0)
return err;
ndm = neigh_valid_get_req(nlh, tb, extack);
if (IS_ERR(ndm))
return PTR_ERR(ndm);
if (dev_idx) {
dev = __dev_get_by_index(net, dev_idx);
if (ndm->ndm_flags & NTF_PROXY)
skb = nlmsg_new(neigh_nlmsg_size(), GFP_KERNEL);
else
skb = nlmsg_new(pneigh_nlmsg_size(), GFP_KERNEL);
if (!skb)
return -ENOBUFS;
rcu_read_lock();
tbl = neigh_find_table(ndm->ndm_family);
if (!tbl) {
NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request");
err = -EAFNOSUPPORT;
goto err_unlock;
}
if (nla_len(tb[NDA_DST]) != (int)tbl->key_len) {
NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request");
err = -EINVAL;
goto err_unlock;
}
dst = nla_data(tb[NDA_DST]);
if (ndm->ndm_ifindex) {
dev = dev_get_by_index_rcu(net, ndm->ndm_ifindex);
if (!dev) {
NL_SET_ERR_MSG(extack, "Unknown device ifindex");
return -ENODEV;
err = -ENODEV;
goto err_unlock;
}
}
if (!dst) {
NL_SET_ERR_MSG(extack, "Network address not specified");
return -EINVAL;
}
if (ndm_flags & NTF_PROXY) {
if (ndm->ndm_flags & NTF_PROXY) {
struct pneigh_entry *pn;
pn = pneigh_lookup(tbl, net, dst, dev, 0);
pn = pneigh_lookup(tbl, net, dst, dev);
if (!pn) {
NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found");
return -ENOENT;
err = -ENOENT;
goto err_unlock;
}
return pneigh_get_reply(net, pn, NETLINK_CB(in_skb).portid,
nlh->nlmsg_seq, tbl);
err = pneigh_fill_info(skb, pn, pid, seq, RTM_NEWNEIGH, 0, tbl);
if (err)
goto err_unlock;
} else {
neigh = neigh_lookup(tbl, dst, dev);
if (!neigh) {
NL_SET_ERR_MSG(extack, "Neighbour entry not found");
err = -ENOENT;
goto err_unlock;
}
err = neigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0);
neigh_release(neigh);
if (err)
goto err_unlock;
}
if (!dev) {
NL_SET_ERR_MSG(extack, "No device specified");
return -EINVAL;
}
neigh = neigh_lookup(tbl, dst, dev);
if (!neigh) {
NL_SET_ERR_MSG(extack, "Neighbour entry not found");
return -ENOENT;
}
err = neigh_get_reply(net, neigh, NETLINK_CB(in_skb).portid,
nlh->nlmsg_seq);
neigh_release(neigh);
rcu_read_unlock();
return rtnl_unicast(skb, net, pid);
err_unlock:
rcu_read_unlock();
kfree_skb(skb);
return err;
}
@@ -3295,9 +3288,10 @@ 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];
pn = rcu_dereference(tbl->phash_buckets[bucket]);
while (pn && !net_eq(pneigh_net(pn), net))
pn = pn->next;
pn = rcu_dereference(pn->next);
if (pn)
break;
}
@@ -3315,15 +3309,17 @@ static struct pneigh_entry *pneigh_get_next(struct seq_file *seq,
struct neigh_table *tbl = state->tbl;
do {
pn = pn->next;
pn = rcu_dereference(pn->next);
} while (pn && !net_eq(pneigh_net(pn), net));
while (!pn) {
if (++state->bucket > PNEIGH_HASHMASK)
break;
pn = tbl->phash_buckets[state->bucket];
pn = rcu_dereference(tbl->phash_buckets[state->bucket]);
while (pn && !net_eq(pneigh_net(pn), net))
pn = pn->next;
pn = rcu_dereference(pn->next);
if (pn)
break;
}
@@ -3887,7 +3883,7 @@ static const struct rtnl_msg_handler neigh_rtnl_msg_handlers[] __initconst = {
{.msgtype = RTM_NEWNEIGH, .doit = neigh_add},
{.msgtype = RTM_DELNEIGH, .doit = neigh_delete},
{.msgtype = RTM_GETNEIGH, .doit = neigh_get, .dumpit = neigh_dump_info,
.flags = RTNL_FLAG_DUMP_UNLOCKED},
.flags = RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED},
{.msgtype = RTM_GETNEIGHTBL, .dumpit = neightbl_dump_info},
{.msgtype = RTM_SETNEIGHTBL, .doit = neightbl_set},
};

View File

@@ -864,7 +864,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
(arp_fwd_proxy(in_dev, dev, rt) ||
arp_fwd_pvlan(in_dev, dev, rt, sip, tip) ||
(rt->dst.dev != dev &&
pneigh_lookup(&arp_tbl, net, &tip, dev, 0)))) {
pneigh_lookup(&arp_tbl, net, &tip, dev)))) {
n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
if (n)
neigh_release(n);
@@ -1089,9 +1089,7 @@ static int arp_req_set_public(struct net *net, struct arpreq *r,
if (mask) {
__be32 ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
if (!pneigh_lookup(&arp_tbl, net, &ip, dev, 1))
return -ENOBUFS;
return 0;
return pneigh_create(&arp_tbl, net, &ip, dev, 0, 0, false);
}
return arp_req_set_proxy(net, dev, 1);

View File

@@ -563,7 +563,7 @@ int ip6_forward(struct sk_buff *skb)
/* XXX: idev->cnf.proxy_ndp? */
if (READ_ONCE(net->ipv6.devconf_all->proxy_ndp) &&
pneigh_lookup(&nd_tbl, net, &hdr->daddr, skb->dev, 0)) {
pneigh_lookup(&nd_tbl, net, &hdr->daddr, skb->dev)) {
int proxied = ip6_forward_proxy_check(skb);
if (proxied > 0) {
/* It's tempting to decrease the hop limit

View File

@@ -768,11 +768,9 @@ static int pndisc_is_router(const void *pkey,
struct pneigh_entry *n;
int ret = -1;
read_lock_bh(&nd_tbl.lock);
n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
n = pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
if (n)
ret = !!(n->flags & NTF_ROUTER);
read_unlock_bh(&nd_tbl.lock);
ret = !!(READ_ONCE(n->flags) & NTF_ROUTER);
return ret;
}
@@ -1100,7 +1098,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
READ_ONCE(net->ipv6.devconf_all->forwarding) &&
READ_ONCE(net->ipv6.devconf_all->proxy_ndp) &&
pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
pneigh_lookup(&nd_tbl, net, &msg->target, dev)) {
/* XXX: idev->cnf.proxy_ndp */
goto out;
}