[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <953ccac617385774c73cbe99512ee84d742c9c58.1770241104.git.marcdevel@gmail.com>
Date: Wed, 4 Feb 2026 23:12:01 +0100
From: Marc Suñé <marcdevel@...il.com>
To: kuba@...nel.org,
willemdebruijn.kernel@...il.com,
pabeni@...hat.com
Cc: netdev@...r.kernel.org,
dborkman@...nel.org,
vadim.fedorenko@...ux.dev,
Marc Suñé <marcdevel@...il.com>
Subject: [PATCH net-next v3 4/4] selftests/net: add no NDP b/mcast,null poison test
Add a selftest to test that NDP bcast/mcast/null poisioning checks are
never bypassed.
Signed-off-by: Marc Suñé <marcdevel@...il.com>
---
tools/testing/selftests/net/.gitignore | 1 +
tools/testing/selftests/net/Makefile | 2 +
.../net/arp_ndisc_no_invalid_sha_poison.sh | 368 ++++++++++++++++++
.../net/arp_no_invalid_sha_poision.sh | 173 --------
tools/testing/selftests/net/ndisc_send.c | 198 ++++++++++
5 files changed, 569 insertions(+), 173 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 6930fe926c58..ffe522e36d39 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -17,6 +17,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 64bfbb29a427..5ccea6fc35a8 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -10,6 +10,7 @@ TEST_PROGS := \
altnames.sh \
amt.sh \
arp_ndisc_evict_nocarrier.sh \
+ arp_ndisc_no_invalid_sha_poison.sh \
arp_ndisc_untracked_subnets.sh \
arp_no_invalid_sha_poision.sh \
bareudp.sh \
@@ -169,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..ad91eac18c89
--- /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=request
+readonly ARP_REPLY=reply
+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
+ mausezahn "veth0" -q -a "${VALID_MAC}" -b "${BCAST_MAC}" -t arp \
+ "${op}, sip=${V4_ADDR0}, tip=${V4_ADDR0}, smac=${VALID_MAC}, tmac=${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
+ mausezahn "veth0" -q -a "${VALID_MAC}" -b "${l2_dmac}" -t arp \
+ "${op}, sip=${V4_ADDR0}, tip=${V4_ADDR0}, smac=${tmac}, tmac=${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 755dd31212c8..000000000000
--- a/tools/testing/selftests/net/arp_no_invalid_sha_poision.sh
+++ /dev/null
@@ -1,173 +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="request"
-readonly ARP_REPLY="reply"
-ret=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
-
- 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
- mausezahn "veth0" -q -a "${VALID_MAC}" -b "${BCAST_MAC}" -t arp \
- "${op}, sip=${V4_ADDR0}, tip=${V4_ADDR0}, smac=${VALID_MAC}, tmac=${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
- mausezahn "veth0" -q -a "${VALID_MAC}" -b "${l2_dmac}" -t arp \
- "${op}, sip=${V4_ADDR0}, tip=${V4_ADDR0}, smac=${tmac}, tmac=${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
Powered by blists - more mailing lists