1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# This test is for the accept_unsolicited_na feature to
5# enable RFC9131 behaviour. The following is the test-matrix.
6# drop   accept  fwding                   behaviour
7# ----   ------  ------  ----------------------------------------------
8#    1        X       X  Drop NA packet and don't pass up the stack
9#    0        0       X  Pass NA packet up the stack, don't update NC
10#    0        1       0  Pass NA packet up the stack, don't update NC
11#    0        1       1  Pass NA packet up the stack, and add a STALE
12#                           NC entry
13
14ret=0
15# Kselftest framework requirement - SKIP code is 4.
16ksft_skip=4
17
18PAUSE_ON_FAIL=no
19PAUSE=no
20
21HOST_NS="ns-host"
22ROUTER_NS="ns-router"
23
24HOST_INTF="veth-host"
25ROUTER_INTF="veth-router"
26
27ROUTER_ADDR="2000:20::1"
28HOST_ADDR="2000:20::2"
29SUBNET_WIDTH=64
30ROUTER_ADDR_WITH_MASK="${ROUTER_ADDR}/${SUBNET_WIDTH}"
31HOST_ADDR_WITH_MASK="${HOST_ADDR}/${SUBNET_WIDTH}"
32
33IP_HOST="ip -6 -netns ${HOST_NS}"
34IP_HOST_EXEC="ip netns exec ${HOST_NS}"
35IP_ROUTER="ip -6 -netns ${ROUTER_NS}"
36IP_ROUTER_EXEC="ip netns exec ${ROUTER_NS}"
37
38tcpdump_stdout=
39tcpdump_stderr=
40
41log_test()
42{
43	local rc=$1
44	local expected=$2
45	local msg="$3"
46
47	if [ ${rc} -eq ${expected} ]; then
48		printf "    TEST: %-60s  [ OK ]\n" "${msg}"
49		nsuccess=$((nsuccess+1))
50	else
51		ret=1
52		nfail=$((nfail+1))
53		printf "    TEST: %-60s  [FAIL]\n" "${msg}"
54		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
55		echo
56			echo "hit enter to continue, 'q' to quit"
57			read a
58			[ "$a" = "q" ] && exit 1
59		fi
60	fi
61
62	if [ "${PAUSE}" = "yes" ]; then
63		echo
64		echo "hit enter to continue, 'q' to quit"
65		read a
66		[ "$a" = "q" ] && exit 1
67	fi
68}
69
70setup()
71{
72	set -e
73
74	local drop_unsolicited_na=$1
75	local accept_unsolicited_na=$2
76	local forwarding=$3
77
78	# Setup two namespaces and a veth tunnel across them.
79	# On end of the tunnel is a router and the other end is a host.
80	ip netns add ${HOST_NS}
81	ip netns add ${ROUTER_NS}
82	${IP_ROUTER} link add ${ROUTER_INTF} type veth \
83                peer name ${HOST_INTF} netns ${HOST_NS}
84
85	# Enable IPv6 on both router and host, and configure static addresses.
86	# The router here is the DUT
87	# Setup router configuration as specified by the arguments.
88	# forwarding=0 case is to check that a non-router
89	# doesn't add neighbour entries.
90        ROUTER_CONF=net.ipv6.conf.${ROUTER_INTF}
91	${IP_ROUTER_EXEC} sysctl -qw \
92                ${ROUTER_CONF}.forwarding=${forwarding}
93	${IP_ROUTER_EXEC} sysctl -qw \
94                ${ROUTER_CONF}.drop_unsolicited_na=${drop_unsolicited_na}
95	${IP_ROUTER_EXEC} sysctl -qw \
96                ${ROUTER_CONF}.accept_unsolicited_na=${accept_unsolicited_na}
97	${IP_ROUTER_EXEC} sysctl -qw ${ROUTER_CONF}.disable_ipv6=0
98	${IP_ROUTER} addr add ${ROUTER_ADDR_WITH_MASK} dev ${ROUTER_INTF}
99
100	# Turn on ndisc_notify on host interface so that
101	# the host sends unsolicited NAs.
102	HOST_CONF=net.ipv6.conf.${HOST_INTF}
103	${IP_HOST_EXEC} sysctl -qw ${HOST_CONF}.ndisc_notify=1
104	${IP_HOST_EXEC} sysctl -qw ${HOST_CONF}.disable_ipv6=0
105	${IP_HOST} addr add ${HOST_ADDR_WITH_MASK} dev ${HOST_INTF}
106
107	set +e
108}
109
110start_tcpdump() {
111	set -e
112	tcpdump_stdout=`mktemp`
113	tcpdump_stderr=`mktemp`
114	${IP_ROUTER_EXEC} timeout 15s \
115                tcpdump --immediate-mode -tpni ${ROUTER_INTF} -c 1 \
116                "icmp6 && icmp6[0] == 136 && src ${HOST_ADDR}" \
117                > ${tcpdump_stdout} 2> /dev/null
118	set +e
119}
120
121cleanup_tcpdump()
122{
123	set -e
124	[[ ! -z  ${tcpdump_stdout} ]] && rm -f ${tcpdump_stdout}
125	[[ ! -z  ${tcpdump_stderr} ]] && rm -f ${tcpdump_stderr}
126	tcpdump_stdout=
127	tcpdump_stderr=
128	set +e
129}
130
131cleanup()
132{
133	cleanup_tcpdump
134	ip netns del ${HOST_NS}
135	ip netns del ${ROUTER_NS}
136}
137
138link_up() {
139	set -e
140	${IP_ROUTER} link set dev ${ROUTER_INTF} up
141	${IP_HOST} link set dev ${HOST_INTF} up
142	set +e
143}
144
145verify_ndisc() {
146	local drop_unsolicited_na=$1
147	local accept_unsolicited_na=$2
148	local forwarding=$3
149
150	neigh_show_output=$(${IP_ROUTER} neigh show \
151                to ${HOST_ADDR} dev ${ROUTER_INTF} nud stale)
152	if [ ${drop_unsolicited_na} -eq 0 ] && \
153			[ ${accept_unsolicited_na} -eq 1 ] && \
154			[ ${forwarding} -eq 1 ]; then
155		# Neighbour entry expected to be present for 011 case
156		[[ ${neigh_show_output} ]]
157	else
158		# Neighbour entry expected to be absent for all other cases
159		[[ -z ${neigh_show_output} ]]
160	fi
161}
162
163test_unsolicited_na_common()
164{
165	# Setup the test bed, but keep links down
166	setup $1 $2 $3
167
168	# Bring the link up, wait for the NA,
169	# and add a delay to ensure neighbour processing is done.
170	link_up
171	start_tcpdump
172
173	# Verify the neighbour table
174	verify_ndisc $1 $2 $3
175
176}
177
178test_unsolicited_na_combination() {
179	test_unsolicited_na_common $1 $2 $3
180	test_msg=("test_unsolicited_na: "
181		"drop_unsolicited_na=$1 "
182		"accept_unsolicited_na=$2 "
183		"forwarding=$3")
184	log_test $? 0 "${test_msg[*]}"
185	cleanup
186}
187
188test_unsolicited_na_combinations() {
189	# Args: drop_unsolicited_na accept_unsolicited_na forwarding
190
191	# Expect entry
192	test_unsolicited_na_combination 0 1 1
193
194	# Expect no entry
195	test_unsolicited_na_combination 0 0 0
196	test_unsolicited_na_combination 0 0 1
197	test_unsolicited_na_combination 0 1 0
198	test_unsolicited_na_combination 1 0 0
199	test_unsolicited_na_combination 1 0 1
200	test_unsolicited_na_combination 1 1 0
201	test_unsolicited_na_combination 1 1 1
202}
203
204###############################################################################
205# usage
206
207usage()
208{
209	cat <<EOF
210usage: ${0##*/} OPTS
211        -p          Pause on fail
212        -P          Pause after each test before cleanup
213EOF
214}
215
216###############################################################################
217# main
218
219while getopts :pPh o
220do
221	case $o in
222		p) PAUSE_ON_FAIL=yes;;
223		P) PAUSE=yes;;
224		h) usage; exit 0;;
225		*) usage; exit 1;;
226	esac
227done
228
229# make sure we don't pause twice
230[ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no
231
232if [ "$(id -u)" -ne 0 ];then
233	echo "SKIP: Need root privileges"
234	exit $ksft_skip;
235fi
236
237if [ ! -x "$(command -v ip)" ]; then
238	echo "SKIP: Could not run test without ip tool"
239	exit $ksft_skip
240fi
241
242if [ ! -x "$(command -v tcpdump)" ]; then
243	echo "SKIP: Could not run test without tcpdump tool"
244	exit $ksft_skip
245fi
246
247# start clean
248cleanup &> /dev/null
249
250test_unsolicited_na_combinations
251
252printf "\nTests passed: %3d\n" ${nsuccess}
253printf "Tests failed: %3d\n"   ${nfail}
254
255exit $ret
256