fibs_test.sh revision 263779
1#
2#  Copyright (c) 2014 Spectra Logic Corporation
3#  All rights reserved.
4# 
5#  Redistribution and use in source and binary forms, with or without
6#  modification, are permitted provided that the following conditions
7#  are met:
8#  1. Redistributions of source code must retain the above copyright
9#     notice, this list of conditions, and the following disclaimer,
10#     without modification.
11#  2. Redistributions in binary form must reproduce at minimum a disclaimer
12#     substantially similar to the "NO WARRANTY" disclaimer below
13#     ("Disclaimer") and any redistribution must be conditioned upon
14#     including a substantially similar Disclaimer requirement for further
15#     binary redistribution.
16# 
17#  NO WARRANTY
18#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21#  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22#  HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24#  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26#  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27#  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28#  POSSIBILITY OF SUCH DAMAGES.
29# 
30#  Authors: Alan Somers         (Spectra Logic Corporation)
31#
32# $FreeBSD: head/tests/sys/netinet/fibs_test.sh 263779 2014-03-26 22:46:03Z asomers $
33
34# All of the tests in this file requires the test-suite config variable "fibs"
35# to be defined to a space-delimited list of FIBs that may be used for testing.
36
37# arpresolve should check the interface fib for routes to a target when
38# creating an ARP table entry.  This is a regression for kern/167947, where
39# arpresolve only checked the default route.
40#
41# Outline:
42# Create two tap(4) interfaces
43# Simulate a crossover cable between them by using net/socat
44# Use nping (from security/nmap) to send an ICMP echo request from one
45# interface to the other, spoofing the source IP.  The source IP must be
46# spoofed, or else it will already have an entry in the arp table. 
47# Check whether an arp entry exists for the spoofed IP
48atf_test_case arpresolve_checks_interface_fib cleanup
49arpresolve_checks_interface_fib_head()
50{
51	atf_set "descr" "arpresolve should check the interface fib, not the default fib, for routes"
52	atf_set "require.user" "root"
53	atf_set "require.config" "fibs"
54	atf_set "require.progs" "socat nping"
55}
56arpresolve_checks_interface_fib_body()
57{
58	# Configure the TAP interfaces to use a RFC5737 nonrouteable addresses
59	# and a non-default fib
60	ADDR0="192.0.2.2"
61	ADDR1="192.0.2.3"
62	SUBNET="192.0.2.0"
63	# Due to bug TBD (regressed by multiple_fibs_on_same_subnet) we need
64	# diffferent subnet masks, or FIB1 won't have a subnet route.
65	MASK0="24"
66	MASK1="25"
67	# Spoof a MAC that is reserved per RFC7042
68	SPOOF_ADDR="192.0.2.4"
69	SPOOF_MAC="00:00:5E:00:53:00"
70
71	# Check system configuration
72	if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
73		atf_skip "This test requires net.add_addr_allfibs=0"
74	fi
75	get_fibs 2
76
77	# Configure TAP interfaces
78	setup_tap "$FIB0" ${ADDR0} ${MASK0}
79	TAP0=$TAP
80	setup_tap "$FIB1" ${ADDR1} ${MASK1}
81	TAP1=$TAP
82
83	# Simulate a crossover cable
84	socat /dev/${TAP0} /dev/${TAP1} &
85	SOCAT_PID=$!
86	echo ${SOCAT_PID} >> "processes_to_kill"
87	
88	# Send an ICMP echo request with a spoofed source IP
89	setfib 2 nping -c 1 -e ${TAP0} -S ${SPOOF_ADDR} \
90		--source-mac ${SPOOF_MAC} --icmp --icmp-type "echo-request" \
91		--icmp-code 0 --icmp-id 0xdead --icmp-seq 1 --data 0xbeef \
92		${ADDR1}
93	# For informational and debugging purposes only, look for the
94	# characteristic error message
95	dmesg | grep "llinfo.*${SPOOF_ADDR}"
96	# Check that the ARP entry exists
97	atf_check -o match:"${SPOOF_ADDR}.*expires" setfib 3 arp ${SPOOF_ADDR}
98}
99arpresolve_checks_interface_fib_cleanup()
100{
101	for PID in `cat "processes_to_kill"`; do
102		kill $PID
103	done
104	cleanup_tap
105}
106
107
108# Regression test for kern/187549
109atf_test_case loopback_and_network_routes_on_nondefault_fib cleanup
110loopback_and_network_routes_on_nondefault_fib_head()
111{
112	atf_set "descr" "When creating and deleting loopback routes, use the interface's fib"
113	atf_set "require.user" "root"
114	atf_set "require.config" "fibs"
115}
116
117loopback_and_network_routes_on_nondefault_fib_body()
118{
119	atf_expect_fail "kern/187549 Host and network routes for a new interface appear in the wrong FIB"
120	# Configure the TAP interface to use an RFC5737 nonrouteable address
121	# and a non-default fib
122	ADDR="192.0.2.2"
123	SUBNET="192.0.2.0"
124	MASK="24"
125
126	# Check system configuration
127	if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
128		atf_skip "This test requires net.add_addr_allfibs=0"
129	fi
130	get_fibs 1
131
132	# Configure a TAP interface
133	setup_tap ${FIB0} ${ADDR} ${MASK}
134
135	# Check whether the host route exists in only the correct FIB
136	setfib ${FIB0} netstat -rn -f inet | grep -q "^${ADDR}.*UHS.*lo0"
137	if [ 0 -ne $? ]; then
138		setfib ${FIB0} netstat -rn -f inet
139		atf_fail "Host route did not appear in the correct FIB"
140	fi
141	setfib 0 netstat -rn -f inet | grep -q "^${ADDR}.*UHS.*lo0"
142	if [ 0 -eq $? ]; then
143		setfib 0 netstat -rn -f inet
144		atf_fail "Host route appeared in the wrong FIB"
145	fi
146
147	# Check whether the network route exists in only the correct FIB
148	setfib ${FIB0} netstat -rn -f inet | \
149		grep -q "^${SUBNET}/${MASK}.*${TAPD}"
150	if [ 0 -ne $? ]; then
151		setfib ${FIB0} netstat -rn -f inet
152		atf_fail "Network route did not appear in the correct FIB"
153	fi
154	setfib 0 netstat -rn -f inet | \
155		grep -q "^${SUBNET}/${MASK}.*${TAPD}"
156	if [ 0 -eq $? ]; then
157		setfib ${FIB0} netstat -rn -f inet
158		atf_fail "Network route appeared in the wrong FIB"
159	fi
160}
161
162loopback_and_network_routes_on_nondefault_fib_cleanup()
163{
164	cleanup_tap
165}
166
167
168# Regression test for kern/187552
169atf_test_case default_route_with_multiple_fibs_on_same_subnet cleanup
170default_route_with_multiple_fibs_on_same_subnet_head()
171{
172	atf_set "descr" "Multiple interfaces on the same subnet but with different fibs can both have default routes"
173	atf_set "require.user" "root"
174	atf_set "require.config" "fibs"
175}
176
177default_route_with_multiple_fibs_on_same_subnet_body()
178{
179	atf_expect_fail "kern/187552 default route uses the wrong interface when multiple interfaces have the same subnet but different fibs"
180	# Configure the TAP interfaces to use a RFC5737 nonrouteable addresses
181	# and a non-default fib
182	ADDR0="192.0.2.2"
183	ADDR1="192.0.2.3"
184	GATEWAY="192.0.2.1"
185	SUBNET="192.0.2.0"
186	MASK="24"
187
188	# Check system configuration
189	if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
190		atf_skip "This test requires net.add_addr_allfibs=0"
191	fi
192	get_fibs 2
193
194	# Configure TAP interfaces
195	setup_tap "$FIB0" ${ADDR0} ${MASK}
196	TAP0=$TAP
197	setup_tap "$FIB1" ${ADDR1} ${MASK}
198	TAP1=$TAP
199
200	# Attempt to add default routes
201	setfib ${FIB0} route add default ${GATEWAY}
202	setfib ${FIB1} route add default ${GATEWAY}
203
204	# Verify that the default route exists for both fibs, with their
205	# respective interfaces.
206	atf_check -o match:"^default.*${TAP0}$" \
207		setfib ${FIB0} netstat -rn -f inet
208	atf_check -o match:"^default.*${TAP1}$" \
209		setfib ${FIB1} netstat -rn -f inet
210}
211
212default_route_with_multiple_fibs_on_same_subnet_cleanup()
213{
214	cleanup_tap
215}
216
217
218# Regression test for kern/187550
219atf_test_case subnet_route_with_multiple_fibs_on_same_subnet cleanup
220subnet_route_with_multiple_fibs_on_same_subnet_head()
221{
222	atf_set "descr" "Multiple FIBs can have subnet routes for the same subnet"
223	atf_set "require.user" "root"
224	atf_set "require.config" "fibs"
225}
226
227subnet_route_with_multiple_fibs_on_same_subnet_body()
228{
229	atf_expect_fail "kern/187550 Multiple interfaces on different FIBs but the same subnet don't all have a subnet route"
230	# Configure the TAP interfaces to use a RFC5737 nonrouteable addresses
231	# and a non-default fib
232	ADDR0="192.0.2.2"
233	ADDR1="192.0.2.3"
234	SUBNET="192.0.2.0"
235	MASK="24"
236
237	# Check system configuration
238	if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
239		atf_skip "This test requires net.add_addr_allfibs=0"
240	fi
241	get_fibs 2
242
243	# Configure TAP interfaces
244	setup_tap "$FIB0" ${ADDR0} ${MASK}
245	setup_tap "$FIB1" ${ADDR1} ${MASK}
246
247	# Check that a subnet route exists on both fibs
248	atf_check -o ignore setfib "$FIB0" route get $ADDR1
249	atf_check -o ignore setfib "$FIB1" route get $ADDR0
250}
251
252subnet_route_with_multiple_fibs_on_same_subnet_cleanup()
253{
254	cleanup_tap
255}
256
257# Regression test for kern/187553 "Source address selection for UDP packets
258# with SO_DONTROUTE uses the default FIB".  The original complaint was that a
259# UDP packet with SO_DONTROUTE set would select a source address from an
260# interface on the default FIB instead of the process FIB.
261# This bug was discovered with "setfib 1 netperf -t UDP_STREAM -H some_host"
262# Regression test for kern/187553
263
264# The root cause was that ifa_ifwithnet() did not have a fib argument.  It
265# would return an address from an interface on any FIB that had a subnet route
266# for the destination.  If more than one were available, it would choose the
267# most specific.  The root cause is most easily tested by creating two
268# interfaces with overlapping subnet routes, adding a default route to the
269# interface with the less specific subnet route, and looking up a host that
270# requires the default route using the FIB of the interface with the less
271# specific subnet route.  "route get" should provide a route that uses the
272# interface on the chosen FIB.  However, absent the patch for this bug it will
273# instead use the other interface.
274atf_test_case src_addr_selection_by_subnet cleanup
275src_addr_selection_by_subnet_head()
276{
277	atf_set "descr" "Source address selection for UDP packets with SO_DONTROUTE on non-default FIBs works"
278	atf_set "require.user" "root"
279	atf_set "require.config" "fibs"
280}
281
282src_addr_selection_by_subnet_body()
283{
284	atf_expect_fail "kern/187553 Source address selection for UDP packets with SO_DONTROUTE uses the default FIB"
285	# Configure the TAP interface to use an RFC5737 nonrouteable address
286	# and a non-default fib
287	ADDR0="192.0.2.2"
288	ADDR1="192.0.2.3"
289	GATEWAY0="192.0.2.1"
290	TARGET="192.0.2.128"
291	SUBNET="192.0.2.0"
292	MASK0="25"
293	MASK1="26"
294
295	# Check system configuration
296	if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
297		atf_skip "This test requires net.add_addr_allfibs=0"
298	fi
299	get_fibs 2
300
301	# Configure a TAP interface
302	setup_tap ${FIB0} ${ADDR0} ${MASK0}
303	TAP0=${TAP}
304	setup_tap ${FIB1} ${ADDR1} ${MASK1}
305	TAP1=${TAP}
306
307	# Add a gateway to the interface with the less specific subnet route
308	setfib ${FIB0} route add default ${GATEWAY0}
309
310	# Lookup a route
311	echo "Looking up route to ${TARGET} with fib ${FIB0}"
312	echo "Expected behavior is to use interface ${TAP0}"
313	atf_check -o match:"interface:.${TAP0}" setfib ${FIB0} route -n get ${TARGET}
314}
315
316src_addr_selection_by_subnet_cleanup()
317{
318	cleanup_tap
319}
320
321
322atf_init_test_cases()
323{
324	atf_add_test_case arpresolve_checks_interface_fib
325	atf_add_test_case loopback_and_network_routes_on_nondefault_fib 
326	atf_add_test_case default_route_with_multiple_fibs_on_same_subnet 
327	atf_add_test_case subnet_route_with_multiple_fibs_on_same_subnet 
328	atf_add_test_case src_addr_selection_by_subnet
329}
330
331# Looks up one or more fibs from the configuration data and validates them.
332# Returns the results in the env varilables FIB0, FIB1, etc.
333
334# parameter numfibs	The number of fibs to lookup
335get_fibs()
336{
337	NUMFIBS=$1
338	net_fibs=`sysctl -n net.fibs`
339	i=0
340	while [ $i -lt "$NUMFIBS" ]; do
341		fib=`atf_config_get "fibs" | \
342			awk -v i=$(( i + 1 )) '{print $i}'`
343		echo "fib is ${fib}"
344		eval FIB${i}=${fib}
345		if [ "$fib" -ge "$net_fibs" ]; then
346			atf_skip "The ${i}th configured fib is ${fib}, which is not less than net.fibs, which is ${net_fibs}"
347		fi
348		i=$(( $i + 1 ))
349	done
350}
351
352# Creates a new tap(4) interface, registers it for cleanup, and returns the
353# name via the environment variable TAP
354get_tap()
355{
356	local TAPN=0
357	while ! ifconfig tap${TAPN} create > /dev/null 2>&1; do
358		if [ "$TAPN" -ge 8 ]; then
359			atf_skip "Could not create a tap(4) interface"
360		else
361			TAPN=$(($TAPN + 1))
362		fi
363	done
364	local TAPD=tap${TAPN}
365	# Record the TAP device so we can clean it up later
366	echo ${TAPD} >> "tap_devices_to_cleanup"
367	TAP=${TAPD}
368}
369
370# Create a tap(4) interface, configure it, and register it for cleanup.
371# parameters:
372# fib
373# IP address
374# Netmask in number of bits (eg 24 or 8)
375# Return: the tap interface name as the env variable TAP
376setup_tap()
377{
378	local FIB=$1
379	local ADDR=$2
380	local MASK=$3
381	get_tap
382	echo setfib ${FIB} ifconfig $TAP ${ADDR}/${MASK} fib $FIB
383	setfib ${FIB} ifconfig $TAP ${ADDR}/${MASK} fib $FIB
384}
385
386cleanup_tap()
387{
388	for TAPD in `cat "tap_devices_to_cleanup"`; do
389		ifconfig ${TAPD} destroy
390	done
391}
392