#!/bin/bash # SPDX-License-Identifier: GPL-2.0 # 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 ret=0 # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 PAUSE_ON_FAIL=no PAUSE=no HOST_NS="ns-host" ROUTER_NS="ns-router" HOST_INTF="veth-host" ROUTER_INTF="veth-router" ROUTER_ADDR="2000:20::1" HOST_ADDR="2000:20::2" SUBNET_WIDTH=64 ROUTER_ADDR_WITH_MASK="${ROUTER_ADDR}/${SUBNET_WIDTH}" HOST_ADDR_WITH_MASK="${HOST_ADDR}/${SUBNET_WIDTH}" IP_HOST="ip -6 -netns ${HOST_NS}" IP_HOST_EXEC="ip netns exec ${HOST_NS}" IP_ROUTER="ip -6 -netns ${ROUTER_NS}" IP_ROUTER_EXEC="ip netns exec ${ROUTER_NS}" tcpdump_stdout= tcpdump_stderr= log_test() { local rc=$1 local expected=$2 local msg="$3" if [ ${rc} -eq ${expected} ]; then printf " TEST: %-60s [ OK ]\n" "${msg}" nsuccess=$((nsuccess+1)) else ret=1 nfail=$((nfail+1)) printf " TEST: %-60s [FAIL]\n" "${msg}" if [ "${PAUSE_ON_FAIL}" = "yes" ]; then echo echo "hit enter to continue, 'q' to quit" read a [ "$a" = "q" ] && exit 1 fi fi if [ "${PAUSE}" = "yes" ]; then echo echo "hit enter to continue, 'q' to quit" read a [ "$a" = "q" ] && exit 1 fi } setup() { set -e local drop_unsolicited_na=$1 local accept_untracked_na=$2 local forwarding=$3 # Setup two namespaces and a veth tunnel across them. # On end of the tunnel is a router and the other end is a host. ip netns add ${HOST_NS} ip netns add ${ROUTER_NS} ${IP_ROUTER} link add ${ROUTER_INTF} type veth \ peer name ${HOST_INTF} netns ${HOST_NS} # Enable IPv6 on both router and host, and configure static addresses. # The router here is the DUT # Setup router configuration as specified by the arguments. # forwarding=0 case is to check that a non-router # doesn't add neighbour entries. ROUTER_CONF=net.ipv6.conf.${ROUTER_INTF} ${IP_ROUTER_EXEC} sysctl -qw \ ${ROUTER_CONF}.forwarding=${forwarding} ${IP_ROUTER_EXEC} sysctl -qw \ ${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}.disable_ipv6=0 ${IP_ROUTER} addr add ${ROUTER_ADDR_WITH_MASK} dev ${ROUTER_INTF} # Turn on ndisc_notify on host interface so that # the host sends unsolicited NAs. HOST_CONF=net.ipv6.conf.${HOST_INTF} ${IP_HOST_EXEC} sysctl -qw ${HOST_CONF}.ndisc_notify=1 ${IP_HOST_EXEC} sysctl -qw ${HOST_CONF}.disable_ipv6=0 ${IP_HOST} addr add ${HOST_ADDR_WITH_MASK} dev ${HOST_INTF} set +e } start_tcpdump() { set -e tcpdump_stdout=`mktemp` tcpdump_stderr=`mktemp` ${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 } cleanup_tcpdump() { set -e [[ ! -z ${tcpdump_stdout} ]] && rm -f ${tcpdump_stdout} [[ ! -z ${tcpdump_stderr} ]] && rm -f ${tcpdump_stderr} tcpdump_stdout= tcpdump_stderr= set +e } cleanup() { cleanup_tcpdump ip netns del ${HOST_NS} ip netns del ${ROUTER_NS} } link_up() { set -e ${IP_ROUTER} link set dev ${ROUTER_INTF} 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 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} ]] else # Neighbour entry expected to be absent for all other cases [[ -z ${neigh_show_output} ]] fi } test_unsolicited_na_common() { # Setup the test bed, but keep links down setup $1 $2 $3 # Bring the link up, wait for the NA, # and add a delay to ensure neighbour processing is done. link_up start_tcpdump # Verify the neighbour table verify_ndisc $1 $2 $3 } test_unsolicited_na_combination() { test_unsolicited_na_common $1 $2 $3 test_msg=("test_unsolicited_na: " "drop_unsolicited_na=$1 " "accept_untracked_na=$2 " "forwarding=$3") log_test $? 0 "${test_msg[*]}" cleanup } test_unsolicited_na_combinations() { # Args: drop_unsolicited_na accept_untracked_na forwarding # Expect entry test_unsolicited_na_combination 0 1 1 # Expect no entry test_unsolicited_na_combination 0 0 0 test_unsolicited_na_combination 0 0 1 test_unsolicited_na_combination 0 1 0 test_unsolicited_na_combination 1 0 0 test_unsolicited_na_combination 1 0 1 test_unsolicited_na_combination 1 1 0 test_unsolicited_na_combination 1 1 1 } ############################################################################### # usage usage() { cat <<EOF usage: ${0##*/} OPTS -p Pause on fail -P Pause after each test before cleanup EOF } ############################################################################### # main while getopts :pPh o do case $o in p) PAUSE_ON_FAIL=yes;; P) PAUSE=yes;; h) usage; exit 0;; *) usage; exit 1;; esac done # make sure we don't pause twice [ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no 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 if [ ! -x "$(command -v tcpdump)" ]; then echo "SKIP: Could not run test without tcpdump tool" exit $ksft_skip fi # start clean cleanup &> /dev/null test_unsolicited_na_combinations printf "\nTests passed: %3d\n" ${nsuccess} printf "Tests failed: %3d\n" ${nfail} exit $ret