ipv6: update NUD_FAILED neighbors from NA messages
Subsystems (5)
v4 cover ipv6: update NUD_FAILED neighbors from NA messages
Neighbor Advertisements for existing NUD_FAILED entries are currently ignored. This can leave a first-hop router with an unusable neighbor after the host becomes reachable and announces itself, particularly when forwarding is offloaded away from the kernel and traffic destined to the FAILED neighbor is unable to trigger the kernel's normal neighbor resolution process.
Allowing neighbor entries to be updated from NAs already received by the kernel is preferable since it creates less work compared to periodically probing FAILED neighbors (e.g. using NTF_EXT_MANAGED).
Extend accept_untracked_na to treat FAILED entries like absent entries. Require a target link-layer address, IPv6 forwarding, and acceptance by the sysctl, including its source-prefix check in mode 2. Recover the entry only to STALE, including for solicited NAs, to maintain parity with handling of non-existent neighbor cache entries.
Patch #1 implements the behavior and updates RFC 9131-related comments and accept_untracked_na documentation.
Patch #2 extends ndisc_unsolicited_na_test.sh to cover FAILED-entry recovery and each acceptance gate, including in-prefix and out-of-prefix mode 2.
Thanks to Ido Schimmel for feedback on the original RFC.
---
v4:
- Add Ido Schimmel's Reviewed-by tag to patch #1.
- Add a Fixes tag and document the selftest return-code fix in patch #2.
v3:
- Cite RFC 4861 section 7.3.3 when describing FAILED entries.
- Simplify the FAILED-entry path as suggested by Ido Schimmel.
- Drop the NDISC callback and 6LoWPAN changes. The existing 6LoWPAN override-only behavior also applies to INCOMPLETE entries and is left unchanged.
- Restore the existing rt6_clean_tohost() source-address behavior. The source-versus-target behavior applies to all neighbor states and is left unchanged.
- Verify the link-layer address learned during recovery in selftests.
- Keep the NA source, target, and neighbor key fixed while varying the router prefix in the mode 2 selftest cases.
- Arm packet capture in selftest before starting host DAD and wait for capture readiness and receive completion.
https://lore.kernel.org/r/[email protected]
v2:
- Full implementation of logic to update FAILED entries to STALE.
- Update the RFC 9131-related comments and sysctl documentation.
- Add selftests to verify state changes only when the accept_untracked_na setting is enabled.
https://lore.kernel.org/r/[email protected]
v1 (RFC):
https://lore.kernel.org/r/[email protected]
Testing:
- x86_64 allyesconfig build with W=1
- Sparse on net/ipv6/ndisc.c
- tools/testing/selftests/net/ndisc_unsolicited_na_test.sh: 14 passed
- Local NIPA checks: all 45 checks passed
- Sashiko local review completed
Lawrence Lee (2):
ipv6: update NUD_FAILED neighbors from NA messages
selftests: net: test untracked NA recovery of FAILED neighbors
Documentation/networking/ip-sysctl.rst | 28 +-- net/ipv6/ndisc.c | 47 +++-- .../net/ndisc_unsolicited_na_test.sh | 195 ++++++++++++++---- 3 files changed, 201 insertions(+), 69 deletions(-)
base-commit: ee05ecfe2cea997d4c8cb1c3af01406104a15a80
Signature · 1 line
2.43.0
PATCH v4 1/2 ipv6: update NUD_FAILED neighbors from NA messages
Transition a FAILED neighbor entry to STALE upon receipt of an NA message on routers when accept_untracked_na is enabled. This extends the RFC 9131 accept_untracked_na behavior so that FAILED entries are treated the same as non-existent entries.
RFC 4861 section 7.3.3 says that an entry should be deleted when address resolution fails. Linux instead retains the entry in NUD_FAILED, so treating it as untracked is consistent with the protocol model.
Trying to resolve FAILED neighbors via periodic probing (e.g. using NTF_EXT_MANAGED) is more work compared to this approach which uses information in NAs that the kernel may already be receiving. Note that because this behavior in IPv6 is dependent on the accept_untracked_na sysctl setting, this approach is more conservative than IPv4 which transitions FAILED neighbors to STALE by default upon receiving GARPs.
---
The existing 6LoWPAN override-only handling also applies to INCOMPLETE entries and is intentionally left unchanged. The existing rt6_clean_tohost() source-versus-target behavior applies to all neighbor states and is also left unchanged.
Documentation/networking/ip-sysctl.rst | 28 ++++++++------- net/ipv6/ndisc.c | 47 ++++++++++++++------------ 2 files changed, 41 insertions(+), 34 deletions(-)
Documentation/networking/ip-sysctl.rst +15 -13
@@ -3225,18 +3225,19 @@ drop_unsolicited_na - BOOLEAN Default: 0 (disabled). accept_untracked_na - INTEGER - Define behavior for accepting neighbor advertisements from devices that - are absent in the neighbor cache: + Define behavior for accepting neighbor advertisements for IPv6 addresses + that are absent from the neighbor cache or whose entries are in FAILED + state: - - 0 - (default) Do not accept unsolicited and untracked neighbor - advertisements. + - 0 - (default) Do not create new neighbor cache entries or update + FAILED entries from neighbor advertisements. - - 1 - Add a new neighbor cache entry in STALE state for routers on - receiving a neighbor advertisement (either solicited or unsolicited) - with target link-layer address option specified if no neighbor entry - is already present for the advertised IPv6 address. Without this knob, - NAs received for untracked addresses (absent in neighbor cache) are - silently ignored. + - 1 - For routers, add a new neighbor cache entry or update an existing + FAILED entry to STALE upon receiving a neighbor advertisement (either + solicited or unsolicited) with the target link-layer address option + specified. Without this knob, NAs received for untracked addresses + (absent from the neighbor cache or in FAILED state) are silently + ignored. This is as per router-side behavior documented in RFC9131.
@@ -3251,9 +3252,10 @@ accept_untracked_na - INTEGER used in conjunction with the ndisc_notify setting on the host to satisfy this prerequisite. - - 2 - Extend option (1) to add a new neighbor cache entry only if the - source IP address is in the same subnet as an address configured on - the interface that received the neighbor advertisement. + - 2 - Extend option (1) to add a new neighbor cache entry or update a + FAILED entry only if the source IP address is in the same subnet as + an address configured on the interface that received the neighbor + advertisement. enhanced_dad - BOOLEAN Include a nonce option in the IPv6 neighbor solicitation messages used for
net/ipv6/ndisc.c +26 -21
73 lines not on this page · raw message
Signature · 1 line
2.43.0
PATCH v4 2/2 selftests: net: test untracked NA recovery of FAILED neighbors
In reply to Lawrence LeeSeed a FAILED neighbor before triggering an unsolicited NA. Verify that the entry transitions to STALE and learns the advertised link-layer address only when drop_unsolicited_na is disabled, accept_untracked_na mode 1 or in-prefix mode 2 is enabled, and IPv6 forwarding is enabled. Verify that disabling each gate keeps the entry in FAILED without a link-layer address.
Mark the seed externally learned and disable carrier-based eviction so the entry cannot be garbage-collected before the NA arrives. Require the marker after processing to prove that a recovered entry was updated in place instead of deleted and recreated.
Keep the NA source, target, and neighbor key fixed for the mode 2 cases. Vary the prefix configured on the router to exercise the source-prefix check without changing the entry under test.
Arm packet capture before bringing up the host interface and starting DAD. Wait for the capture to become ready and for tcpdump to exit so the NA cannot be missed and receive processing is complete before checking the neighbor entry.
Preserve the return code from test_unsolicited_na_common() before constructing the test description. The array assignment previously reset the return code to zero and caused existing cases to report false success.
--- .../net/ndisc_unsolicited_na_test.sh | 195 ++++++++++++++---- 1 file changed, 160 insertions(+), 35 deletions(-)
tools/testing/selftests/net/ndisc_unsolicited_na_test.sh +160 -35
@@ -3,12 +3,18 @@ # This test is for the accept_untracked_na feature to # enable RFC9131 behaviour. The following is the test-matrix. -# drop accept fwding behaviour -# ---- ------ ------ ---------------------------------------------- -# 1 X X Don't update NC -# 0 0 X Don't update NC -# 0 1 0 Don't update NC -# 0 1 1 Add a STALE NC entry +# state drop accept fwding behaviour +# ------ ---- ------ ------ ---------------------------------------------- +# absent 1 X X Don't update NC +# absent 0 0 X Don't update NC +# absent 0 1 0 Don't update NC +# absent 0 1 1 Add a STALE NC entry +# failed 1 X X Keep the NC entry in FAILED state +# failed 0 0 X Keep the NC entry in FAILED state +# failed 0 1 0 Keep the NC entry in FAILED state +# failed 0 1 1 Update the NC entry to STALE +# failed 0 2 1 Update the NC entry to STALE if in-network +# failed 0 2 1 Keep the NC entry FAILED if out-of-network source lib.sh ret=0
@@ -19,14 +25,18 @@ PAUSE=no HOST_INTF="veth-host" ROUTER_INTF="veth-router" -ROUTER_ADDR="2000:20::1" +ROUTER_ADDR_IN_NETWORK="2000:20::1" +ROUTER_ADDR_OUT_OF_NETWORK="2000:21::1" +ROUTER_ADDR="${ROUTER_ADDR_IN_NETWORK}" HOST_ADDR="2000:20::2" +HOST_LLADDR="02:00:00:00:00:02" SUBNET_WIDTH=64 ROUTER_ADDR_WITH_MASK="${ROUTER_ADDR}/${SUBNET_WIDTH}" HOST_ADDR_WITH_MASK="${HOST_ADDR}/${SUBNET_WIDTH}" tcpdump_stdout= tcpdump_stderr= +tcpdump_pid= log_test() {
@@ -75,6 +85,7 @@ setup() ${IP_ROUTER} link add ${ROUTER_INTF} type veth \ peer name ${HOST_INTF} netns ${HOST_NS} + ${IP_HOST} link set dev "${HOST_INTF}" address "${HOST_LLADDR}" # Enable IPv6 on both router and host, and configure static addresses. # The router here is the DUT
@@ -88,6 +99,8 @@ setup() ${ROUTER_CONF}.drop_unsolicited_na=${drop_unsolicited_na} ${IP_ROUTER_EXEC} sysctl -qw \ ${ROUTER_CONF}.accept_untracked_na=${accept_untracked_na} + ${IP_ROUTER_EXEC} sysctl -qw \ + ${ROUTER_CONF}.ndisc_evict_nocarrier=0 ${IP_ROUTER_EXEC} sysctl -qw ${ROUTER_CONF}.disable_ipv6=0 ${IP_ROUTER} addr add ${ROUTER_ADDR_WITH_MASK} dev ${ROUTER_INTF}
@@ -102,24 +115,39 @@ setup() } start_tcpdump() { - set -e - tcpdump_stdout=`mktemp` - tcpdump_stderr=`mktemp` + tcpdump_stdout=$(mktemp) || return 1 + tcpdump_stderr=$(mktemp) || return 1 ${IP_ROUTER_EXEC} timeout 15s \ tcpdump --immediate-mode -tpni ${ROUTER_INTF} -c 1 \ "icmp6 && icmp6[0] == 136 && src ${HOST_ADDR}" \ - > ${tcpdump_stdout} 2> /dev/null - set +e + > "${tcpdump_stdout}" 2> "${tcpdump_stderr}" & + tcpdump_pid=$! + + slowwait 5 grep -q "listening on ${ROUTER_INTF}" "${tcpdump_stderr}" +} + +wait_tcpdump() +{ + local rc + + wait "${tcpdump_pid}" + rc=$? + tcpdump_pid= + + return "${rc}" } cleanup_tcpdump() { - set -e - [[ ! -z ${tcpdump_stdout} ]] && rm -f ${tcpdump_stdout} - [[ ! -z ${tcpdump_stderr} ]] && rm -f ${tcpdump_stderr} + if [ -n "${tcpdump_pid}" ]; then + kill "${tcpdump_pid}" 2> /dev/null + wait "${tcpdump_pid}" 2> /dev/null + fi + [ -n "${tcpdump_stdout}" ] && rm -f "${tcpdump_stdout}" + [ -n "${tcpdump_stderr}" ] && rm -f "${tcpdump_stderr}" tcpdump_stdout= tcpdump_stderr= - set +e + tcpdump_pid= } cleanup()
@@ -129,58 +157,145 @@ cleanup() ip netns del ${ROUTER_NS} } -link_up() { - set -e +router_link_up() +{ ${IP_ROUTER} link set dev ${ROUTER_INTF} up +} + +host_link_up() +{ ${IP_HOST} link set dev ${HOST_INTF} up - set +e } verify_ndisc() { local drop_unsolicited_na=$1 local accept_untracked_na=$2 local forwarding=$3 + local initial_state=${4:-absent} + local same_subnet=${5:-1} + local expected_lladdr + local neigh_show_output + local expected_state + + if [ "${drop_unsolicited_na}" -eq 0 ] && + [ "${forwarding}" -eq 1 ]; then + case "${accept_untracked_na}" in + 1) + expected_state=STALE + expected_lladdr="${HOST_LLADDR}" + ;; + 2) + if [ "${same_subnet}" -eq 1 ]; then + expected_state=STALE + expected_lladdr="${HOST_LLADDR}" + fi + ;; + esac + fi + if [ -z "${expected_state}" ] && + [ "${initial_state}" = "failed" ]; then + expected_state=FAILED + fi - neigh_show_output=$(${IP_ROUTER} neigh show \ - to ${HOST_ADDR} dev ${ROUTER_INTF} nud stale) - if [ ${drop_unsolicited_na} -eq 0 ] && \ - [ ${accept_untracked_na} -eq 1 ] && \ - [ ${forwarding} -eq 1 ]; then - # Neighbour entry expected to be present for 011 case - [[ ${neigh_show_output} ]] + if [ -n "${expected_state}" ]; then + neigh_show_output=$(${IP_ROUTER} neigh show \ + to "${HOST_ADDR}" dev "${ROUTER_INTF}") + if [[ " ${neigh_show_output} " != \ + *" ${expected_state} "* ]]; then + return 1 + fi + if [ -n "${expected_lladdr}" ] && + [[ " ${neigh_show_output} " != \ + *" lladdr ${expected_lladdr} "* ]]; then + return 1 + fi + if [[ "${expected_state}" == "FAILED" && + "${neigh_show_output}" == *"lladdr"* ]]; then + return 1 + fi + if [ "${initial_state}" = "failed" ]; then + [[ "${neigh_show_output}" == *"extern_learn"* ]] + fi else - # Neighbour entry expected to be absent for all other cases + neigh_show_output=$(${IP_ROUTER} neigh show \ + to "${HOST_ADDR}" dev "${ROUTER_INTF}") [[ -z ${neigh_show_output} ]] fi } test_unsolicited_na_common() { + local same_subnet=${5:-1} + local neigh_show_output + + if [ "${same_subnet}" -eq 1 ]; then + ROUTER_ADDR="${ROUTER_ADDR_IN_NETWORK}" + else + ROUTER_ADDR="${ROUTER_ADDR_OUT_OF_NETWORK}" + fi + ROUTER_ADDR_WITH_MASK="${ROUTER_ADDR}/${SUBNET_WIDTH}" + # Setup the test bed, but keep links down - setup $1 $2 $3 + setup "$1" "$2" "$3" + + if [ "${4:-absent}" = "failed" ]; then + if ! ${IP_ROUTER} neigh replace "${HOST_ADDR}" \ + dev "${ROUTER_INTF}" \ + nud failed extern_learn; then + echo "Unable to create NUD_FAILED neighbor entry" + return 1 + fi + neigh_show_output=$(${IP_ROUTER} neigh show \ + to "${HOST_ADDR}" dev "${ROUTER_INTF}") + if [[ " ${neigh_show_output} " != *" FAILED "* ]]; then + echo "Unable to verify NUD_FAILED neighbor entry" + return 1 + fi + if [[ "${neigh_show_output}" != *"extern_learn"* ]]; then + echo "Neighbor entry is not externally learned" + return 1 + fi + fi - # Bring the link up, wait for the NA, - # and add a delay to ensure neighbour processing is done. - link_up - start_tcpdump + # Arm the capture before bringing up the host and starting DAD. + router_link_up || return 1 + start_tcpdump || return 1 + host_link_up || return 1 + + # Closing tcpdump's packet socket calls synchronize_net(), so waiting + # for it also waits for receive processing of the captured NA. + wait_tcpdump || return 1 # Verify the neighbour table - verify_ndisc $1 $2 $3 + verify_ndisc "$1" "$2" "$3" "$4" "${same_subnet}" } test_unsolicited_na_combination() { - test_unsolicited_na_common $1 $2 $3 + local initial_state=${4:-absent} + local same_subnet=${5:-1} + local rc + + test_unsolicited_na_common "$1" "$2" "$3" "${initial_state}" \ + "${same_subnet}" + rc=$? test_msg=("test_unsolicited_na: " "drop_unsolicited_na=$1 " "accept_untracked_na=$2 " "forwarding=$3") - log_test $? 0 "${test_msg[*]}" + if [ "${initial_state}" = "failed" ]; then + test_msg+=("initial_state=failed") + fi + if [ "$2" -eq 2 ]; then + test_msg+=("same_subnet=${same_subnet}") + fi + log_test "${rc}" 0 "${test_msg[*]}" cleanup } test_unsolicited_na_combinations() { # Args: drop_unsolicited_na accept_untracked_na forwarding + # [initial_state] [same_subnet] # Expect entry test_unsolicited_na_combination 0 1 1
@@ -193,6 +308,16 @@ test_unsolicited_na_combinations() { test_unsolicited_na_combination 1 0 1 test_unsolicited_na_combination 1 1 0 test_unsolicited_na_combination 1 1 1 + + # Expect FAILED entry to become STALE + test_unsolicited_na_combination 0 1 1 failed + test_unsolicited_na_combination 0 2 1 failed 1 + + # Expect FAILED entry to remain FAILED + test_unsolicited_na_combination 0 0 1 failed + test_unsolicited_na_combination 0 1 0 failed + test_unsolicited_na_combination 1 1 1 failed + test_unsolicited_na_combination 0 2 1 failed 0 } ###############################################################################
Signature · 1 line
2.43.0
Ido Schimmel
maintainerRe: [PATCH net-next v4 2/2] selftests: net: test untracked NA recovery of FAILED neighbors
Quoted 21 lines · Lawrence Lee
Seed a FAILED neighbor before triggering an unsolicited NA. Verify that the entry transitions to STALE and learns the advertised link-layer address only when
drop_unsolicited_nais disabled,accept_untracked_namode 1 or in-prefix mode 2 is enabled, and IPv6 forwarding is enabled. Verify that disabling each gate keeps the entry in FAILED without a link-layer address.Mark the seed externally learned and disable carrier-based eviction so the entry cannot be garbage-collected before the NA arrives. Require the marker after processing to prove that a recovered entry was updated in place instead of deleted and recreated.
Keep the NA source, target, and neighbor key fixed for the mode 2 cases. Vary the prefix configured on the router to exercise the source-prefix check without changing the entry under test.
Arm packet capture before bringing up the host interface and starting DAD. Wait for the capture to become ready and for tcpdump to exit so the NA cannot be missed and receive processing is complete before checking the neighbor entry.
Quoted 5 lines
Preserve the return code from
test_unsolicited_na_common()before constructing the test description. The array assignment previously reset the return code to zero and caused existing cases to report false success.Fixes f9a2fb73318e ("net/ipv6: Introduce accept_unsolicited_na knob to implement router-side changes for RFC9131")
You shouldn't have added a Fixes tag. Backporting this patch to older kernels will make the test fail. Wait for feedback from Jakub / Paolo before posting v5. They might agree to strip it before applying.
Quoted 3 lines
Assisted-by LLM Sashiko sparseSigned-off-by Lawrence Lee <[email protected]>
Re: [PATCH net-next v4 2/2] selftests: net: test untracked NA recovery of FAILED neighbors
Quoted 4 lines · Ido Schimmel
You shouldn't have added a Fixes tag. Backporting this patch to older kernels will make the test fail. Wait for feedback from Jakub / Paolo before posting v5. They might agree to strip it before applying.
That's my mistake, I added it in response to Sashiko review feedback on v3 but thought it was just for documentation purposes, I didn't realize the tag would cause it to get backported since I only submitted to net-next. Sorry about that, will wait to hear from Jakub/Paolo before sending any new versions.
Jakub Kicinski
maintainerRe: [PATCH net-next v4 2/2] selftests: net: test untracked NA recovery of FAILED neighbors
Quoted 11 lines · On Wed, 23 Sep 2026 17:49:33 +0000 Lawrence Lee wrote:
Quoted 4 lines · Ido Schimmel
You shouldn't have added a Fixes tag. Backporting this patch to older kernels will make the test fail. Wait for feedback from Jakub / Paolo before posting v5. They might agree to strip it before applying.
That's my mistake, I added it in response to Sashiko review feedback on v3 but thought it was just for documentation purposes, I didn't realize the tag would cause it to get backported since I only submitted to net-next. Sorry about that, will wait to hear from Jakub/Paolo before sending any new versions.
Stripped, thanks for flagging.
[email protected]
AI reviewRe: [PATCH net-next v4 2/2] selftests: net: test untracked NA recovery of FAILED neighbors
In reply to Lawrence LeeThank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider.
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider.
Critical: 0 · High: 0 · Medium: 1 · Low: 1 (1)
If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough).
- [Medium] The Fixes:-tagged bug fix is bundled with new tests that depend on a new feature.
- [Low] The commit message says the test verifies that "disabling each gate keeps the entry in FAILED without a link-layer address".
Pre-existing issues:
- [Low] Some script exit paths skip cleanup().
--
selftests: net: test untracked NA recovery of FAILED neighbors
This adds cases to ndisc_unsolicited_na_test.sh that seed a NUD_FAILED neighbor. Each case checks whether an unsolicited NA moves the entry to STALE, based on drop_unsolicited_na, accept_untracked_na and forwarding.
It also runs tcpdump in the background and waits for it to exit. It keeps the return code in test_unsolicited_na_combination() so existing cases stop reporting false success.
Quoted 4 lines
tools/testing/selftests/net/ndisc_unsolicited_na_test.sh +0
-0
[ ... ]
Quoted 6 lines
tools/testing/selftests/net/ndisc_unsolicited_na_test.sh +0
-2@@ -102,24 +115,39 @@ setup() } start_tcpdump() { - set -e - tcpdump_stdout=`mktemp`
tools/testing/selftests/net/ndisc_unsolicited_na_test.sh setup() · line 124
Quoted 10 lines
tools/testing/selftests/net/ndisc_unsolicited_na_test.sh +4
-3- tcpdump_stderr=`mktemp` + tcpdump_stdout=$(mktemp) || return 1 + tcpdump_stderr=$(mktemp) || return 1 ${IP_ROUTER_EXEC} timeout 15s \ tcpdump --immediate-mode -tpni ${ROUTER_INTF} -c 1 \ "icmp6 && icmp6[0] == 136 && src ${HOST_ADDR}" \ - > ${tcpdump_stdout} 2> /dev/null - set +e + > "${tcpdump_stdout}" 2> "${tcpdump_stderr}" & + tcpdump_pid=$!
[Severity: Low]
This is a pre-existing issue, but some exit paths in this script never reach cleanup(). log_test() does this when the user quits under PAUSE_ON_FAIL or PAUSE:
[ "$a" = "q" ] && exit 1
Also, setup() runs under set -e after setup_ns() has created the namespaces.
Neither the script nor lib.sh installs an EXIT trap. On those paths the namespaces, the veth pair and the mktemp files are left behind.
This patch adds one new case. tcpdump now runs in the background here. If start_tcpdump() or host_link_up() fails and the user answers 'q' in log_test(), is the tcpdump process orphaned? It still stops by itself because of timeout 15s.
[ ... ]
Quoted 1 line
tools/testing/selftests/net/ndisc_unsolicited_na_test.sh +0
-0@@ -129,58 +157,145 @@ cleanup()
[ ... ]
Quoted 6 lines
tools/testing/selftests/net/ndisc_unsolicited_na_test.sh +6
-0+ if [ -n "${expected_state}" ]; then + neigh_show_output=$(${IP_ROUTER} neigh show \ + to "${HOST_ADDR}" dev "${ROUTER_INTF}") + if [[ " ${neigh_show_output} " != \ + *" ${expected_state} "* ]]; then + return 1
tools/testing/selftests/net/ndisc_unsolicited_na_test.sh cleanup() · line 215
Quoted 10 lines
tools/testing/selftests/net/ndisc_unsolicited_na_test.sh +10
-0+ fi + if [ -n "${expected_lladdr}" ] && + [[ " ${neigh_show_output} " != \ + *" lladdr ${expected_lladdr} "* ]]; then + return 1 + fi + if [[ "${expected_state}" == "FAILED" && + "${neigh_show_output}" == *"lladdr"* ]]; then + return 1 + fi
[Severity: Low]
The commit message says:
Verify that disabling each gate keeps the entry in FAILED without a link-layer address.
Can this lladdr check in verify_ndisc() ever trigger? __neigh_fill_info() only emits NDA_LLADDR for NUD_VALID states:
net/core/neighbour.c:__neigh_fill_info() {
...
ndm->ndm_state = neigh->nud_state;
if (neigh->nud_state & NUD_VALID) {
char haddr[MAX_ADDR_LEN];
neigh_ha_snapshot(haddr, neigh, neigh->dev);
if (nla_put(skb, NDA_LLADDR, neigh->dev->addr_len, haddr) < 0)
goto nla_put_failure;
}
...
}NUD_VALID does not include NUD_FAILED, so ip neigh show never prints lladdr for a FAILED entry. Once the FAILED state match above succeeds, this check is always false. That means the "without a link-layer address" part is not actually tested.
[ ... ]
Quoted 14 lines
test_unsolicited_na_combination(){tools/testing/selftests/net/ndisc_unsolicited_na_test.sh +7
-2- test_unsolicited_na_common $1 $2 $3 + local initial_state=${4:-absent} + local same_subnet=${5:-1} + local rc + + test_unsolicited_na_common "$1" "$2" "$3" "${initial_state}" \ + "${same_subnet}" + rc=$? test_msg=("test_unsolicited_na: " "drop_unsolicited_na=$1 " "accept_untracked_na=$2 " "forwarding=$3") - log_test $? 0 "${test_msg[*]}"
[ ... ]
tools/testing/selftests/net/ndisc_unsolicited_na_test.sh test_unsolicited_na_combinations() { · line 314
Quoted 8 lines
tools/testing/selftests/net/ndisc_unsolicited_na_test.sh +4
-0@@ -193,6 +308,16 @@ test_unsolicited_na_combinations() { test_unsolicited_na_combination 1 0 1 test_unsolicited_na_combination 1 1 0 test_unsolicited_na_combination 1 1 1 + + # Expect FAILED entry to become STALE + test_unsolicited_na_combination 0 1 1 failed + test_unsolicited_na_combination 0 2 1 failed 1
[Severity: Medium]
This commit carries a Fixes: tag for f9a2fb73318e. The only change that fixes that commit seems to be the rc=$? move in test_unsolicited_na_combination(), which now runs before the test_msg=(...) assignment.
These two new cases expect a seeded NUD_FAILED entry to become STALE with lladdr 02:00:00:00:00:02 and to keep extern_learn. That behaviour comes from the preceding series commit "ipv6: update NUD_FAILED neighbors from NA messages", which has no Fixes: or stable tag.
Without that commit, ndisc_recv_na() still skips FAILED entries:
net/ipv6/ndisc.c:ndisc_recv_na() {
...
if (neigh && !IS_ERR(neigh)) {
u8 old_flags = neigh->flags;
if (READ_ONCE(neigh->nud_state) & NUD_FAILED)
goto out;
...
}Suppose a stable tree takes this patch because of the Fixes: tag, but not the feature commit. With rc now preserved, won't these two cases report FAIL?
Could the rc fix go in its own patch with the Fixes: tag, and the FAILED-recovery tests in a separate patch without it? If they stay together, could the commit message at least state the dependency?
[ ... ]
Signature · 1 line
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/cover.1790127207.git.lfqlee314%40gmail.com
This series was applied to netdev/net-next.git (main) by Jakub Kicinski <[email protected]>:
Hello:
This series was applied to netdev/net-next.git (main) by Jakub Kicinski <[email protected]>:
Quoted 8 lines · On Wed, 23 Sep 2026 01:34:17 +0000 you wrote:
Neighbor Advertisements for existing
NUD_FAILEDentries are currently ignored. This can leave a first-hop router with an unusable neighbor after the host becomes reachable and announces itself, particularly when forwarding is offloaded away from the kernel and traffic destined to the FAILED neighbor is unable to trigger the kernel's normal neighbor resolution process.[...]
Here is the summary with links:
- [net-next,v4,1/2] ipv6: update NUD_FAILED neighbors from NA messages
https://git.kernel.org/netdev/net-next/c/24717a02aee4 - [net-next,v4,2/2] selftests: net: test untracked NA recovery of FAILED neighbors https://git.kernel.org/netdev/net-next/c/022e48e20776
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html