* [PATCH net v2 4/4] selftests/net: add no NDP b/mcast,null poison test
2026-01-26 23:53 [PATCH net v2 0/4] discard ARP/NDP b/mcast/null announce (poison) Marc Suñé
@ 2026-01-26 23:53 ` Marc Suñé
0 siblings, 0 replies; 2+ messages in thread
From: Marc Suñé @ 2026-01-26 23:53 UTC (permalink / raw)
To: kuba, willemdebruijn.kernel, pabeni
Cc: netdev, dborkman, vadim.fedorenko, Marc Suñé
Add a selftest to test that NDP bcast/mcast/null poisioning checks are
never bypassed.
Signed-off-by: Marc Suñé <marcdevel@gmail.com>
---
tools/testing/selftests/net/.gitignore | 1 +
tools/testing/selftests/net/Makefile | 3 +-
.../net/arp_ndisc_no_invalid_sha_poison.sh | 368 ++++++++++++++++++
.../net/arp_no_invalid_sha_poision.sh | 176 ---------
tools/testing/selftests/net/ndisc_send.c | 198 ++++++++++
5 files changed, 569 insertions(+), 177 deletions(-)
create mode 100755 tools/testing/selftests/net/arp_ndisc_no_invalid_sha_poison.sh
delete mode 100755 tools/testing/selftests/net/arp_no_invalid_sha_poision.sh
create mode 100644 tools/testing/selftests/net/ndisc_send.c
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index fd08ceeab07c..5a82300a22a9 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -18,6 +18,7 @@ ipv6_flowlabel_mgr
ipv6_fragmentation
log.txt
msg_zerocopy
+ndisc_send
netlink-dumps
nettest
proc_net_pktgen
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index a765f1800752..afc24d419135 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -9,7 +9,7 @@ CFLAGS += -I../
TEST_PROGS := \
altnames.sh \
amt.sh \
- arp_no_invalid_sha_poision.sh \
+ arp_ndisc_no_invalid_sha_poison.sh \
arp_ndisc_evict_nocarrier.sh \
arp_ndisc_untracked_subnets.sh \
bareudp.sh \
@@ -170,6 +170,7 @@ TEST_GEN_PROGS := \
bind_wildcard \
epoll_busy_poll \
ipv6_fragmentation \
+ ndisc_send \
proc_net_pktgen \
reuseaddr_conflict \
reuseport_bpf \
diff --git a/tools/testing/selftests/net/arp_ndisc_no_invalid_sha_poison.sh b/tools/testing/selftests/net/arp_ndisc_no_invalid_sha_poison.sh
new file mode 100755
index 000000000000..65cde354b9f8
--- /dev/null
+++ b/tools/testing/selftests/net/arp_ndisc_no_invalid_sha_poison.sh
@@ -0,0 +1,368 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Tests that ARP announcements with Broadcast or NULL mac are never
+# accepted
+#
+
+source lib.sh
+
+readonly V4_ADDR0="10.0.10.1"
+readonly V6_ADDR0="fd00:1::1"
+readonly V4_ADDR1="10.0.10.2"
+readonly V6_ADDR1="fd00:1::2"
+readonly V6_ALL_NODES="ff02::1"
+readonly V6_SOL_NODE1="ff02::1:ff00:0002"
+readonly BCAST_MAC="ff:ff:ff:ff:ff:ff"
+readonly MCAST_MAC="01:00:5e:00:00:00"
+readonly NULL_MAC="00:00:00:00:00:00"
+readonly VALID_MAC="02:01:02:03:04:05"
+readonly V6_ALL_NODE_MAC="33:33:FF:00:00:01"
+readonly V6_SOL_NODE_MAC1="33:33:FF:00:00:02"
+readonly NS=135
+readonly NA=136
+readonly ARP_REQ=1
+readonly ARP_REPLY=2
+nsid=100
+ret=0
+veth0_ifindex=0
+veth1_mac=
+
+setup() {
+ setup_ns PEER_NS
+
+ ip link add name veth0 type veth peer name veth1
+ ip link set dev veth0 up
+ ip link set dev veth1 netns ${PEER_NS}
+ ip netns exec ${PEER_NS} ip link set dev veth1 up
+ ip addr add ${V4_ADDR0}/24 dev veth0
+ ip addr add ${V6_ADDR0}/64 dev veth0
+ ip netns exec ${PEER_NS} ip addr add ${V4_ADDR1}/24 dev veth1
+ ip netns exec ${PEER_NS} ip route add default via ${V4_ADDR0} dev veth1
+
+ ip netns exec ${PEER_NS} ip addr add ${V6_ADDR1}/64 dev veth1
+ ip netns exec ${PEER_NS} ip route add default via ${V6_ADDR0} dev veth1
+
+ # Raise ARP timers to avoid flakes due to refreshes
+ sysctl -w net.ipv4.neigh.veth0.base_reachable_time=3600 \
+ >/dev/null 2>&1
+ ip netns exec ${PEER_NS} \
+ sysctl -w net.ipv4.neigh.veth1.gc_stale_time=3600 \
+ >/dev/null 2>&1
+ ip netns exec ${PEER_NS} \
+ sysctl -w net.ipv4.neigh.veth1.base_reachable_time=3600 \
+ >/dev/null 2>&1
+
+ veth0_ifindex=$(ip -j link show veth0 | jq -r '.[0].ifindex')
+ veth1_mac="$(ip netns exec ${PEER_NS} ip -j link show veth1 | \
+ jq -r '.[0].address' )"
+}
+
+cleanup() {
+ ip neigh flush dev veth0
+ ip link del veth0
+ cleanup_ns ${PEER_NS}
+}
+
+# Make sure ARP announcement with invalid MAC is never learnt
+run_no_arp_poisoning() {
+ local l2_dmac=${1}
+ local tmac=${2}
+ local op=${3}
+
+ ret=0
+
+ ip netns exec ${PEER_NS} ip neigh flush dev veth1 >/dev/null 2>&1
+ ip netns exec ${PEER_NS} ping -c 1 ${V4_ADDR0} >/dev/null 2>&1
+
+ # Poison with a valid MAC to ensure injection is working
+ ./arp_send ${veth0_ifindex} ${BCAST_MAC} ${VALID_MAC} ${op} \
+ ${V4_ADDR0} ${VALID_MAC} ${V4_ADDR0} ${VALID_MAC}
+
+ neigh=$(ip netns exec ${PEER_NS} ip neigh show ${V4_ADDR0} | \
+ grep ${VALID_MAC})
+ if [ "${neigh}" == "" ]; then
+ echo "ERROR: unable to ARP poision with a valid MAC ${VALID_MAC}"
+ ip netns exec ${PEER_NS} ip neigh show ${V4_ADDR0}
+ ret=1
+ return
+ fi
+
+ # Poison with tmac
+ ./arp_send ${veth0_ifindex} ${l2_dmac} ${VALID_MAC} ${op} \
+ ${V4_ADDR0} ${tmac} ${V4_ADDR0} ${tmac}
+
+ neigh=$(ip netns exec ${PEER_NS} ip neigh show ${V4_ADDR0} | \
+ grep ${tmac})
+ if [ "${neigh}" != "" ]; then
+ echo "ERROR: ARP entry learnt for ${tmac} announcement."
+ ip netns exec ${PEER_NS} ip neigh show ${V4_ADDR0}
+ ret=1
+ return
+ fi
+}
+
+# Make sure NDP announcement with invalid MAC is never learnt
+run_no_ndp_poisoning() {
+ local l2_dmac=${1}
+ local dst_ip=${2}
+ local op=${3}
+ local tip=${V6_ADDR0}
+ local tmac=${4}
+
+ if [ "${op}" == "${NS}" ]; then
+ tip=${V6_ADDR1}
+ fi
+
+ ret=0
+
+ ip netns exec ${PEER_NS} ip -6 neigh flush dev veth1 >/dev/null 2>&1
+ ip netns exec ${PEER_NS} ping -c 1 ${V6_ADDR0} >/dev/null 2>&1
+
+ # Poison with a valid MAC to ensure injection is working
+ ./ndisc_send ${veth0_ifindex} ${l2_dmac} ${VALID_MAC} ${dst_ip} \
+ ${V6_ADDR0} ${tip} ${op} ${VALID_MAC}
+ neigh=$(ip netns exec ${PEER_NS} ip neigh show ${V6_ADDR0} | \
+ grep ${VALID_MAC})
+ if [ "${neigh}" == "" ]; then
+ echo "ERROR: unable to NDP poision with a valid MAC ${VALID_MAC}"
+ ip netns exec ${PEER_NS} ip neigh show ${V6_ADDR0}
+ ret=1
+ return
+ fi
+
+ # Poison with tmac
+ ./ndisc_send ${veth0_ifindex} ${l2_dmac} ${VALID_MAC} ${dst_ip} \
+ ${V6_ADDR0} ${tip} ${op} ${tmac}
+ neigh=$(ip netns exec ${PEER_NS} ip neigh show ${V6_ADDR0} | \
+ grep ${tmac})
+ if [ "${neigh}" != "" ]; then
+ echo "ERROR: NDP entry learnt for ${tmac} announcement."
+ ip netns exec ${PEER_NS} ip neigh show ${V6_ADDR0}
+ ret=1
+ return
+ fi
+}
+
+print_test_result() {
+ local msg=${1}
+ local rc=${2}
+
+ if [ ${rc} == 0 ]; then
+ printf "TEST: %-60s [ OK ]" "${msg}"
+ else
+ printf "TEST: %-60s [ FAIL ]" "${msg}"
+ fi
+}
+
+run_all_tests() {
+ local results
+
+ setup
+
+ ## ARP
+ # Broadcast gARPs
+ msg="1.1 ARP no poisoning dmac=bcast reply sha=bcast"
+ run_no_arp_poisoning ${BCAST_MAC} ${BCAST_MAC} ${ARP_REPLY}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="1.2 ARP no poisoning dmac=bcast reply sha=null"
+ run_no_arp_poisoning ${BCAST_MAC} ${NULL_MAC} ${ARP_REPLY}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="1.3 ARP no poisoning dmac=bcast req sha=bcast"
+ run_no_arp_poisoning ${BCAST_MAC} ${BCAST_MAC} ${ARP_REQ}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="1.4 ARP no poisoning dmac=bcast req sha=null"
+ run_no_arp_poisoning ${BCAST_MAC} ${NULL_MAC} ${ARP_REQ}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="1.5 ARP no poisoning dmac=bcast req sha=mcast"
+ run_no_arp_poisoning ${BCAST_MAC} ${MCAST_MAC} ${ARP_REQ}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="1.6 ARP no poisoning dmac=bcast reply sha=mcast"
+ run_no_arp_poisoning ${BCAST_MAC} ${MCAST_MAC} ${ARP_REPLY}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ # Targeted gARPs
+ msg="1.7 ARP no poisoning dmac=veth0 reply sha=bcast"
+ run_no_arp_poisoning ${veth1_mac} ${BCAST_MAC} ${ARP_REPLY}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="1.8 ARP no poisoning dmac=veth0 reply sha=null"
+ run_no_arp_poisoning ${veth1_mac} ${NULL_MAC} ${ARP_REPLY}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="1.9 ARP no poisoning dmac=veth0 req sha=bcast"
+ run_no_arp_poisoning ${veth1_mac} ${BCAST_MAC} ${ARP_REQ}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="1.10 ARP no poisoning dmac=veth0 req sha=null"
+ run_no_arp_poisoning ${veth1_mac} ${NULL_MAC} ${ARP_REQ}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="1.11 ARP no poisoning dmac=veth0 req sha=mcast"
+ run_no_arp_poisoning ${veth1_mac} ${MCAST_MAC} ${ARP_REQ}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="1.12 ARP no poisoning dmac=veth0 reply sha=mcast"
+ run_no_arp_poisoning ${veth1_mac} ${MCAST_MAC} ${ARP_REPLY}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ ### NDP
+ ## NA
+ # Broadcast / All node MAC, all-node IP announcements
+ msg="2.1 NDP no poisoning dmac=bcast all_nodes na lladdr=bcast"
+ run_no_ndp_poisoning ${BCAST_MAC} ${V6_ALL_NODES} ${NA} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.2 NDP no poisoning dmac=bcast all_nodes na lladdr=null"
+ run_no_ndp_poisoning ${BCAST_MAC} ${V6_ALL_NODES} ${NA} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.3 NDP no poisoning dmac=allnode all_nodes na lladdr=bcast"
+ run_no_ndp_poisoning ${V6_ALL_NODE_MAC} ${V6_ALL_NODES} ${NA} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.4 NDP no poisoning dmac=allnode all_nodes na lladdr=null"
+ run_no_ndp_poisoning ${V6_ALL_NODE_MAC} ${V6_ALL_NODES} ${NA} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.5 NDP no poisoning dmac=bcast all_nodes na lladdr=bcast"
+ run_no_ndp_poisoning ${BCAST_MAC} ${V6_ALL_NODES} ${NA} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.6 NDP no poisoning dmac=bcast all_nodes na lladdr=null"
+ run_no_ndp_poisoning ${BCAST_MAC} ${V6_ALL_NODES} ${NA} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.7 NDP no poisoning dmac=allnode all_nodes na lladdr=bcast"
+ run_no_ndp_poisoning ${V6_ALL_NODE_MAC} ${V6_ALL_NODES} ${NA} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.8 NDP no poisoning dmac=allnode all_nodes na lladdr=null"
+ run_no_ndp_poisoning ${V6_ALL_NODE_MAC} ${V6_ALL_NODES} ${NA} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ # Broadcast / All node MAC, Targeted IP announce
+ msg="2.9 NDP no poisoning dmac=bcast targeted na lladdr=bcast"
+ run_no_ndp_poisoning ${BCAST_MAC} ${V6_ADDR1} ${NA} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.10 NDP no poisoning dmac=bcast targeted na lladdr=null"
+ run_no_ndp_poisoning ${BCAST_MAC} ${V6_ADDR1} ${NA} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.11 NDP no poisoning dmac=allnode targeted na lladdr=bcast"
+ run_no_ndp_poisoning ${V6_ALL_NODE_MAC} ${V6_ADDR1} ${NA} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.12 NDP no poisoning dmac=allnode targeted na lladdr=null"
+ run_no_ndp_poisoning ${V6_ALL_NODE_MAC} ${V6_ADDR1} ${NA} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.13 NDP no poisoning dmac=bcast targeted na lladdr=bcast"
+ run_no_ndp_poisoning ${BCAST_MAC} ${V6_ADDR1} ${NA} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.14 NDP no poisoning dmac=bcast targeted na lladdr=null"
+ run_no_ndp_poisoning ${BCAST_MAC} ${V6_ADDR1} ${NA} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.15 NDP no poisoning dmac=allnode targeted na lladdr=bcast"
+ run_no_ndp_poisoning ${V6_ALL_NODE_MAC} ${V6_ADDR1} ${NA} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.16 NDP no poisoning dmac=allnode targeted na lladdr=null"
+ run_no_ndp_poisoning ${V6_ALL_NODE_MAC} ${V6_ADDR1} ${NA} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ # Targeted MAC, Targeted IP announce
+ msg="2.17 NDP no poisoning dmac=veth1 targeted na lladdr=bcast"
+ run_no_ndp_poisoning ${veth1_mac} ${V6_ADDR1} ${NA} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.18 NDP no poisoning dmac=veth1 targeted na lladdr=null"
+ run_no_ndp_poisoning ${veth1_mac} ${V6_ADDR1} ${NA} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ # Poison with MCAST mac
+ msg="2.19 NDP no poisoning dmac=allnode all_nodes na lladdr=mcast"
+ run_no_ndp_poisoning ${V6_ALL_NODE_MAC} ${V6_ALL_NODES} ${NA} ${MCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+ msg="2.20 NDP no poisoning dmac=veth1 targeted na lladdr=mcast"
+ run_no_ndp_poisoning ${veth1_mac} ${V6_ADDR1} ${NA} ${MCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ ## NS
+ # Broadcast / SolNode node MAC, SolNode IP solic
+ msg="2.21 NDP no poisoning dmac=bcast solnode ns lladdr=bcast"
+ run_no_ndp_poisoning ${BCAST_MAC} ${V6_SOL_NODE1} ${NS} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.22 NDP no poisoning dmac=bcast solnode ns lladdr=null"
+ run_no_ndp_poisoning ${BCAST_MAC} ${V6_SOL_NODE1} ${NS} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.23 NDP no poisoning dmac=solnode solnode ns lladdr=bcast"
+ run_no_ndp_poisoning ${V6_SOL_NODE_MAC1} ${V6_SOL_NODE1} ${NS} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.24 NDP no poisoning dmac=solnode solnode ns lladdr=null"
+ run_no_ndp_poisoning ${V6_SOL_NODE_MAC1} ${V6_SOL_NODE1} ${NS} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ # Broadcast / SolNode node MAC, target IP solic
+ msg="2.25 NDP no poisoning dmac=bcast target ns lladdr=bcast"
+ run_no_ndp_poisoning ${BCAST_MAC} ${V6_ADDR1} ${NS} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.26 NDP no poisoning dmac=bcast target ns lladdr=null"
+ run_no_ndp_poisoning ${BCAST_MAC} ${V6_ADDR1} ${NS} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.27 NDP no poisoning dmac=solnode target ns lladdr=bcast"
+ run_no_ndp_poisoning ${V6_SOL_NODE_MAC1} ${V6_ADDR1} ${NS} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.28 NDP no poisoning dmac=solnode target ns lladdr=null"
+ run_no_ndp_poisoning ${V6_SOL_NODE_MAC1} ${V6_ADDR1} ${NS} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ # Targeted MAC, Targeted IP solic
+ msg="2.29 NDP no poisoning dmac=veth1 target ns lladdr=bcast"
+ run_no_ndp_poisoning ${veth1_mac} ${V6_ADDR1} ${NS} ${BCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.30 NDP no poisoning dmac=veth1 target ns lladdr=null"
+ run_no_ndp_poisoning ${veth1_mac} ${V6_ADDR1} ${NS} ${NULL_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ # Poison with MCAST mac
+ msg="2.31 NDP no poisoning dmac=solnode solnode ns lladdr=mcast"
+ run_no_ndp_poisoning ${V6_SOL_NODE_MAC1} ${V6_SOL_NODE1} ${NS} ${MCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ msg="2.32 NDP no poisoning dmac=veth1 target ns lladdr=mcast"
+ run_no_ndp_poisoning ${veth1_mac} ${V6_ADDR1} ${NS} ${MCAST_MAC}
+ results+="$(print_test_result "${msg}" ${ret})\n"
+
+ cleanup
+
+ printf '%b' "${results}"
+}
+
+if [ "$(id -u)" -ne 0 ];then
+ echo "SKIP: Need root privileges"
+ exit $ksft_skip;
+fi
+
+if [ ! -x "$(command -v ip)" ]; then
+ echo "SKIP: Could not run test without ip tool"
+ exit $ksft_skip
+fi
+
+run_all_tests
+exit $ret
diff --git a/tools/testing/selftests/net/arp_no_invalid_sha_poision.sh b/tools/testing/selftests/net/arp_no_invalid_sha_poision.sh
deleted file mode 100755
index 1505f1bde487..000000000000
--- a/tools/testing/selftests/net/arp_no_invalid_sha_poision.sh
+++ /dev/null
@@ -1,176 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-#
-# Tests that ARP announcements with Broadcast, Multicast or NULL mac are never
-# accepted
-#
-
-source lib.sh
-
-readonly V4_ADDR0="10.0.10.1"
-readonly V4_ADDR1="10.0.10.2"
-readonly BCAST_MAC="ff:ff:ff:ff:ff:ff"
-readonly MCAST_MAC="01:00:5e:00:00:00"
-readonly NULL_MAC="00:00:00:00:00:00"
-readonly VALID_MAC="02:01:02:03:04:05"
-readonly ARP_REQ=1
-readonly ARP_REPLY=2
-nsid=100
-ret=0
-veth0_ifindex=0
-veth1_mac=
-
-setup() {
- setup_ns PEER_NS
-
- ip link add name veth0 type veth peer name veth1
- ip link set dev veth0 up
- ip link set dev veth1 netns ${PEER_NS}
- ip netns exec ${PEER_NS} ip link set dev veth1 up
- ip addr add ${V4_ADDR0}/24 dev veth0
- ip netns exec ${PEER_NS} ip addr add ${V4_ADDR1}/24 dev veth1
- ip netns exec ${PEER_NS} ip route add default via ${V4_ADDR0} dev veth1
-
- # Raise ARP timers to avoid flakes due to refreshes
- sysctl -w net.ipv4.neigh.veth0.base_reachable_time=3600 \
- >/dev/null 2>&1
- ip netns exec ${PEER_NS} \
- sysctl -w net.ipv4.neigh.veth1.gc_stale_time=3600 \
- >/dev/null 2>&1
- ip netns exec ${PEER_NS} \
- sysctl -w net.ipv4.neigh.veth1.base_reachable_time=3600 \
- >/dev/null 2>&1
-
- veth0_ifindex=$(ip -j link show veth0 | jq -r '.[0].ifindex')
- veth1_mac="$(ip netns exec ${PEER_NS} ip -j link show veth1 | \
- jq -r '.[0].address' )"
-}
-
-cleanup() {
- ip neigh flush dev veth0
- ip link del veth0
- cleanup_ns ${PEER_NS}
-}
-
-# Make sure ARP announcement with invalid MAC is never learnt
-run_no_arp_poisoning() {
- local l2_dmac=${1}
- local tmac=${2}
- local op=${3}
-
- ret=0
-
- ip netns exec ${PEER_NS} ip neigh flush dev veth1 >/dev/null 2>&1
- ip netns exec ${PEER_NS} ping -c 1 ${V4_ADDR0} >/dev/null 2>&1
-
- # Poison with a valid MAC to ensure injection is working
- ./arp_send ${veth0_ifindex} ${l2_dmac} ${VALID_MAC} ${op} \
- ${V4_ADDR0} ${VALID_MAC} ${V4_ADDR0} ${VALID_MAC}
-
- neigh=$(ip netns exec ${PEER_NS} ip neigh show ${V4_ADDR0} | \
- grep ${VALID_MAC})
- if [ "${neigh}" == "" ]; then
- echo "ERROR: unable to ARP poision with a valid MAC ${VALID_MAC}"
- ip netns exec ${PEER_NS} ip neigh show ${V4_ADDR0}
- ret=1
- return
- fi
-
- # Poison with tmac
- ./arp_send ${veth0_ifindex} ${l2_dmac} ${VALID_MAC} ${op} \
- ${V4_ADDR0} ${tmac} ${V4_ADDR0} ${tmac}
-
- neigh=$(ip netns exec ${PEER_NS} ip neigh show ${V4_ADDR0} | \
- grep ${tmac})
- if [ "${neigh}" != "" ]; then
- echo "ERROR: ARP entry learnt for ${tmac} announcement."
- ip netns exec ${PEER_NS} ip neigh show ${V4_ADDR0}
- ret=1
- return
- fi
-}
-
-print_test_result() {
- local msg=${1}
- local rc=${2}
-
- if [ ${rc} == 0 ]; then
- printf "TEST: %-60s [ OK ]" "${msg}"
- else
- printf "TEST: %-60s [ FAIL ]" "${msg}"
- fi
-}
-
-run_all_tests() {
- local results
-
- setup
-
- ## ARP
- # Broadcast gARPs
- msg="1.1 ARP no poisoning dmac=bcast reply sha=bcast"
- run_no_arp_poisoning ${BCAST_MAC} ${BCAST_MAC} ${ARP_REPLY}
- results+="$(print_test_result "${msg}" ${ret})\n"
-
- msg="1.2 ARP no poisoning dmac=bcast reply sha=null"
- run_no_arp_poisoning ${BCAST_MAC} ${NULL_MAC} ${ARP_REPLY}
- results+="$(print_test_result "${msg}" ${ret})\n"
-
- msg="1.3 ARP no poisoning dmac=bcast req sha=bcast"
- run_no_arp_poisoning ${BCAST_MAC} ${BCAST_MAC} ${ARP_REQ}
- results+="$(print_test_result "${msg}" ${ret})\n"
-
- msg="1.4 ARP no poisoning dmac=bcast req sha=null"
- run_no_arp_poisoning ${BCAST_MAC} ${NULL_MAC} ${ARP_REQ}
- results+="$(print_test_result "${msg}" ${ret})\n"
-
- msg="1.5 ARP no poisoning dmac=bcast req sha=mcast"
- run_no_arp_poisoning ${BCAST_MAC} ${MCAST_MAC} ${ARP_REQ}
- results+="$(print_test_result "${msg}" ${ret})\n"
-
- msg="1.6 ARP no poisoning dmac=bcast reply sha=mcast"
- run_no_arp_poisoning ${BCAST_MAC} ${MCAST_MAC} ${ARP_REPLY}
- results+="$(print_test_result "${msg}" ${ret})\n"
-
- # Targeted gARPs
- msg="1.7 ARP no poisoning dmac=veth0 reply sha=bcast"
- run_no_arp_poisoning ${veth1_mac} ${BCAST_MAC} ${ARP_REPLY}
- results+="$(print_test_result "${msg}" ${ret})\n"
-
- msg="1.8 ARP no poisoning dmac=veth0 reply sha=null"
- run_no_arp_poisoning ${veth1_mac} ${NULL_MAC} ${ARP_REPLY}
- results+="$(print_test_result "${msg}" ${ret})\n"
-
- msg="1.9 ARP no poisoning dmac=veth0 req sha=bcast"
- run_no_arp_poisoning ${veth1_mac} ${BCAST_MAC} ${ARP_REQ}
- results+="$(print_test_result "${msg}" ${ret})\n"
-
- msg="1.10 ARP no poisoning dmac=veth0 req sha=null"
- run_no_arp_poisoning ${veth1_mac} ${NULL_MAC} ${ARP_REQ}
- results+="$(print_test_result "${msg}" ${ret})\n"
-
- msg="1.11 ARP no poisoning dmac=veth0 req sha=mcast"
- run_no_arp_poisoning ${veth1_mac} ${MCAST_MAC} ${ARP_REQ}
- results+="$(print_test_result "${msg}" ${ret})\n"
-
- msg="1.12 ARP no poisoning dmac=veth0 reply sha=mcast"
- run_no_arp_poisoning ${veth1_mac} ${MCAST_MAC} ${ARP_REPLY}
- results+="$(print_test_result "${msg}" ${ret})\n"
-
- cleanup
-
- printf '%b' "${results}"
-}
-
-if [ "$(id -u)" -ne 0 ];then
- echo "SKIP: Need root privileges"
- exit $ksft_skip;
-fi
-
-if [ ! -x "$(command -v ip)" ]; then
- echo "SKIP: Could not run test without ip tool"
- exit $ksft_skip
-fi
-
-run_all_tests
-exit $ret
diff --git a/tools/testing/selftests/net/ndisc_send.c b/tools/testing/selftests/net/ndisc_send.c
new file mode 100644
index 000000000000..4f226221d079
--- /dev/null
+++ b/tools/testing/selftests/net/ndisc_send.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <inttypes.h>
+#include <netinet/ether.h>
+#include <arpa/inet.h>
+
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
+
+#define ICMPV6_ND_NS 135
+#define ICMPV6_ND_NA 136
+#define ICMPV6_ND_SLLADR 1
+#define ICMPV6_ND_TLLADR 2
+
+#ifndef __noinline
+#define __noinline __attribute__((noinline))
+#endif
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+struct icmp6_pseudohdr {
+ struct in6_addr saddr;
+ struct in6_addr daddr;
+ uint32_t plen;
+ uint8_t zero[3];
+ uint8_t next;
+};
+
+struct ndisc_pkt {
+ struct ethhdr eth;
+ struct ipv6hdr ip6;
+ struct ndp_hdrs {
+ struct icmp6hdr hdr;
+ struct in6_addr target;
+
+ uint8_t opt_type;
+ uint8_t opt_len;
+ uint8_t opt_mac[ETH_ALEN];
+ } __packed ndp;
+} __packed;
+
+__noinline uint32_t csum_add(void *buf, int len, uint32_t sum)
+{
+ uint16_t *p = (uint16_t *)buf;
+
+ while (len > 1) {
+ sum += *p++;
+ len -= 2;
+ }
+
+ if (len)
+ sum += *(uint8_t *)p;
+
+ return sum;
+}
+
+static uint16_t csum_fold(uint32_t sum)
+{
+ return ~((sum & 0xffff) + (sum >> 16)) ? : 0xffff;
+}
+
+int parse_opts(int argc, char **argv, int *ifindex, struct ndisc_pkt *pkt)
+{
+ struct ether_addr *mac;
+ uint16_t op;
+ struct icmp6_pseudohdr ph = {0};
+ uint32_t sum = 0;
+
+ if (argc != 9) {
+ fprintf(stderr, "Usage: %s <iface> <mac_dst> <mac_src> <dst_ip> <src_ip> <target_ip> <op> <lladr>\n",
+ argv[0]);
+ return -1;
+ }
+
+ *ifindex = atoi(argv[1]);
+ mac = ether_aton(argv[2]);
+ if (!mac) {
+ fprintf(stderr, "Unable to parse mac_dst from '%s'\n", argv[1]);
+ return -1;
+ }
+
+ /* Ethernet */
+ memcpy(pkt->eth.h_dest, mac, ETH_ALEN);
+ mac = ether_aton(argv[3]);
+ if (!mac) {
+ fprintf(stderr, "Unable to parse mac_src from '%s'\n", argv[2]);
+ return -1;
+ }
+ memcpy(pkt->eth.h_source, mac, ETH_ALEN);
+ pkt->eth.h_proto = htons(ETH_P_IPV6);
+
+ /* IPv6 */
+ pkt->ip6.version = 6;
+ pkt->ip6.nexthdr = IPPROTO_ICMPV6;
+ pkt->ip6.hop_limit = 255;
+
+ if (inet_pton(AF_INET6, argv[4], &pkt->ip6.daddr) != 1) {
+ fprintf(stderr, "Unable to parse src_ip from '%s'\n", argv[4]);
+ return -1;
+ }
+ if (inet_pton(AF_INET6, argv[5], &pkt->ip6.saddr) != 1) {
+ fprintf(stderr, "Unable to parse src_ip from '%s'\n", argv[5]);
+ return -1;
+ }
+
+ /* ICMPv6 */
+ op = atoi(argv[7]);
+ if (op != ICMPV6_ND_NS && op != ICMPV6_ND_NA) {
+ fprintf(stderr, "Invalid ICMPv6 op %d\n", op);
+ return -1;
+ }
+
+ pkt->ndp.hdr.icmp6_type = op;
+ pkt->ndp.hdr.icmp6_code = 0;
+
+ if (inet_pton(AF_INET6, argv[6], &pkt->ndp.target) != 1) {
+ fprintf(stderr, "Unable to parse target_ip from '%s'\n",
+ argv[6]);
+ return -1;
+ }
+
+ /* Target/Source Link-Layer Address */
+ if (op == ICMPV6_ND_NS) {
+ pkt->ndp.opt_type = ICMPV6_ND_SLLADR;
+ } else {
+ pkt->ndp.opt_type = ICMPV6_ND_TLLADR;
+ pkt->ndp.hdr.icmp6_override = 1;
+ }
+ pkt->ndp.opt_len = 1;
+
+ mac = ether_aton(argv[8]);
+ if (!mac) {
+ fprintf(stderr, "Invalid lladdr %s\n", argv[8]);
+ return -1;
+ }
+
+ memcpy(pkt->ndp.opt_mac, mac, ETH_ALEN);
+
+ pkt->ip6.payload_len = htons(sizeof(pkt->ndp));
+
+ /* Pseudoheader */
+ ph.saddr = pkt->ip6.saddr;
+ ph.daddr = pkt->ip6.daddr;
+ ph.plen = htonl(sizeof(pkt->ndp));
+ ph.next = IPPROTO_ICMPV6;
+
+ sum = csum_add(&ph, sizeof(ph), 0);
+ sum = csum_add(&pkt->ndp, sizeof(pkt->ndp), sum);
+
+ pkt->ndp.hdr.icmp6_cksum = csum_fold(sum);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int rc, fd;
+ struct sockaddr_ll bind_addr = {0};
+ int ifindex;
+ struct ndisc_pkt pkt = {0};
+
+ if (parse_opts(argc, argv, &ifindex, &pkt) < 0)
+ return -1;
+
+ fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open raw socket(%d). Need root privileges?\n",
+ fd);
+ return 1;
+ }
+
+ bind_addr.sll_family = AF_PACKET;
+ bind_addr.sll_protocol = htons(ETH_P_ALL);
+ bind_addr.sll_ifindex = ifindex;
+
+ rc = bind(fd, (struct sockaddr *)&bind_addr, sizeof(bind_addr));
+ if (rc < 0) {
+ fprintf(stderr, "Unable to bind raw socket(%d). Invalid iface '%d'?\n",
+ rc, ifindex);
+ return 1;
+ }
+
+ rc = send(fd, &pkt, sizeof(pkt), 0);
+ if (rc < 0) {
+ fprintf(stderr, "Unable to send packet: %d\n", rc);
+ return 1;
+ }
+
+ return 0;
+}
--
2.47.3
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH net v2 4/4] selftests/net: add no NDP b/mcast,null poison test
@ 2026-01-27 9:01 kernel test robot
0 siblings, 0 replies; 2+ messages in thread
From: kernel test robot @ 2026-01-27 9:01 UTC (permalink / raw)
To: oe-kbuild; +Cc: lkp
::::::
:::::: Manual check reason: "low confidence bisect report"
::::::
BCC: lkp@intel.com
CC: oe-kbuild-all@lists.linux.dev
In-Reply-To: <606dba34fb962b5ec2694053e1eb080e381dce55.1769464405.git.marcdevel@gmail.com>
References: <606dba34fb962b5ec2694053e1eb080e381dce55.1769464405.git.marcdevel@gmail.com>
TO: "Marc Suñé" <marcdevel@gmail.com>
TO: kuba@kernel.org
TO: willemdebruijn.kernel@gmail.com
TO: pabeni@redhat.com
CC: netdev@vger.kernel.org
CC: dborkman@kernel.org
CC: vadim.fedorenko@linux.dev
CC: "Marc Suñé" <marcdevel@gmail.com>
Hi Marc,
kernel test robot noticed the following build errors:
[auto build test ERROR on net/main]
url: https://github.com/intel-lab-lkp/linux/commits/Marc-Su/arp-discard-invalid-sha-addr-b-mcast-ARP-poison/20260127-075649
base: net/main
patch link: https://lore.kernel.org/r/606dba34fb962b5ec2694053e1eb080e381dce55.1769464405.git.marcdevel%40gmail.com
patch subject: [PATCH net v2 4/4] selftests/net: add no NDP b/mcast,null poison test
:::::: branch date: 9 hours ago
:::::: commit date: 9 hours ago
config: powerpc64-allnoconfig-bpf (https://download.01.org/0day-ci/archive/20260127/202601271016.wlWFeNUI-lkp@intel.com/config)
compiler: powerpc64-linux-gcc (GCC) 15.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260127/202601271016.wlWFeNUI-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/r/202601271016.wlWFeNUI-lkp@intel.com/
All errors (new ones prefixed by >>):
>> ndisc_send.c:4:10: fatal error: stdio.h: No such file or directory
4 | #include <stdio.h>
| ^~~~~~~~~
compilation terminated.
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-01-27 9:02 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-27 9:01 [PATCH net v2 4/4] selftests/net: add no NDP b/mcast,null poison test kernel test robot
-- strict thread matches above, loose matches on Subject: below --
2026-01-26 23:53 [PATCH net v2 0/4] discard ARP/NDP b/mcast/null announce (poison) Marc Suñé
2026-01-26 23:53 ` [PATCH net v2 4/4] selftests/net: add no NDP b/mcast,null poison test Marc Suñé
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.