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