linux-loongson/Documentation/netlink/specs/rt-neigh.yaml
Ido Schimmel 03dc03fa04 neighbor: Add NTF_EXT_VALIDATED flag for externally validated entries
tl;dr
=====

Add a new neighbor flag ("extern_valid") that can be used to indicate to
the kernel that a neighbor entry was learned and determined to be valid
externally. The kernel will not try to remove or invalidate such an
entry, leaving these decisions to the user space control plane. This is
needed for EVPN multi-homing where a neighbor entry for a multi-homed
host needs to be synced across all the VTEPs among which the host is
multi-homed.

Background
==========

In a typical EVPN multi-homing setup each host is multi-homed using a
set of links called ES (Ethernet Segment, i.e., LAG) to multiple leaf
switches (VTEPs). VTEPs that are connected to the same ES are called ES
peers.

When a neighbor entry is learned on a VTEP, it is distributed to both ES
peers and remote VTEPs using EVPN MAC/IP advertisement routes. ES peers
use the neighbor entry when routing traffic towards the multi-homed host
and remote VTEPs use it for ARP/NS suppression.

Motivation
==========

If the ES link between a host and the VTEP on which the neighbor entry
was locally learned goes down, the EVPN MAC/IP advertisement route will
be withdrawn and the neighbor entries will be removed from both ES peers
and remote VTEPs. Routing towards the multi-homed host and ARP/NS
suppression can fail until another ES peer locally learns the neighbor
entry and distributes it via an EVPN MAC/IP advertisement route.

"draft-rbickhart-evpn-ip-mac-proxy-adv-03" [1] suggests avoiding these
intermittent failures by having the ES peers install the neighbor
entries as before, but also injecting EVPN MAC/IP advertisement routes
with a proxy indication. When the previously mentioned ES link goes down
and the original EVPN MAC/IP advertisement route is withdrawn, the ES
peers will not withdraw their neighbor entries, but instead start aging
timers for the proxy indication.

If an ES peer locally learns the neighbor entry (i.e., it becomes
"reachable"), it will restart its aging timer for the entry and emit an
EVPN MAC/IP advertisement route without a proxy indication. An ES peer
will stop its aging timer for the proxy indication if it observes the
removal of the proxy indication from at least one of the ES peers
advertising the entry.

In the event that the aging timer for the proxy indication expired, an
ES peer will withdraw its EVPN MAC/IP advertisement route. If the timer
expired on all ES peers and they all withdrew their proxy
advertisements, the neighbor entry will be completely removed from the
EVPN fabric.

Implementation
==============

In the above scheme, when the control plane (e.g., FRR) advertises a
neighbor entry with a proxy indication, it expects the corresponding
entry in the data plane (i.e., the kernel) to remain valid and not be
removed due to garbage collection or loss of carrier. The control plane
also expects the kernel to notify it if the entry was learned locally
(i.e., became "reachable") so that it will remove the proxy indication
from the EVPN MAC/IP advertisement route. That is why these entries
cannot be programmed with dummy states such as "permanent" or "noarp".

Instead, add a new neighbor flag ("extern_valid") which indicates that
the entry was learned and determined to be valid externally and should
not be removed or invalidated by the kernel. The kernel can probe the
entry and notify user space when it becomes "reachable" (it is initially
installed as "stale"). However, if the kernel does not receive a
confirmation, have it return the entry to the "stale" state instead of
the "failed" state.

In other words, an entry marked with the "extern_valid" flag behaves
like any other dynamically learned entry other than the fact that the
kernel cannot remove or invalidate it.

One can argue that the "extern_valid" flag should not prevent garbage
collection and that instead a neighbor entry should be programmed with
both the "extern_valid" and "extern_learn" flags. There are two reasons
for not doing that:

1. Unclear why a control plane would like to program an entry that the
   kernel cannot invalidate but can completely remove.

2. The "extern_learn" flag is used by FRR for neighbor entries learned
   on remote VTEPs (for ARP/NS suppression) whereas here we are
   concerned with local entries. This distinction is currently irrelevant
   for the kernel, but might be relevant in the future.

Given that the flag only makes sense when the neighbor has a valid
state, reject attempts to add a neighbor with an invalid state and with
this flag set. For example:

 # ip neigh add 192.0.2.1 nud none dev br0.10 extern_valid
 Error: Cannot create externally validated neighbor with an invalid state.
 # ip neigh add 192.0.2.1 lladdr 00:11:22:33:44:55 nud stale dev br0.10 extern_valid
 # ip neigh replace 192.0.2.1 nud failed dev br0.10 extern_valid
 Error: Cannot mark neighbor as externally validated with an invalid state.

The above means that a neighbor cannot be created with the
"extern_valid" flag and flags such as "use" or "managed" as they result
in a neighbor being created with an invalid state ("none") and
immediately getting probed:

 # ip neigh add 192.0.2.1 lladdr 00:11:22:33:44:55 nud stale dev br0.10 extern_valid use
 Error: Cannot create externally validated neighbor with an invalid state.

However, these flags can be used together with "extern_valid" after the
neighbor was created with a valid state:

 # ip neigh add 192.0.2.1 lladdr 00:11:22:33:44:55 nud stale dev br0.10 extern_valid
 # ip neigh replace 192.0.2.1 lladdr 00:11:22:33:44:55 nud stale dev br0.10 extern_valid use

One consequence of preventing the kernel from invalidating a neighbor
entry is that by default it will only try to determine reachability
using unicast probes. This can be changed using the "mcast_resolicit"
sysctl:

 # sysctl net.ipv4.neigh.br0/10.mcast_resolicit
 0
 # tcpdump -nn -e -i br0.10 -Q out arp &
 # ip neigh replace 192.0.2.1 lladdr 00:11:22:33:44:55 nud stale dev br0.10 extern_valid use
 62:50:1d:11:93:6f > 00:11:22:33:44:55, ethertype ARP (0x0806), length 42: Request who-has 192.0.2.1 tell 192.0.2.2, length 28
 62:50:1d:11:93:6f > 00:11:22:33:44:55, ethertype ARP (0x0806), length 42: Request who-has 192.0.2.1 tell 192.0.2.2, length 28
 62:50:1d:11:93:6f > 00:11:22:33:44:55, ethertype ARP (0x0806), length 42: Request who-has 192.0.2.1 tell 192.0.2.2, length 28
 # sysctl -wq net.ipv4.neigh.br0/10.mcast_resolicit=3
 # ip neigh replace 192.0.2.1 lladdr 00:11:22:33:44:55 nud stale dev br0.10 extern_valid use
 62:50:1d:11:93:6f > 00:11:22:33:44:55, ethertype ARP (0x0806), length 42: Request who-has 192.0.2.1 tell 192.0.2.2, length 28
 62:50:1d:11:93:6f > 00:11:22:33:44:55, ethertype ARP (0x0806), length 42: Request who-has 192.0.2.1 tell 192.0.2.2, length 28
 62:50:1d:11:93:6f > 00:11:22:33:44:55, ethertype ARP (0x0806), length 42: Request who-has 192.0.2.1 tell 192.0.2.2, length 28
 62:50:1d:11:93:6f > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.0.2.1 tell 192.0.2.2, length 28
 62:50:1d:11:93:6f > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.0.2.1 tell 192.0.2.2, length 28
 62:50:1d:11:93:6f > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.0.2.1 tell 192.0.2.2, length 28

iproute2 patches can be found here [2].

[1] https://datatracker.ietf.org/doc/html/draft-rbickhart-evpn-ip-mac-proxy-adv-03
[2] https://github.com/idosch/iproute2/tree/submit/extern_valid_v1

Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://patch.msgid.link/20250626073111.244534-2-idosch@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-06-30 18:14:23 -07:00

454 lines
7.8 KiB
YAML

# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
---
name: rt-neigh
protocol: netlink-raw
uapi-header: linux/rtnetlink.h
protonum: 0
doc:
IP neighbour management over rtnetlink.
definitions:
-
name: ndmsg
type: struct
members:
-
name: ndm-family
type: u8
-
name: ndm-pad
type: pad
len: 3
-
name: ndm-ifindex
type: s32
-
name: ndm-state
type: u16
enum: nud-state
-
name: ndm-flags
type: u8
enum: ntf-flags
-
name: ndm-type
type: u8
enum: rtm-type
-
name: ndtmsg
type: struct
members:
-
name: family
type: u8
-
name: pad
type: pad
len: 3
-
name: nud-state
type: flags
enum-name:
entries:
- incomplete
- reachable
- stale
- delay
- probe
- failed
- noarp
- permanent
-
name: ntf-flags
type: flags
enum-name:
entries:
- use
- self
- master
- proxy
- ext-learned
- offloaded
- sticky
- router
-
name: ntf-ext-flags
type: flags
enum-name:
entries:
- managed
- locked
- ext-validated
-
name: rtm-type
type: enum
enum-name:
entries:
- unspec
- unicast
- local
- broadcast
- anycast
- multicast
- blackhole
- unreachable
- prohibit
- throw
- nat
- xresolve
-
name: nda-cacheinfo
type: struct
members:
-
name: confirmed
type: u32
-
name: used
type: u32
-
name: updated
type: u32
-
name: refcnt
type: u32
-
name: ndt-config
type: struct
members:
-
name: key-len
type: u16
-
name: entry-size
type: u16
-
name: entries
type: u32
-
name: last-flush
type: u32
-
name: last-rand
type: u32
-
name: hash-rnd
type: u32
-
name: hash-mask
type: u32
-
name: hash-chain-gc
type: u32
-
name: proxy-qlen
type: u32
-
name: ndt-stats
type: struct
members:
-
name: allocs
type: u64
-
name: destroys
type: u64
-
name: hash-grows
type: u64
-
name: res-failed
type: u64
-
name: lookups
type: u64
-
name: hits
type: u64
-
name: rcv-probes-mcast
type: u64
-
name: rcv-probes-ucast
type: u64
-
name: periodic-gc-runs
type: u64
-
name: forced-gc-runs
type: u64
-
name: table-fulls
type: u64
attribute-sets:
-
name: neighbour-attrs
name-prefix: nda-
attributes:
-
name: unspec
type: binary
value: 0
-
name: dst
type: binary
display-hint: ipv4
-
name: lladdr
type: binary
display-hint: mac
-
name: cacheinfo
type: binary
struct: nda-cacheinfo
-
name: probes
type: u32
-
name: vlan
type: u16
-
name: port
type: u16
-
name: vni
type: u32
-
name: ifindex
type: u32
-
name: master
type: u32
-
name: link-netnsid
type: s32
-
name: src-vni
type: u32
-
name: protocol
type: u8
-
name: nh-id
type: u32
-
name: fdb-ext-attrs
type: binary
-
name: flags-ext
type: u32
enum: ntf-ext-flags
-
name: ndm-state-mask
type: u16
-
name: ndm-flags-mask
type: u8
-
name: ndt-attrs
name-prefix: ndta-
attributes:
-
name: name
type: string
-
name: thresh1
type: u32
-
name: thresh2
type: u32
-
name: thresh3
type: u32
-
name: config
type: binary
struct: ndt-config
-
name: parms
type: nest
nested-attributes: ndtpa-attrs
-
name: stats
type: binary
struct: ndt-stats
-
name: gc-interval
type: u64
-
name: pad
type: pad
-
name: ndtpa-attrs
name-prefix: ndtpa-
attributes:
-
name: ifindex
type: u32
-
name: refcnt
type: u32
-
name: reachable-time
type: u64
-
name: base-reachable-time
type: u64
-
name: retrans-time
type: u64
-
name: gc-staletime
type: u64
-
name: delay-probe-time
type: u64
-
name: queue-len
type: u32
-
name: app-probes
type: u32
-
name: ucast-probes
type: u32
-
name: mcast-probes
type: u32
-
name: anycast-delay
type: u64
-
name: proxy-delay
type: u64
-
name: proxy-qlen
type: u32
-
name: locktime
type: u64
-
name: queue-lenbytes
type: u32
-
name: mcast-reprobes
type: u32
-
name: pad
type: pad
-
name: interval-probe-time-ms
type: u64
operations:
enum-model: directional
name-prefix: rtm-
list:
-
name: newneigh
doc: Add new neighbour entry
fixed-header: ndmsg
attribute-set: neighbour-attrs
do:
request:
value: 28
attributes: &neighbour-all
- dst
- lladdr
- probes
- vlan
- port
- vni
- ifindex
- master
- protocol
- nh-id
- flags-ext
- fdb-ext-attrs
-
name: delneigh
doc: Remove an existing neighbour entry
fixed-header: ndmsg
attribute-set: neighbour-attrs
do:
request:
value: 29
attributes:
- dst
- ifindex
-
name: delneigh-ntf
doc: Notify a neighbour deletion
value: 29
notify: getneigh
fixed-header: ndmsg
-
name: getneigh
doc: Get or dump neighbour entries
fixed-header: ndmsg
attribute-set: neighbour-attrs
do:
request:
value: 30
attributes:
- dst
reply:
value: 28
attributes: *neighbour-all
dump:
request:
attributes:
- ifindex
- master
reply:
value: 28
attributes: *neighbour-all
-
name: newneigh-ntf
doc: Notify a neighbour creation
value: 28
notify: getneigh
fixed-header: ndmsg
-
name: getneightbl
doc: Get or dump neighbour tables
fixed-header: ndtmsg
attribute-set: ndt-attrs
dump:
request:
value: 66
reply:
value: 64
attributes:
- name
- thresh1
- thresh2
- thresh3
- config
- parms
- stats
- gc-interval
-
name: setneightbl
doc: Set neighbour tables
fixed-header: ndtmsg
attribute-set: ndt-attrs
do:
request:
value: 67
attributes:
- name
- thresh1
- thresh2
- thresh3
- parms
- gc-interval
mcast-groups:
list:
-
name: rtnlgrp-neigh
value: 3