Skip to content

Commit b28fe7f

Browse files
committed
Merge branch 'add-support-for-externally-validated-neighbor-entries'
Ido Schimmel says: ==================== Add support for externally validated neighbor entries Patch kernel-patches#1 adds a new neighbor flag ("extern_valid") that prevents the kernel from invalidating or removing a neighbor entry, while allowing the kernel to notify user space when the entry becomes reachable. See motivation and implementation details in the commit message. Patch kernel-patches#2 adds a selftest. v1: https://lore.kernel.org/20250611141551.462569-1-idosch@nvidia.com ==================== Link: https://patch.msgid.link/20250626073111.244534-1-idosch@nvidia.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2 parents af232e7 + 171f2ee commit b28fe7f

File tree

6 files changed

+445
-11
lines changed

6 files changed

+445
-11
lines changed

Documentation/netlink/specs/rt-neigh.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ definitions:
7979
entries:
8080
- managed
8181
- locked
82+
- ext-validated
8283
-
8384
name: rtm-type
8485
type: enum

include/net/neighbour.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,13 +261,15 @@ static inline void *neighbour_priv(const struct neighbour *n)
261261
#define NEIGH_UPDATE_F_EXT_LEARNED BIT(5)
262262
#define NEIGH_UPDATE_F_ISROUTER BIT(6)
263263
#define NEIGH_UPDATE_F_ADMIN BIT(7)
264+
#define NEIGH_UPDATE_F_EXT_VALIDATED BIT(8)
264265

265266
/* In-kernel representation for NDA_FLAGS_EXT flags: */
266267
#define NTF_OLD_MASK 0xff
267268
#define NTF_EXT_SHIFT 8
268-
#define NTF_EXT_MASK (NTF_EXT_MANAGED)
269+
#define NTF_EXT_MASK (NTF_EXT_MANAGED | NTF_EXT_EXT_VALIDATED)
269270

270271
#define NTF_MANAGED (NTF_EXT_MANAGED << NTF_EXT_SHIFT)
272+
#define NTF_EXT_VALIDATED (NTF_EXT_EXT_VALIDATED << NTF_EXT_SHIFT)
271273

272274
extern const struct nla_policy nda_policy[];
273275

include/uapi/linux/neighbour.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ enum {
5454
/* Extended flags under NDA_FLAGS_EXT: */
5555
#define NTF_EXT_MANAGED (1 << 0)
5656
#define NTF_EXT_LOCKED (1 << 1)
57+
#define NTF_EXT_EXT_VALIDATED (1 << 2)
5758

5859
/*
5960
* Neighbor Cache Entry States.
@@ -92,6 +93,10 @@ enum {
9293
* bridge in response to a host trying to communicate via a locked bridge port
9394
* with MAB enabled. Their purpose is to notify user space that a host requires
9495
* authentication.
96+
*
97+
* NTF_EXT_EXT_VALIDATED flagged neighbor entries were externally validated by
98+
* a user space control plane. The kernel will not remove or invalidate them,
99+
* but it can probe them and notify user space when they become reachable.
95100
*/
96101

97102
struct nda_cacheinfo {

net/core/neighbour.c

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,12 @@ static void neigh_update_gc_list(struct neighbour *n)
154154
if (n->dead)
155155
goto out;
156156

157-
/* remove from the gc list if new state is permanent or if neighbor
158-
* is externally learned; otherwise entry should be on the gc list
157+
/* remove from the gc list if new state is permanent or if neighbor is
158+
* externally learned / validated; otherwise entry should be on the gc
159+
* list
159160
*/
160161
exempt_from_gc = n->nud_state & NUD_PERMANENT ||
161-
n->flags & NTF_EXT_LEARNED;
162+
n->flags & (NTF_EXT_LEARNED | NTF_EXT_VALIDATED);
162163
on_gc_list = !list_empty(&n->gc_list);
163164

164165
if (exempt_from_gc && on_gc_list) {
@@ -205,6 +206,7 @@ static void neigh_update_flags(struct neighbour *neigh, u32 flags, int *notify,
205206

206207
ndm_flags = (flags & NEIGH_UPDATE_F_EXT_LEARNED) ? NTF_EXT_LEARNED : 0;
207208
ndm_flags |= (flags & NEIGH_UPDATE_F_MANAGED) ? NTF_MANAGED : 0;
209+
ndm_flags |= (flags & NEIGH_UPDATE_F_EXT_VALIDATED) ? NTF_EXT_VALIDATED : 0;
208210

209211
if ((old_flags ^ ndm_flags) & NTF_EXT_LEARNED) {
210212
if (ndm_flags & NTF_EXT_LEARNED)
@@ -222,6 +224,14 @@ static void neigh_update_flags(struct neighbour *neigh, u32 flags, int *notify,
222224
*notify = 1;
223225
*managed_update = true;
224226
}
227+
if ((old_flags ^ ndm_flags) & NTF_EXT_VALIDATED) {
228+
if (ndm_flags & NTF_EXT_VALIDATED)
229+
neigh->flags |= NTF_EXT_VALIDATED;
230+
else
231+
neigh->flags &= ~NTF_EXT_VALIDATED;
232+
*notify = 1;
233+
*gc_update = true;
234+
}
225235
}
226236

227237
bool neigh_remove_one(struct neighbour *n)
@@ -379,7 +389,9 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev,
379389
dev_head = neigh_get_dev_table(dev, tbl->family);
380390

381391
hlist_for_each_entry_safe(n, tmp, dev_head, dev_list) {
382-
if (skip_perm && n->nud_state & NUD_PERMANENT)
392+
if (skip_perm &&
393+
(n->nud_state & NUD_PERMANENT ||
394+
n->flags & NTF_EXT_VALIDATED))
383395
continue;
384396

385397
hlist_del_rcu(&n->hash);
@@ -942,7 +954,8 @@ static void neigh_periodic_work(struct work_struct *work)
942954

943955
state = n->nud_state;
944956
if ((state & (NUD_PERMANENT | NUD_IN_TIMER)) ||
945-
(n->flags & NTF_EXT_LEARNED)) {
957+
(n->flags &
958+
(NTF_EXT_LEARNED | NTF_EXT_VALIDATED))) {
946959
write_unlock(&n->lock);
947960
continue;
948961
}
@@ -1095,9 +1108,15 @@ static void neigh_timer_handler(struct timer_list *t)
10951108

10961109
if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) &&
10971110
atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) {
1098-
WRITE_ONCE(neigh->nud_state, NUD_FAILED);
1111+
if (neigh->nud_state == NUD_PROBE &&
1112+
neigh->flags & NTF_EXT_VALIDATED) {
1113+
WRITE_ONCE(neigh->nud_state, NUD_STALE);
1114+
neigh->updated = jiffies;
1115+
} else {
1116+
WRITE_ONCE(neigh->nud_state, NUD_FAILED);
1117+
neigh_invalidate(neigh);
1118+
}
10991119
notify = 1;
1100-
neigh_invalidate(neigh);
11011120
goto out;
11021121
}
11031122

@@ -1245,6 +1264,8 @@ static void neigh_update_hhs(struct neighbour *neigh)
12451264
NTF_ROUTER flag.
12461265
NEIGH_UPDATE_F_ISROUTER indicates if the neighbour is known as
12471266
a router.
1267+
NEIGH_UPDATE_F_EXT_VALIDATED means that the entry will not be removed
1268+
or invalidated.
12481269
12491270
Caller MUST hold reference count on the entry.
12501271
*/
@@ -1979,7 +2000,7 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
19792000
if (ndm_flags & NTF_PROXY) {
19802001
struct pneigh_entry *pn;
19812002

1982-
if (ndm_flags & NTF_MANAGED) {
2003+
if (ndm_flags & (NTF_MANAGED | NTF_EXT_VALIDATED)) {
19832004
NL_SET_ERR_MSG(extack, "Invalid NTF_* flag combination");
19842005
goto out;
19852006
}
@@ -2010,7 +2031,8 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
20102031
if (neigh == NULL) {
20112032
bool ndm_permanent = ndm->ndm_state & NUD_PERMANENT;
20122033
bool exempt_from_gc = ndm_permanent ||
2013-
ndm_flags & NTF_EXT_LEARNED;
2034+
ndm_flags & (NTF_EXT_LEARNED |
2035+
NTF_EXT_VALIDATED);
20142036

20152037
if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
20162038
err = -ENOENT;
@@ -2021,10 +2043,27 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
20212043
err = -EINVAL;
20222044
goto out;
20232045
}
2046+
if (ndm_flags & NTF_EXT_VALIDATED) {
2047+
u8 state = ndm->ndm_state;
2048+
2049+
/* NTF_USE and NTF_MANAGED will result in the neighbor
2050+
* being created with an invalid state (NUD_NONE).
2051+
*/
2052+
if (ndm_flags & (NTF_USE | NTF_MANAGED))
2053+
state = NUD_NONE;
2054+
2055+
if (!(state & NUD_VALID)) {
2056+
NL_SET_ERR_MSG(extack,
2057+
"Cannot create externally validated neighbor with an invalid state");
2058+
err = -EINVAL;
2059+
goto out;
2060+
}
2061+
}
20242062

20252063
neigh = ___neigh_create(tbl, dst, dev,
20262064
ndm_flags &
2027-
(NTF_EXT_LEARNED | NTF_MANAGED),
2065+
(NTF_EXT_LEARNED | NTF_MANAGED |
2066+
NTF_EXT_VALIDATED),
20282067
exempt_from_gc, true);
20292068
if (IS_ERR(neigh)) {
20302069
err = PTR_ERR(neigh);
@@ -2036,6 +2075,24 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
20362075
neigh_release(neigh);
20372076
goto out;
20382077
}
2078+
if (ndm_flags & NTF_EXT_VALIDATED) {
2079+
u8 state = ndm->ndm_state;
2080+
2081+
/* NTF_USE and NTF_MANAGED do not update the existing
2082+
* state other than clearing it if it was
2083+
* NUD_PERMANENT.
2084+
*/
2085+
if (ndm_flags & (NTF_USE | NTF_MANAGED))
2086+
state = READ_ONCE(neigh->nud_state) & ~NUD_PERMANENT;
2087+
2088+
if (!(state & NUD_VALID)) {
2089+
NL_SET_ERR_MSG(extack,
2090+
"Cannot mark neighbor as externally validated with an invalid state");
2091+
err = -EINVAL;
2092+
neigh_release(neigh);
2093+
goto out;
2094+
}
2095+
}
20392096

20402097
if (!(nlh->nlmsg_flags & NLM_F_REPLACE))
20412098
flags &= ~(NEIGH_UPDATE_F_OVERRIDE |
@@ -2052,6 +2109,8 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
20522109
flags |= NEIGH_UPDATE_F_MANAGED;
20532110
if (ndm_flags & NTF_USE)
20542111
flags |= NEIGH_UPDATE_F_USE;
2112+
if (ndm_flags & NTF_EXT_VALIDATED)
2113+
flags |= NEIGH_UPDATE_F_EXT_VALIDATED;
20552114

20562115
err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags,
20572116
NETLINK_CB(skb).portid, extack);

tools/testing/selftests/net/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ TEST_PROGS += test_vxlan_mdb.sh
100100
TEST_PROGS += test_bridge_neigh_suppress.sh
101101
TEST_PROGS += test_vxlan_nolocalbypass.sh
102102
TEST_PROGS += test_bridge_backup_port.sh
103+
TEST_PROGS += test_neigh.sh
103104
TEST_PROGS += fdb_flush.sh fdb_notify.sh
104105
TEST_PROGS += fq_band_pktlimit.sh
105106
TEST_PROGS += vlan_hw_filter.sh

0 commit comments

Comments
 (0)