mirror of
https://github.com/torvalds/linux.git
synced 2026-05-29 17:43:52 +02:00
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:
commit
25bf7d7f45
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user