net, neigh: Fix NTF_EXT_LEARNED in combination with NTF_USE
authorDaniel Borkmann <daniel@iogearbox.net>
Mon, 11 Oct 2021 12:12:35 +0000 (14:12 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 12 Oct 2021 10:27:47 +0000 (11:27 +0100)
The NTF_EXT_LEARNED neigh flag is usually propagated back to user space
upon dump of the neighbor table. However, when used in combination with
NTF_USE flag this is not the case despite exempting the entry from the
garbage collector. This results in inconsistent state since entries are
typically marked in neigh->flags with NTF_EXT_LEARNED, but here they are
not. Fix it by propagating the creation flag to ___neigh_create().

Before fix:

  # ./ip/ip n replace 192.168.178.30 dev enp5s0 use extern_learn
  # ./ip/ip n
  192.168.178.30 dev enp5s0 lladdr f4:8c:50:5e:71:9a REACHABLE
  [...]

After fix:

  # ./ip/ip n replace 192.168.178.30 dev enp5s0 use extern_learn
  # ./ip/ip n
  192.168.178.30 dev enp5s0 lladdr f4:8c:50:5e:71:9a extern_learn REACHABLE
  [...]

Fixes: 9ce33e46531d ("neighbour: support for NTF_EXT_LEARNED flag")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Roopa Prabhu <roopa@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/core/neighbour.c

index 2d5bc3a75faeca8eef211e4569fa84501a6f82eb..8457d5f97022bfbc74a642e3851ecea0f9a450d2 100644 (file)
@@ -379,7 +379,7 @@ EXPORT_SYMBOL(neigh_ifdown);
 
 static struct neighbour *neigh_alloc(struct neigh_table *tbl,
                                     struct net_device *dev,
-                                    bool exempt_from_gc)
+                                    u8 flags, bool exempt_from_gc)
 {
        struct neighbour *n = NULL;
        unsigned long now = jiffies;
@@ -412,6 +412,7 @@ do_alloc:
        n->updated        = n->used = now;
        n->nud_state      = NUD_NONE;
        n->output         = neigh_blackhole;
+       n->flags          = flags;
        seqlock_init(&n->hh.hh_lock);
        n->parms          = neigh_parms_clone(&tbl->parms);
        timer_setup(&n->timer, neigh_timer_handler, 0);
@@ -575,19 +576,18 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
 }
 EXPORT_SYMBOL(neigh_lookup_nodev);
 
-static struct neighbour *___neigh_create(struct neigh_table *tbl,
-                                        const void *pkey,
-                                        struct net_device *dev,
-                                        bool exempt_from_gc, bool want_ref)
+static struct neighbour *
+___neigh_create(struct neigh_table *tbl, const void *pkey,
+               struct net_device *dev, u8 flags,
+               bool exempt_from_gc, bool want_ref)
 {
-       struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev, exempt_from_gc);
-       u32 hash_val;
-       unsigned int key_len = tbl->key_len;
-       int error;
+       u32 hash_val, key_len = tbl->key_len;
+       struct neighbour *n1, *rc, *n;
        struct neigh_hash_table *nht;
+       int error;
 
+       n = neigh_alloc(tbl, dev, flags, exempt_from_gc);
        trace_neigh_create(tbl, dev, pkey, n, exempt_from_gc);
-
        if (!n) {
                rc = ERR_PTR(-ENOBUFS);
                goto out;
@@ -674,7 +674,7 @@ out_neigh_release:
 struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
                                 struct net_device *dev, bool want_ref)
 {
-       return ___neigh_create(tbl, pkey, dev, false, want_ref);
+       return ___neigh_create(tbl, pkey, dev, 0, false, want_ref);
 }
 EXPORT_SYMBOL(__neigh_create);
 
@@ -1942,7 +1942,9 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
 
                exempt_from_gc = ndm->ndm_state & NUD_PERMANENT ||
                                 ndm->ndm_flags & NTF_EXT_LEARNED;
-               neigh = ___neigh_create(tbl, dst, dev, exempt_from_gc, true);
+               neigh = ___neigh_create(tbl, dst, dev,
+                                       ndm->ndm_flags & NTF_EXT_LEARNED,
+                                       exempt_from_gc, true);
                if (IS_ERR(neigh)) {
                        err = PTR_ERR(neigh);
                        goto out;