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