fibs_test.sh revision 317093
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: stable/10/tests/sys/netinet/fibs_test.sh 317093 2017-04-18 16:17:38Z 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	if [ -f processes_to_kill ]; then
102		for pid in $(cat processes_to_kill); do
103			kill "${pid}"
104		done
105		rm -f processes_to_kill
106	fi
107	cleanup_tap
108}
109
110
111# Regression test for kern/187549
112atf_test_case loopback_and_network_routes_on_nondefault_fib cleanup
113loopback_and_network_routes_on_nondefault_fib_head()
114{
115	atf_set "descr" "When creating and deleting loopback routes, use the interface's fib"
116	atf_set "require.user" "root"
117	atf_set "require.config" "fibs"
118}
119
120loopback_and_network_routes_on_nondefault_fib_body()
121{
122	# Configure the TAP interface to use an RFC5737 nonrouteable address
123	# and a non-default fib
124	ADDR="192.0.2.2"
125	SUBNET="192.0.2.0"
126	MASK="24"
127
128	# Check system configuration
129	if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
130		atf_skip "This test requires net.add_addr_allfibs=0"
131	fi
132	get_fibs 1
133
134	# Configure a TAP interface
135	setup_tap ${FIB0} ${ADDR} ${MASK}
136
137	# Check whether the host route exists in only the correct FIB
138	setfib ${FIB0} netstat -rn -f inet | grep -q "^${ADDR}.*UHS.*lo0"
139	if [ 0 -ne $? ]; then
140		setfib ${FIB0} netstat -rn -f inet
141		atf_fail "Host route did not appear in the correct FIB"
142	fi
143	setfib 0 netstat -rn -f inet | grep -q "^${ADDR}.*UHS.*lo0"
144	if [ 0 -eq $? ]; then
145		setfib 0 netstat -rn -f inet
146		atf_fail "Host route appeared in the wrong FIB"
147	fi
148
149	# Check whether the network route exists in only the correct FIB
150	setfib ${FIB0} netstat -rn -f inet | \
151		grep -q "^${SUBNET}/${MASK}.*${TAPD}"
152	if [ 0 -ne $? ]; then
153		setfib ${FIB0} netstat -rn -f inet
154		atf_fail "Network route did not appear in the correct FIB"
155	fi
156	setfib 0 netstat -rn -f inet | \
157		grep -q "^${SUBNET}/${MASK}.*${TAPD}"
158	if [ 0 -eq $? ]; then
159		setfib ${FIB0} netstat -rn -f inet
160		atf_fail "Network route appeared in the wrong FIB"
161	fi
162}
163
164loopback_and_network_routes_on_nondefault_fib_cleanup()
165{
166	cleanup_tap
167}
168
169
170# Regression test for kern/187552
171atf_test_case default_route_with_multiple_fibs_on_same_subnet cleanup
172default_route_with_multiple_fibs_on_same_subnet_head()
173{
174	atf_set "descr" "Multiple interfaces on the same subnet but with different fibs can both have default routes"
175	atf_set "require.user" "root"
176	atf_set "require.config" "fibs"
177}
178
179default_route_with_multiple_fibs_on_same_subnet_body()
180{
181	# Configure the TAP interfaces to use a RFC5737 nonrouteable addresses
182	# and a non-default fib
183	ADDR0="192.0.2.2"
184	ADDR1="192.0.2.3"
185	GATEWAY="192.0.2.1"
186	SUBNET="192.0.2.0"
187	MASK="24"
188
189	# Check system configuration
190	if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
191		atf_skip "This test requires net.add_addr_allfibs=0"
192	fi
193	get_fibs 2
194
195	# Configure TAP interfaces
196	setup_tap "$FIB0" ${ADDR0} ${MASK}
197	TAP0=$TAP
198	setup_tap "$FIB1" ${ADDR1} ${MASK}
199	TAP1=$TAP
200
201	# Attempt to add default routes
202	setfib ${FIB0} route add default ${GATEWAY}
203	setfib ${FIB1} route add default ${GATEWAY}
204
205	# Verify that the default route exists for both fibs, with their
206	# respective interfaces.
207	atf_check -o match:"^default.*${TAP0}$" \
208		setfib ${FIB0} netstat -rn -f inet
209	atf_check -o match:"^default.*${TAP1}$" \
210		setfib ${FIB1} netstat -rn -f inet
211}
212
213default_route_with_multiple_fibs_on_same_subnet_cleanup()
214{
215	cleanup_tap
216}
217
218
219# Regression test for PR kern/189089
220# Create two tap interfaces and assign them both the same IP address but with
221# different netmasks, and both on the default FIB.  Then remove one's IP
222# address.  Hopefully the machine won't panic.
223atf_test_case same_ip_multiple_ifaces_fib0 cleanup
224same_ip_multiple_ifaces_fib0_head()
225{
226	atf_set "descr" "Can remove an IP alias from an interface when the same IP is also assigned to another interface."
227	atf_set "require.user" "root"
228	atf_set "require.config" "fibs"
229}
230same_ip_multiple_ifaces_fib0_body()
231{
232	ADDR="192.0.2.2"
233	MASK0="24"
234	MASK1="32"
235
236	# Unlike most of the tests in this file, this is applicable regardless
237	# of net.add_addr_allfibs
238
239	# Setup the interfaces, then remove one alias.  It should not panic.
240	setup_tap 0 ${ADDR} ${MASK0}
241	TAP0=${TAP}
242	setup_tap 0 ${ADDR} ${MASK1}
243	TAP1=${TAP}
244	ifconfig ${TAP1} -alias ${ADDR}
245
246	# Do it again, in the opposite order.  It should not panic.
247	setup_tap 0 ${ADDR} ${MASK0}
248	TAP0=${TAP}
249	setup_tap 0 ${ADDR} ${MASK1}
250	TAP1=${TAP}
251	ifconfig ${TAP0} -alias ${ADDR}
252}
253same_ip_multiple_ifaces_fib0_cleanup()
254{
255	cleanup_tap
256}
257
258# Regression test for PR kern/189088
259# Test that removing an IP address works even if the same IP is assigned to a
260# different interface, on a different FIB.  Tests the same code that whose
261# panic was regressed by same_ip_multiple_ifaces_fib0.  
262# Create two tap interfaces and assign them both the same IP address but with
263# different netmasks, and on different FIBs.  Then remove one's IP
264# address.  Hopefully the machine won't panic.  Also, the IP's hostroute should
265# dissappear from the correct fib.
266atf_test_case same_ip_multiple_ifaces cleanup
267same_ip_multiple_ifaces_head()
268{
269	atf_set "descr" "Can remove an IP alias from an interface when the same IP is also assigned to another interface, on non-default FIBs."
270	atf_set "require.user" "root"
271	atf_set "require.config" "fibs"
272}
273same_ip_multiple_ifaces_body()
274{
275	atf_expect_fail "kern/189088 Assigning the same IP to multiple interfaces in different FIBs creates a host route for only one"
276	ADDR="192.0.2.2"
277	MASK0="24"
278	MASK1="32"
279
280	# Unlike most of the tests in this file, this is applicable regardless
281	# of net.add_addr_allfibs
282	get_fibs 2
283
284	# Setup the interfaces, then remove one alias.  It should not panic.
285	setup_tap ${FIB0} ${ADDR} ${MASK0}
286	TAP0=${TAP}
287	setup_tap ${FIB1} ${ADDR} ${MASK1}
288	TAP1=${TAP}
289	ifconfig ${TAP1} -alias ${ADDR}
290	atf_check -o not-match:"^${ADDR}[[:space:]]" \
291		setfib ${FIB1} netstat -rn -f inet
292
293	# Do it again, in the opposite order.  It should not panic.
294	setup_tap ${FIB0} ${ADDR} ${MASK0}
295	TAP0=${TAP}
296	setup_tap ${FIB1} ${ADDR} ${MASK1}
297	TAP1=${TAP}
298	ifconfig ${TAP0} -alias ${ADDR}
299	atf_check -o not-match:"^${ADDR}[[:space:]]" \
300		setfib ${FIB0} netstat -rn -f inet
301}
302same_ip_multiple_ifaces_cleanup()
303{
304	# Due to PR kern/189088, we must destroy the interfaces in LIFO order
305	# in order for the routes to be correctly cleaned up.
306	for TAPD in `tail -r "tap_devices_to_cleanup"`; do
307		ifconfig ${TAPD} destroy
308	done
309}
310
311# Regression test for kern/187550
312atf_test_case subnet_route_with_multiple_fibs_on_same_subnet cleanup
313subnet_route_with_multiple_fibs_on_same_subnet_head()
314{
315	atf_set "descr" "Multiple FIBs can have subnet routes for the same subnet"
316	atf_set "require.user" "root"
317	atf_set "require.config" "fibs"
318}
319
320subnet_route_with_multiple_fibs_on_same_subnet_body()
321{
322	# Configure the TAP interfaces to use a RFC5737 nonrouteable addresses
323	# and a non-default fib
324	ADDR0="192.0.2.2"
325	ADDR1="192.0.2.3"
326	SUBNET="192.0.2.0"
327	MASK="24"
328
329	# Check system configuration
330	if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
331		atf_skip "This test requires net.add_addr_allfibs=0"
332	fi
333	get_fibs 2
334
335	# Configure TAP interfaces
336	setup_tap "$FIB0" ${ADDR0} ${MASK}
337	setup_tap "$FIB1" ${ADDR1} ${MASK}
338
339	# Check that a subnet route exists on both fibs
340	atf_check -o ignore setfib "$FIB0" route get $ADDR1
341	atf_check -o ignore setfib "$FIB1" route get $ADDR0
342}
343
344subnet_route_with_multiple_fibs_on_same_subnet_cleanup()
345{
346	cleanup_tap
347}
348
349# Test that source address selection works correctly for UDP packets with
350# SO_DONTROUTE set that are sent on non-default FIBs.
351# This bug was discovered with "setfib 1 netperf -t UDP_STREAM -H some_host"
352# Regression test for kern/187553
353#
354# The root cause was that ifa_ifwithnet() did not have a fib argument.  It
355# would return an address from an interface on any FIB that had a subnet route
356# for the destination.  If more than one were available, it would choose the
357# most specific.  This is most easily tested by creating a FIB without a
358# default route, then trying to send a UDP packet with SO_DONTROUTE set to an
359# address which is not routable on that FIB.  Absent the fix for this bug,
360# in_pcbladdr would choose an interface on any FIB with a default route.  With
361# the fix, you will get EUNREACH or ENETUNREACH.
362atf_test_case udp_dontroute cleanup
363udp_dontroute_head()
364{
365	atf_set "descr" "Source address selection for UDP packets with SO_DONTROUTE on non-default FIBs works"
366	atf_set "require.user" "root"
367	atf_set "require.config" "fibs"
368}
369
370udp_dontroute_body()
371{
372	atf_expect_fail "kern/187553 Source address selection for UDP packets with SO_DONTROUTE uses the default FIB"
373	# Configure the TAP interface to use an RFC5737 nonrouteable address
374	# and a non-default fib
375	ADDR0="192.0.2.2"
376	ADDR1="192.0.2.3"
377	SUBNET="192.0.2.0"
378	MASK="24"
379	# Use a different IP on the same subnet as the target
380	TARGET="192.0.2.100"
381	SRCDIR=`atf_get_srcdir`
382
383	# Check system configuration
384	if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
385		atf_skip "This test requires net.add_addr_allfibs=0"
386	fi
387	get_fibs 2
388
389	# Configure the TAP interfaces
390	setup_tap ${FIB0} ${ADDR0} ${MASK}
391	TARGET_TAP=${TAP}
392	setup_tap ${FIB1} ${ADDR1} ${MASK}
393
394	# Send a UDP packet with SO_DONTROUTE.  In the failure case, it will
395	# return ENETUNREACH, or send the packet to the wrong tap
396	atf_check -o ignore setfib ${FIB0} \
397		${SRCDIR}/udp_dontroute ${TARGET} /dev/${TARGET_TAP}
398	cleanup_tap
399
400	# Repeat, but this time target the other tap
401	setup_tap ${FIB0} ${ADDR0} ${MASK}
402	setup_tap ${FIB1} ${ADDR1} ${MASK}
403	TARGET_TAP=${TAP}
404
405	atf_check -o ignore setfib ${FIB1} \
406		${SRCDIR}/udp_dontroute ${TARGET} /dev/${TARGET_TAP}
407}
408
409udp_dontroute_cleanup()
410{
411	cleanup_tap
412}
413
414
415atf_init_test_cases()
416{
417	atf_add_test_case arpresolve_checks_interface_fib
418	atf_add_test_case loopback_and_network_routes_on_nondefault_fib
419	atf_add_test_case default_route_with_multiple_fibs_on_same_subnet
420	atf_add_test_case same_ip_multiple_ifaces_fib0
421	atf_add_test_case same_ip_multiple_ifaces
422	atf_add_test_case subnet_route_with_multiple_fibs_on_same_subnet
423	atf_add_test_case udp_dontroute
424}
425
426# Looks up one or more fibs from the configuration data and validates them.
427# Returns the results in the env varilables FIB0, FIB1, etc.
428
429# parameter numfibs	The number of fibs to lookup
430get_fibs()
431{
432	NUMFIBS=$1
433	net_fibs=`sysctl -n net.fibs`
434	i=0
435	while [ $i -lt "$NUMFIBS" ]; do
436		fib=`atf_config_get "fibs" | \
437			awk -v i=$(( i + 1 )) '{print $i}'`
438		echo "fib is ${fib}"
439		eval FIB${i}=${fib}
440		if [ "$fib" -ge "$net_fibs" ]; then
441			atf_skip "The ${i}th configured fib is ${fib}, which is not less than net.fibs, which is ${net_fibs}"
442		fi
443		i=$(( $i + 1 ))
444	done
445}
446
447# Creates a new tap(4) interface, registers it for cleanup, and returns the
448# name via the environment variable TAP
449get_tap()
450{
451	local TAPN=0
452	while ! ifconfig tap${TAPN} create > /dev/null 2>&1; do
453		if [ "$TAPN" -ge 8 ]; then
454			atf_skip "Could not create a tap(4) interface"
455		else
456			TAPN=$(($TAPN + 1))
457		fi
458	done
459	local TAPD=tap${TAPN}
460	# Record the TAP device so we can clean it up later
461	echo ${TAPD} >> "tap_devices_to_cleanup"
462	TAP=${TAPD}
463}
464
465# Create a tap(4) interface, configure it, and register it for cleanup.
466# parameters:
467# fib
468# IP address
469# Netmask in number of bits (eg 24 or 8)
470# Return: the tap interface name as the env variable TAP
471setup_tap()
472{
473	local FIB=$1
474	local ADDR=$2
475	local MASK=$3
476	get_tap
477	echo setfib ${FIB} ifconfig $TAP ${ADDR}/${MASK} fib $FIB
478	setfib ${FIB} ifconfig $TAP ${ADDR}/${MASK} fib $FIB
479}
480
481cleanup_tap()
482{
483	if [ -f tap_devices_to_cleanup ]; then
484		for tap_device in $(cat tap_devices_to_cleanup); do
485			ifconfig "${tap_device}" destroy
486		done
487		rm -f tap_devices_to_cleanup
488	fi
489}
490