1#!/bin/sh
2#-
3# Copyright (c) 2010, "Bjoern A. Zeeb" <bz@FreeBSD.org>
4# Copyright (c) 2011, Sandvine Incorporated ULC.
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27#
28#
29
30#
31# Test ipfw fwd for IPv4 and IPv6 using VIMAGE, testing that as well.
32# For no test the packet header contents must be changed but always
33# keeping the original destination.
34#
35
36case `id -u` in
370)	;;
38*)	echo "ERROR: Must be run as superuser." >&2
39	exit 2
40esac
41
42epair_base()
43{
44	local ep
45
46	ep=`ifconfig epair create`
47	expr ${ep} : '\(.*\).'
48}
49
50debug_err()
51{
52	local _p
53	_p="$1"
54
55	case "${DEBUG}" in
56	"")	;;
57	*)
58		echo " ~~ start of debug ~~"
59		echo " ~~ left:"
60		jexec ${ljid} /sbin/ipfw show
61		echo " ~~ middle:"
62		jexec ${mjid} /sbin/ipfw show
63		echo " ~~ right:"
64		jexec ${rjid} /sbin/ipfw show
65		echo " ~~ result file:"
66		cat ${_p}.1
67		echo " ~~ log file:"
68		cat ${_p}
69		echo " ~~ end of debug ~~"
70		;;
71	esac
72}
73
74check_cleanup_result_file()
75{
76	local _p
77	_p="$1"
78
79	if test ! -s ${_p}.1; then
80		echo "FAIL (output file empty)."
81		debug_err ${_p}
82	else
83		read line < ${_p}.1
84		# Netcat adds 'X's in udp mode.
85		l="/${line#*/}"
86		if test "${l}" = "${_p}"; then
87			echo "PASS."
88		else
89			echo "FAIL (expected: '${_p}' got '${l}')."
90			debug_err ${_p}
91		fi
92	fi
93
94	rm -f ${_p}.1
95	rm -f ${_p}
96}
97
98# Transparent proxy scenario (local address).
99run_test_tp()
100{
101	local _descr
102	local _sip _dip _fip _fport _dport _p
103	local _nc_af _nc_p
104	local _lport
105	descr="$1"
106	_sip="$2"
107	_dip="$3"
108	_fip="$4"
109	_fport="$5"
110	_dport="$6"
111	_p="$7"
112	_nc_af="$8"
113
114	_lport=${_dport}
115	case "${_fport}" in
116	"")	_lport="${_dport}" ;;
117	*)	_lport="${_fport#,}" ;;
118	esac
119
120	case "${_p}" in
121	udp)	_nc_p="-u" ;;
122	esac
123
124	OUT=`mktemp -t "ipfwfwd$$-XXXXXX"`
125	echo -n "${descr} (${OUT}).."
126	(
127	jexec ${ljid} /sbin/ipfw -f flush
128	jexec ${ljid} /sbin/ipfw -f zero
129	jexec ${mjid} /sbin/ipfw -f flush
130	jexec ${mjid} /sbin/ipfw -f zero
131	jexec ${rjid} /sbin/ipfw -f flush
132	jexec ${rjid} /sbin/ipfw -f zero
133	jexec ${mjid} /sbin/ipfw add 100 fwd ${_fip}${_fport} ${_p} from ${_sip} to ${_dip}
134
135	jexec ${mjid} /bin/sh -c "nc -w 10 ${_nc_af} -n ${_nc_p} -l ${_fip} ${_lport} > ${OUT}.1 &"
136	jexec ${rjid} /bin/sh -c "echo '${OUT}' | nc -w 1 -v ${_nc_af} -n ${_nc_p} ${_dip} ${_dport}"
137	) > ${OUT} 2>&1
138	check_cleanup_result_file "${OUT}"
139}
140
141# Transparent redirect scenario (non-local address).
142run_test_nh()
143{
144	local _descr
145	local _sip _dip _fip _fport _dport _p
146	local _nc_af _nc_p
147	local _lport
148	descr="$1"
149	_sip="$2"
150	_dip="$3"
151	_fip="$4"
152	_fport="$5"
153	_dport="$6"
154	_p="$7"
155	_nc_af="$8"
156
157	_lport=${_dport}
158	case "${_fport}" in
159	"")	_lport="${_dport}" ;;
160	*)	_lport="${_fport#,}" ;;
161	esac
162
163	case "${_p}" in
164	udp)	_nc_p="-u" ;;
165	esac
166
167	OUT=`mktemp -t "ipfwfwd$$-XXXXXX"`
168	echo -n "${descr} (${OUT}).."
169	(
170	jexec ${ljid} /sbin/ipfw -f flush
171	jexec ${ljid} /sbin/ipfw -f zero
172	jexec ${mjid} /sbin/ipfw -f flush
173	jexec ${mjid} /sbin/ipfw -f zero
174	jexec ${rjid} /sbin/ipfw -f flush
175	jexec ${rjid} /sbin/ipfw -f zero
176	jexec ${mjid} /sbin/ipfw add 100 fwd ${_fip} ${_p} from ${_sip} to ${_dip}
177
178	jexec ${ljid} /bin/sh -c "nc -w 10 ${_nc_af} -n ${_nc_p} -l ${_dip} ${_lport} > ${OUT}.1 &"
179	jexec ${rjid} /bin/sh -c "echo '${OUT}' | nc -w 1 -v ${_nc_af} -n ${_nc_p} ${_dip} ${_dport}"
180	) > ${OUT} 2>&1
181	check_cleanup_result_file "${OUT}"
182}
183
184echo "==> Setting up test network"
185kldload -q ipfw > /dev/null 2>&1
186
187# Start left (sender) jail.
188ljid=`jail -i -c -n lef$$ host.hostname=left.example.net vnet persist`
189
190# Start middle (ipfw) jail.
191mjid=`jail -i -c -n mid$$ host.hostname=center.example.net vnet persist`
192
193# Start right (non-local ip redirects go to here) jail.
194rjid=`jail -i -c -n right$$ host.hostname=right.example.net vnet persist`
195
196echo "left ${ljid}   middle ${mjid}    right ${rjid}"
197
198# Create networking.
199#
200# jail:		left            middle           right
201# ifaces:	lmep:a ---- lmep:b  mrep:a ---- mrep:b
202#
203
204jexec ${mjid} sysctl net.inet.ip.forwarding=1
205jexec ${mjid} sysctl net.inet6.ip6.forwarding=1
206jexec ${mjid} sysctl net.inet6.ip6.accept_rtadv=0
207
208lmep=$(epair_base)
209ifconfig ${lmep}a vnet ${ljid}
210ifconfig ${lmep}b vnet ${mjid}
211
212jexec ${ljid} ifconfig lo0 inet 127.0.0.1/8
213jexec ${ljid} ifconfig lo0 inet 192.0.2.5/32 alias		# Test 9-10
214jexec ${ljid} ifconfig lo0 inet6 2001:db8:1::1/128 alias	# Test 11-12
215jexec ${ljid} ifconfig ${lmep}a inet  192.0.2.1/30 up
216jexec ${ljid} ifconfig ${lmep}a inet6 2001:db8::1/64 alias
217
218jexec ${ljid} route add default 192.0.2.2
219jexec ${ljid} route add -inet6 default 2001:db8::2
220
221jexec ${mjid} ifconfig lo0 inet 127.0.0.1/8
222jexec ${mjid} ifconfig lo0 inet 192.0.2.255/32 alias		# Test 1-4
223jexec ${mjid} ifconfig lo0 inet6 2001:db8:ffff::1/128 alias	# Test 5-8
224jexec ${mjid} ifconfig ${lmep}b inet  192.0.2.2/30 up
225jexec ${mjid} ifconfig ${lmep}b inet6 2001:db8::2/64 alias
226jexec ${mjid} route add default 192.0.2.1
227
228mrep=$(epair_base)
229ifconfig ${mrep}a vnet ${mjid}
230ifconfig ${mrep}b vnet ${rjid}
231
232jexec ${mjid} ifconfig ${mrep}a inet  192.0.2.5/30 up
233jexec ${mjid} ifconfig ${mrep}a inet6 2001:db8:1::1/64 alias
234
235jexec ${rjid} ifconfig lo0 inet 127.0.0.1/8
236jexec ${rjid} ifconfig ${mrep}b inet  192.0.2.6/30 up
237jexec ${rjid} ifconfig ${mrep}b inet6 2001:db8:1::2/64 alias
238
239jexec ${rjid} route add default 192.0.2.5
240jexec ${rjid} route add -inet6 default 2001:db8:1::1
241
242# ------------------------------------------------------------------------------
243# Tests
244#
245# The jails are not chrooted to they all share the same base filesystem.
246# This means we can put results into /tmp and just collect them from here.
247#
248echo "==> Running tests"
249
250#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
251i=1
252run_test_tp "TEST ${i} IPv4 UDP redirect local to other local address, same port" \
253	192.0.2.6 192.0.2.5 192.0.2.255 "" 12345 udp "-4"
254
255i=$((i + 1))
256run_test_tp "TEST ${i} IPv4 UDP redirect local to other local address, different port" \
257	192.0.2.6 192.0.2.5 192.0.2.255 ",65534" 12345 udp "-4"
258
259#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
260i=$((i + 1))
261run_test_tp "TEST ${i} IPv4 TCP redirect local to other local address, same port" \
262	192.0.2.6 192.0.2.5 192.0.2.255 "" 12345 tcp "-4"
263
264i=$((i + 1))
265run_test_tp "TEST ${i} IPv4 TCP redirect local to other local address, different port" \
266	192.0.2.6 192.0.2.5 192.0.2.255 ",65534" 12345 tcp "-4"
267
268#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
269i=$((i + 1))
270run_test_tp "TEST ${i} IPv4 UDP redirect foreign to local address, same port" \
271	192.0.2.6 192.0.2.1 192.0.2.255 "" 12345 udp "-4"
272
273i=$((i + 1))
274run_test_tp "TEST ${i} IPv4 UDP redirect foreign to local address, different port" \
275	192.0.2.6 192.0.2.1 192.0.2.255 ",65534" 12345 udp "-4"
276
277#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
278i=$((i + 1))
279run_test_tp "TEST ${i} IPv4 TCP redirect foreign to local address, same port" \
280	192.0.2.6 192.0.2.1 192.0.2.255 "" 12345 tcp "-4"
281
282i=$((i + 1))
283run_test_tp "TEST ${i} IPv4 TCP redirect foreign to local address, different port" \
284	192.0.2.6 192.0.2.1 192.0.2.255 ",65534" 12345 tcp "-4"
285
286#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
287i=$((i + 1))
288run_test_tp "TEST ${i} IPv6 UDP redirect local to other local address, same port" \
289	2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 "" 12345 udp "-6"
290
291i=$((i + 1))
292run_test_tp "TEST ${i} IPv6 UDP redirect local to other local address, different port" \
293	2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 ",65534" 12345 udp "-6"
294
295#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
296i=$((i + 1))
297run_test_tp "TEST ${i} IPv6 TCP redirect local to other local address, same port" \
298	2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 "" 12345 tcp "-6"
299
300i=$((i + 1))
301run_test_tp "TEST ${i} IPv6 TCP redirect local to other local address, different port" \
302	2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 ",65534" 12345 tcp "-6"
303
304#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
305i=$((i + 1))
306run_test_tp "TEST ${i} IPv6 UDP redirect foreign to local address, same port" \
307	2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 "" 12345 udp "-6"
308
309i=$((i + 1))
310run_test_tp "TEST ${i} IPv6 UDP redirect foreign to local address, different port" \
311	2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 ",65534" 12345 udp "-6"
312
313#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
314i=$((i + 1))
315run_test_tp "TEST ${i} IPv6 TCP redirect foreign to local address, same port" \
316	2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 "" 12345 tcp "-6"
317
318i=$((i + 1))
319run_test_tp "TEST ${i} IPv6 TCP redirect foreign to local address, different port" \
320	2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 ",65534" 12345 tcp "-6"
321
322#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
323i=$((i + 1))
324run_test_nh "TEST ${i} IPv4 UDP redirect to foreign address" \
325	192.0.2.6 192.0.2.5 192.0.2.1 "" 12345 udp "-4"
326
327i=$((i + 1))
328run_test_nh "TEST ${i} IPv4 TCP redirect to foreign address" \
329	192.0.2.6 192.0.2.5 192.0.2.1 "" 12345 tcp "-4"
330
331#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
332i=$((i + 1))
333run_test_nh "TEST ${i} IPv6 UDP redirect to foreign address" \
334	2001:db8:1::2 2001:db8:1::1 2001:db8::1 "" 12345 udp "-6"
335
336i=$((i + 1))
337run_test_nh "TEST ${i} IPv6 TCP redirect to foreign address" \
338	2001:db8:1::2 2001:db8:1::1 2001:db8::1 "" 12345 tcp "-6"
339
340################################################################################
341#
342# Cleanup
343#
344echo "==> Cleaning up in 3 seconds"
345# Let VIMAGE network stacks settle to avoid panics while still "experimental".
346sleep 3
347
348jail -r ${rjid}
349jail -r ${mjid}
350jail -r ${ljid}
351
352for jid in ${rjid} ${mjid} ${ljid}; do
353	while : ; do
354		x=`jls -as -j ${jid} jid 2>/dev/null`
355		case "${x}" in
356		jid=*)	echo "Waiting for jail ${jid} to stop." >&2
357			sleep 1
358			continue
359			;;
360		esac
361		break
362	done
363done
364
365ifconfig ${lmep}a destroy
366ifconfig ${mrep}a destroy
367
368# end
369