1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# 4# 2 namespaces: one host and one router. Use arping from the host to send a 5# garp to the router. Router accepts or ignores based on its arp_accept 6# or accept_untracked_na configuration. 7 8source lib.sh 9 10TESTS="arp ndisc" 11 12ROUTER_INTF="veth-router" 13ROUTER_ADDR="10.0.10.1" 14ROUTER_ADDR_V6="2001:db8:abcd:0012::1" 15 16HOST_INTF="veth-host" 17HOST_ADDR="10.0.10.2" 18HOST_ADDR_V6="2001:db8:abcd:0012::2" 19 20SUBNET_WIDTH=24 21PREFIX_WIDTH_V6=64 22 23cleanup() { 24 cleanup_ns ${HOST_NS} ${ROUTER_NS} 25} 26 27cleanup_v6() { 28 cleanup_ns ${HOST_NS_V6} ${ROUTER_NS_V6} 29} 30 31setup() { 32 set -e 33 local arp_accept=$1 34 35 # Set up two namespaces 36 setup_ns HOST_NS ROUTER_NS 37 38 # Set up interfaces veth0 and veth1, which are pairs in separate 39 # namespaces. veth0 is veth-router, veth1 is veth-host. 40 # first, set up the inteface's link to the namespace 41 # then, set the interface "up" 42 ip netns exec ${ROUTER_NS} ip link add name ${ROUTER_INTF} \ 43 type veth peer name ${HOST_INTF} 44 45 ip netns exec ${ROUTER_NS} ip link set dev ${ROUTER_INTF} up 46 ip netns exec ${ROUTER_NS} ip link set dev ${HOST_INTF} netns ${HOST_NS} 47 48 ip netns exec ${HOST_NS} ip link set dev ${HOST_INTF} up 49 ip netns exec ${ROUTER_NS} ip addr add ${ROUTER_ADDR}/${SUBNET_WIDTH} \ 50 dev ${ROUTER_INTF} 51 52 ip netns exec ${HOST_NS} ip addr add ${HOST_ADDR}/${SUBNET_WIDTH} \ 53 dev ${HOST_INTF} 54 ip netns exec ${HOST_NS} ip route add default via ${HOST_ADDR} \ 55 dev ${HOST_INTF} 56 ip netns exec ${ROUTER_NS} ip route add default via ${ROUTER_ADDR} \ 57 dev ${ROUTER_INTF} 58 59 ROUTER_CONF=net.ipv4.conf.${ROUTER_INTF} 60 ip netns exec ${ROUTER_NS} sysctl -w \ 61 ${ROUTER_CONF}.arp_accept=${arp_accept} >/dev/null 2>&1 62 set +e 63} 64 65setup_v6() { 66 set -e 67 local accept_untracked_na=$1 68 69 # Set up two namespaces 70 setup_ns HOST_NS_V6 ROUTER_NS_V6 71 72 # Set up interfaces veth0 and veth1, which are pairs in separate 73 # namespaces. veth0 is veth-router, veth1 is veth-host. 74 # first, set up the inteface's link to the namespace 75 # then, set the interface "up" 76 ip -6 -netns ${ROUTER_NS_V6} link add name ${ROUTER_INTF} \ 77 type veth peer name ${HOST_INTF} 78 79 ip -6 -netns ${ROUTER_NS_V6} link set dev ${ROUTER_INTF} up 80 ip -6 -netns ${ROUTER_NS_V6} link set dev ${HOST_INTF} netns \ 81 ${HOST_NS_V6} 82 83 ip -6 -netns ${HOST_NS_V6} link set dev ${HOST_INTF} up 84 ip -6 -netns ${ROUTER_NS_V6} addr add \ 85 ${ROUTER_ADDR_V6}/${PREFIX_WIDTH_V6} dev ${ROUTER_INTF} nodad 86 87 HOST_CONF=net.ipv6.conf.${HOST_INTF} 88 ip netns exec ${HOST_NS_V6} sysctl -qw ${HOST_CONF}.ndisc_notify=1 89 ip netns exec ${HOST_NS_V6} sysctl -qw ${HOST_CONF}.disable_ipv6=0 90 ip -6 -netns ${HOST_NS_V6} addr add ${HOST_ADDR_V6}/${PREFIX_WIDTH_V6} \ 91 dev ${HOST_INTF} 92 93 ROUTER_CONF=net.ipv6.conf.${ROUTER_INTF} 94 95 ip netns exec ${ROUTER_NS_V6} sysctl -w \ 96 ${ROUTER_CONF}.forwarding=1 >/dev/null 2>&1 97 ip netns exec ${ROUTER_NS_V6} sysctl -w \ 98 ${ROUTER_CONF}.drop_unsolicited_na=0 >/dev/null 2>&1 99 ip netns exec ${ROUTER_NS_V6} sysctl -w \ 100 ${ROUTER_CONF}.accept_untracked_na=${accept_untracked_na} \ 101 >/dev/null 2>&1 102 set +e 103} 104 105verify_arp() { 106 local arp_accept=$1 107 local same_subnet=$2 108 109 neigh_show_output=$(ip netns exec ${ROUTER_NS} ip neigh get \ 110 ${HOST_ADDR} dev ${ROUTER_INTF} 2>/dev/null) 111 112 if [ ${arp_accept} -eq 1 ]; then 113 # Neighbor entries expected 114 [[ ${neigh_show_output} ]] 115 elif [ ${arp_accept} -eq 2 ]; then 116 if [ ${same_subnet} -eq 1 ]; then 117 # Neighbor entries expected 118 [[ ${neigh_show_output} ]] 119 else 120 [[ -z "${neigh_show_output}" ]] 121 fi 122 else 123 [[ -z "${neigh_show_output}" ]] 124 fi 125 } 126 127arp_test_gratuitous() { 128 set -e 129 local arp_accept=$1 130 local same_subnet=$2 131 132 if [ ${arp_accept} -eq 2 ]; then 133 test_msg=("test_arp: " 134 "accept_arp=$1 " 135 "same_subnet=$2") 136 if [ ${same_subnet} -eq 0 ]; then 137 HOST_ADDR=10.0.11.3 138 else 139 HOST_ADDR=10.0.10.3 140 fi 141 else 142 test_msg=("test_arp: " 143 "accept_arp=$1") 144 fi 145 # Supply arp_accept option to set up which sets it in sysctl 146 setup ${arp_accept} 147 ip netns exec ${HOST_NS} arping -A -I ${HOST_INTF} -U ${HOST_ADDR} -c1 2>&1 >/dev/null 148 149 if verify_arp $1 $2; then 150 printf " TEST: %-60s [ OK ]\n" "${test_msg[*]}" 151 else 152 printf " TEST: %-60s [FAIL]\n" "${test_msg[*]}" 153 fi 154 cleanup 155 set +e 156} 157 158arp_test_gratuitous_combinations() { 159 arp_test_gratuitous 0 160 arp_test_gratuitous 1 161 arp_test_gratuitous 2 0 # Second entry indicates subnet or not 162 arp_test_gratuitous 2 1 163} 164 165cleanup_tcpdump() { 166 set -e 167 [[ ! -z ${tcpdump_stdout} ]] && rm -f ${tcpdump_stdout} 168 [[ ! -z ${tcpdump_stderr} ]] && rm -f ${tcpdump_stderr} 169 tcpdump_stdout= 170 tcpdump_stderr= 171 set +e 172} 173 174start_tcpdump() { 175 set -e 176 tcpdump_stdout=`mktemp` 177 tcpdump_stderr=`mktemp` 178 ip netns exec ${ROUTER_NS_V6} timeout 15s \ 179 tcpdump --immediate-mode -tpni ${ROUTER_INTF} -c 1 \ 180 "icmp6 && icmp6[0] == 136 && src ${HOST_ADDR_V6}" \ 181 > ${tcpdump_stdout} 2> /dev/null 182 set +e 183} 184 185verify_ndisc() { 186 local accept_untracked_na=$1 187 local same_subnet=$2 188 189 neigh_show_output=$(ip -6 -netns ${ROUTER_NS_V6} neigh show \ 190 to ${HOST_ADDR_V6} dev ${ROUTER_INTF} nud stale) 191 192 if [ ${accept_untracked_na} -eq 1 ]; then 193 # Neighbour entry expected to be present 194 [[ ${neigh_show_output} ]] 195 elif [ ${accept_untracked_na} -eq 2 ]; then 196 if [ ${same_subnet} -eq 1 ]; then 197 [[ ${neigh_show_output} ]] 198 else 199 [[ -z "${neigh_show_output}" ]] 200 fi 201 else 202 # Neighbour entry expected to be absent for all other cases 203 [[ -z "${neigh_show_output}" ]] 204 fi 205} 206 207ndisc_test_untracked_advertisements() { 208 set -e 209 test_msg=("test_ndisc: " 210 "accept_untracked_na=$1") 211 212 local accept_untracked_na=$1 213 local same_subnet=$2 214 if [ ${accept_untracked_na} -eq 2 ]; then 215 test_msg=("test_ndisc: " 216 "accept_untracked_na=$1 " 217 "same_subnet=$2") 218 if [ ${same_subnet} -eq 0 ]; then 219 # Not same subnet 220 HOST_ADDR_V6=2000:db8:abcd:0013::4 221 else 222 HOST_ADDR_V6=2001:db8:abcd:0012::3 223 fi 224 fi 225 setup_v6 $1 $2 226 start_tcpdump 227 228 if verify_ndisc $1 $2; then 229 printf " TEST: %-60s [ OK ]\n" "${test_msg[*]}" 230 else 231 printf " TEST: %-60s [FAIL]\n" "${test_msg[*]}" 232 fi 233 234 cleanup_tcpdump 235 cleanup_v6 236 set +e 237} 238 239ndisc_test_untracked_combinations() { 240 ndisc_test_untracked_advertisements 0 241 ndisc_test_untracked_advertisements 1 242 ndisc_test_untracked_advertisements 2 0 243 ndisc_test_untracked_advertisements 2 1 244} 245 246################################################################################ 247# usage 248 249usage() 250{ 251 cat <<EOF 252usage: ${0##*/} OPTS 253 254 -t <test> Test(s) to run (default: all) 255 (options: $TESTS) 256EOF 257} 258 259################################################################################ 260# main 261 262while getopts ":t:h" opt; do 263 case $opt in 264 t) TESTS=$OPTARG;; 265 h) usage; exit 0;; 266 *) usage; exit 1;; 267 esac 268done 269 270if [ "$(id -u)" -ne 0 ];then 271 echo "SKIP: Need root privileges" 272 exit $ksft_skip; 273fi 274 275if [ ! -x "$(command -v ip)" ]; then 276 echo "SKIP: Could not run test without ip tool" 277 exit $ksft_skip 278fi 279 280if [ ! -x "$(command -v tcpdump)" ]; then 281 echo "SKIP: Could not run test without tcpdump tool" 282 exit $ksft_skip 283fi 284 285if [ ! -x "$(command -v arping)" ]; then 286 echo "SKIP: Could not run test without arping tool" 287 exit $ksft_skip 288fi 289 290# start clean 291cleanup &> /dev/null 292cleanup_v6 &> /dev/null 293 294for t in $TESTS 295do 296 case $t in 297 arp_test_gratuitous_combinations|arp) arp_test_gratuitous_combinations;; 298 ndisc_test_untracked_combinations|ndisc) \ 299 ndisc_test_untracked_combinations;; 300 help) echo "Test names: $TESTS"; exit 0;; 301esac 302done 303