Add a selftest to test that NDP bcast/mcast/null poisioning checks are never bypassed. Signed-off-by: Marc Suñé --- 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 \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