xref: /freebsd/tests/sys/netpfil/pf/ether.sh (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
1# $FreeBSD$
2#
3# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4#
5# Copyright © 2021. Rubicon Communications, LLC (Netgate). 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. $(atf_get_srcdir)/utils.subr
29
30common_dir=$(atf_get_srcdir)/../common
31
32atf_test_case "mac" "cleanup"
33mac_head()
34{
35	atf_set descr 'Test MAC address filtering'
36	atf_set require.user root
37}
38
39mac_body()
40{
41	pft_init
42
43	epair=$(vnet_mkepair)
44	epair_a_mac=$(ifconfig ${epair}a ether | awk '/ether/ { print $2; }')
45
46	ifconfig ${epair}a 192.0.2.1/24 up
47
48	vnet_mkjail alcatraz ${epair}b
49	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
50
51	pft_set_rules alcatraz \
52		"ether block from ${epair_a_mac}"
53
54	atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
55
56	# Now enable. Ping should fail.
57	jexec alcatraz pfctl -e
58
59	atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
60
61	# Should still fail for 'to'
62	pft_set_rules alcatraz \
63		"ether block to ${epair_a_mac}"
64	atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
65
66	# Succeeds if we block a different MAC address
67	pft_set_rules alcatraz \
68		"ether block to 00:01:02:03:04:05"
69	atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
70
71	# Should still fail for 'to', even if it's in a list
72	pft_set_rules alcatraz \
73		"ether block to { ${epair_a_mac}, 00:01:02:0:04:05 }"
74	atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
75
76	# Now try this with an interface specified
77	pft_set_rules alcatraz \
78		"ether block on ${epair}b from ${epair_a_mac}"
79	atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
80
81	# Wrong interface should not match
82	pft_set_rules alcatraz \
83		"ether block on ${epair}a from ${epair_a_mac}"
84	atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
85
86	# Test negation
87	pft_set_rules alcatraz \
88		"ether block in on ${epair}b from ! ${epair_a_mac}"
89	atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
90
91	pft_set_rules alcatraz \
92		"ether block out on ${epair}b to ! ${epair_a_mac}"
93	atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
94
95	# Block everything not us
96	pft_set_rules alcatraz \
97		"ether block out on ${epair}b to { ! ${epair_a_mac} }"
98	atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
99
100	# Block us now
101	pft_set_rules alcatraz \
102		"ether block out on ${epair}b to { ! 00:01:02:03:04:05 }"
103	atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
104
105	# Block with a masked address
106	pft_set_rules alcatraz \
107		"ether block out on ${epair}b to { ! 00:01:02:03:00:00/32 }"
108	jexec alcatraz pfctl -se
109	atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
110
111	epair_prefix=$(echo $epair_a_mac | cut -c-8)
112	pft_set_rules alcatraz \
113		"ether block out on ${epair}b to { ${epair_prefix}:00:00:00/24 }"
114	atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
115
116	pft_set_rules alcatraz \
117		"ether block out on ${epair}b to { ${epair_prefix}:00:00:00&ff:ff:ff:00:00:00 }"
118	atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
119
120	# Check '-F ethernet' works
121	jexec alcatraz pfctl -F ethernet
122	atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
123}
124
125mac_cleanup()
126{
127	pft_cleanup
128}
129
130atf_test_case "proto" "cleanup"
131proto_head()
132{
133	atf_set descr 'Test EtherType filtering'
134	atf_set require.user root
135}
136
137proto_body()
138{
139	pft_init
140
141	epair=$(vnet_mkepair)
142	epair_a_mac=$(ifconfig ${epair}a ether | awk '/ether/ { print $2; }')
143
144	ifconfig ${epair}a 192.0.2.1/24 up
145
146	vnet_mkjail alcatraz ${epair}b
147	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
148
149	pft_set_rules alcatraz \
150		"ether block proto 0x0810"
151	jexec alcatraz pfctl -e
152
153	atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
154
155	# Block IP
156	pft_set_rules alcatraz \
157		"ether block proto 0x0800"
158	atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
159
160	# Block ARP
161	pft_set_rules alcatraz \
162		"ether block proto 0x0806"
163	arp -d 192.0.2.2
164	atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
165}
166
167proto_cleanup()
168{
169	pft_cleanup
170}
171
172atf_test_case "direction" "cleanup"
173direction_head()
174{
175	atf_set descr 'Test directionality of ether rules'
176	atf_set require.user root
177	atf_set require.progs jq
178}
179
180direction_body()
181{
182	pft_init
183
184	epair=$(vnet_mkepair)
185	epair_a_mac=$(ifconfig ${epair}a ether | awk '/ether/ { print $2; }')
186	epair_b_mac=$(ifconfig ${epair}b ether | awk '/ether/ { print $2; }')
187
188	ifconfig ${epair}a 192.0.2.1/24 up
189
190	vnet_mkjail alcatraz ${epair}b
191	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
192
193	pft_set_rules alcatraz \
194		"ether block in proto 0x0806"
195	jexec alcatraz pfctl -e
196
197	arp -d 192.0.2.2
198	jexec alcatraz arp -d 192.0.2.1
199
200	# We don't allow the jail to receive ARP requests, so if we try to ping
201	# from host to jail the host can't resolve the MAC address
202	ping -c 1 -t 1 192.0.2.2
203
204	mac=$(arp -an --libxo json \
205	    | jq '."arp"."arp-cache"[] |
206	    select(."ip-address"=="192.0.2.2")."mac-address"')
207	atf_check_not_equal "$mac" "$epair_b_mac"
208
209	# Clear ARP table again
210	arp -d 192.0.2.2
211	jexec alcatraz arp -d 192.0.2.1
212
213	# However, we allow outbound ARP, so the host will learn our MAC if the
214	# jail tries to ping
215	jexec alcatraz ping -c 1 -t 1 192.0.2.1
216
217	mac=$(arp -an --libxo json \
218	    | jq '."arp"."arp-cache"[] |
219	    select(."ip-address"=="192.0.2.2")."mac-address"')
220	atf_check_equal "$mac" "$epair_b_mac"
221
222	# Now do the same, but with outbound ARP blocking
223	pft_set_rules alcatraz \
224		"ether block out proto 0x0806"
225
226	# Clear ARP table again
227	arp -d 192.0.2.2
228	jexec alcatraz arp -d 192.0.2.1
229
230	# The jail can't send ARP requests to us, so we'll never learn our MAC
231	# address
232	jexec alcatraz ping -c 1 -t 1 192.0.2.1
233
234	mac=$(jexec alcatraz arp -an --libxo json \
235	    | jq '."arp"."arp-cache"[] |
236	    select(."ip-address"=="192.0.2.1")."mac-address"')
237	atf_check_not_equal "$mac" "$epair_a_mac"
238}
239
240direction_cleanup()
241{
242	pft_cleanup
243}
244
245atf_test_case "captive" "cleanup"
246captive_head()
247{
248	atf_set descr 'Test a basic captive portal-like setup'
249	atf_set require.user root
250}
251
252captive_body()
253{
254	# Host is client, jail 'gw' is the captive portal gateway, jail 'srv'
255	# is a random (web)server. We use the echo protocol rather than http
256	# for the test, because that's easier.
257	pft_init
258
259	epair_gw=$(vnet_mkepair)
260	epair_srv=$(vnet_mkepair)
261	epair_gw_a_mac=$(ifconfig ${epair_gw}a ether | awk '/ether/ { print $2; }')
262
263	vnet_mkjail gw ${epair_gw}b ${epair_srv}a
264	vnet_mkjail srv ${epair_srv}b
265
266	ifconfig ${epair_gw}a 192.0.2.2/24 up
267	route add -net 198.51.100.0/24 192.0.2.1
268	jexec gw ifconfig ${epair_gw}b 192.0.2.1/24 up
269	jexec gw ifconfig lo0 127.0.0.1/8 up
270	jexec gw sysctl net.inet.ip.forwarding=1
271
272	jexec gw ifconfig ${epair_srv}a 198.51.100.1/24 up
273	jexec srv ifconfig ${epair_srv}b 198.51.100.2/24 up
274	jexec srv route add -net 192.0.2.0/24 198.51.100.1
275
276	# Sanity check
277	atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.2
278
279	pft_set_rules gw \
280		"ether pass quick proto 0x0806" \
281		"ether pass tag captive" \
282		"rdr on ${epair_gw}b proto tcp to port echo tagged captive -> 127.0.0.1 port echo"
283	jexec gw pfctl -e
284
285	# ICMP should still work, because we don't redirect it.
286	atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.2
287
288	# Run the echo server only on the gw, so we know we've redirectly
289	# correctly if we get an echo message.
290	jexec gw /usr/sbin/inetd $(atf_get_srcdir)/echo_inetd.conf
291
292	# Confirm that we're getting redirected
293	atf_check -s exit:0 -o match:"^foo$" -x "echo foo | nc -N 198.51.100.2 7"
294
295	jexec gw killall inetd
296
297	# Now pretend we've authenticated, so add the client's MAC address
298	pft_set_rules gw \
299		"ether pass quick proto 0x0806" \
300		"ether pass quick from ${epair_gw_a_mac}" \
301		"ether pass tag captive" \
302		"rdr on ${epair_gw}b proto tcp to port echo tagged captive -> 127.0.0.1 port echo"
303
304	# No redirect, so failure.
305	atf_check -s exit:1 -x "echo foo | nc -N 198.51.100.2 7"
306
307	# Start a server in srv
308	jexec srv /usr/sbin/inetd $(atf_get_srcdir)/echo_inetd.conf
309
310	# And now we can talk to that one.
311	atf_check -s exit:0 -o match:"^foo$" -x "echo foo | nc -N 198.51.100.2 7"
312}
313
314captive_cleanup()
315{
316	pft_cleanup
317}
318
319atf_test_case "captive_long" "cleanup"
320captive_long_head()
321{
322	atf_set descr 'More complex captive portal setup'
323	atf_set require.user root
324}
325
326captive_long_body()
327{
328	# Host is client, jail 'gw' is the captive portal gateway, jail 'srv'
329	# is a random (web)server. We use the echo protocol rather than http
330	# for the test, because that's easier.
331	dummynet_init
332
333	epair_gw=$(vnet_mkepair)
334	epair_srv=$(vnet_mkepair)
335	epair_gw_a_mac=$(ifconfig ${epair_gw}a ether | awk '/ether/ { print $2; }')
336
337	vnet_mkjail gw ${epair_gw}b ${epair_srv}a
338	vnet_mkjail srv ${epair_srv}b
339
340	ifconfig ${epair_gw}a 192.0.2.2/24 up
341	route add -net 198.51.100.0/24 192.0.2.1
342	jexec gw ifconfig ${epair_gw}b 192.0.2.1/24 up
343	jexec gw ifconfig lo0 127.0.0.1/8 up
344	jexec gw sysctl net.inet.ip.forwarding=1
345
346	jexec gw ifconfig ${epair_srv}a 198.51.100.1/24 up
347	jexec srv ifconfig ${epair_srv}b 198.51.100.2/24 up
348	jexec srv route add -net 192.0.2.0/24 198.51.100.1
349
350	jexec gw dnctl pipe 1 config bw 300KByte/s
351
352	# Sanity check
353	atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.2
354
355	pft_set_rules gw \
356		"ether anchor \"captiveportal\" on { ${epair_gw}b } {" \
357			"ether pass quick proto { 0x0806, 0x8035, 0x888e, 0x88c7, 0x8863, 0x8864 }" \
358			"ether pass tag \"captive\"" \
359		"}" \
360		"rdr on ${epair_gw}b proto tcp to port daytime tagged captive -> 127.0.0.1 port echo"
361	jexec gw pfctl -e
362
363	# ICMP should still work, because we don't redirect it.
364	atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.2
365
366	jexec gw /usr/sbin/inetd -p gw.pid $(atf_get_srcdir)/echo_inetd.conf
367	jexec srv /usr/sbin/inetd -p srv.pid $(atf_get_srcdir)/daytime_inetd.conf
368
369	echo foo | nc -N 198.51.100.2 13
370
371	# Confirm that we're getting redirected
372	atf_check -s exit:0 -o match:"^foo$" -x "echo foo | nc -N 198.51.100.2 13"
373
374	# Now update the rules to allow our client to pass without redirect
375	pft_set_rules gw \
376		"ether anchor \"captiveportal\" on { ${epair_gw}b } {" \
377			"ether pass quick proto { 0x0806, 0x8035, 0x888e, 0x88c7, 0x8863, 0x8864 }" \
378			"ether pass quick from { ${epair_gw_a_mac} } dnpipe 1" \
379			"ether pass tag \"captive\"" \
380		"}" \
381		"rdr on ${epair_gw}b proto tcp to port daytime tagged captive -> 127.0.0.1 port echo"
382
383	# We're not being redirected and get datime information now
384	atf_check -s exit:0 -o match:"^(Mon|Tue|Wed|Thu|Fri|Sat|Sun)" -x "echo foo | nc -N 198.51.100.2 13"
385
386	jexec gw killall inetd
387	jexec srv killall inetd
388}
389
390captive_long_cleanup()
391{
392	pft_cleanup
393}
394
395atf_test_case "dummynet" "cleanup"
396dummynet_head()
397{
398	atf_set descr 'Test dummynet for L2 traffic'
399	atf_set require.user root
400}
401
402dummynet_body()
403{
404	pft_init
405
406	if ! kldstat -q -m dummynet; then
407		atf_skip "This test requires dummynet"
408	fi
409
410	epair=$(vnet_mkepair)
411	vnet_mkjail alcatraz ${epair}b
412
413	ifconfig ${epair}a 192.0.2.1/24 up
414	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
415
416	# Sanity check
417	atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
418
419	jexec alcatraz dnctl pipe 1 config bw 30Byte/s
420	jexec alcatraz pfctl -e
421	pft_set_rules alcatraz \
422		"ether pass in dnpipe 1"
423
424	# Ensure things don't break if non-IP(v4/v6) traffic hits dummynet
425	arp -d 192.0.2.2
426
427	# single ping succeeds just fine
428	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
429
430	# Saturate the link
431	ping -i .1 -c 5 -s 1200 192.0.2.2
432
433	# We should now be hitting the limits and get this packet dropped.
434	atf_check -s exit:2 -o ignore ping -c 1 -s 1200 192.0.2.2
435
436	# We can now also dummynet outbound traffic!
437	pft_set_rules alcatraz \
438		"ether pass out dnpipe 1"
439
440	# We should still be hitting the limits and get this packet dropped.
441	atf_check -s exit:2 -o ignore ping -c 1 -s 1200 192.0.2.2
442}
443
444dummynet_cleanup()
445{
446	pft_cleanup
447}
448
449atf_test_case "anchor" "cleanup"
450anchor_head()
451{
452	atf_set descr 'Test ether anchors'
453	atf_set require.user root
454}
455
456anchor_body()
457{
458	pft_init
459
460	epair=$(vnet_mkepair)
461	epair_a_mac=$(ifconfig ${epair}a ether | awk '/ether/ { print $2; }')
462
463	vnet_mkjail alcatraz ${epair}b
464
465	ifconfig ${epair}a 192.0.2.1/24 up
466	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
467
468	# Sanity check
469	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
470
471	jexec alcatraz pfctl -e
472	pft_set_rules alcatraz \
473		"ether anchor \"foo\" in on lo0 {" \
474			"ether block" \
475		"}"
476
477	# That only filters on lo0, so we should still be able to pass traffic
478	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
479
480	pft_set_rules alcatraz \
481		"ether block in" \
482		"ether anchor \"foo\" in on ${epair}b {" \
483			"ether pass" \
484		"}"
485	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
486
487	pft_set_rules alcatraz \
488		"ether pass" \
489		"ether anchor \"bar\" in on ${epair}b {" \
490			"ether block" \
491		"}"
492	atf_check -s exit:2 -o ignore ping -c 1 -t 2 192.0.2.2
493
494	pft_set_rules alcatraz \
495		"ether block in" \
496		"ether anchor \"baz\" on ${epair}b {" \
497			"ether pass in from 01:02:03:04:05:06" \
498		"}" \
499		"ether pass in from ${epair_a_mac}"
500	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
501
502	atf_check -s exit:0 -o match:'baz' jexec alcatraz pfctl -sA
503}
504
505anchor_cleanup()
506{
507	pft_cleanup
508}
509
510atf_test_case "ip" "cleanup"
511ip_head()
512{
513	atf_set descr 'Test filtering based on IP source/destination'
514	atf_set require.user root
515}
516
517ip_body()
518{
519	pft_init
520
521	epair=$(vnet_mkepair)
522
523	vnet_mkjail alcatraz ${epair}b
524
525	ifconfig ${epair}a 192.0.2.1/24 up
526	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
527
528	# Sanity check
529	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
530
531	jexec alcatraz pfctl -e
532	pft_set_rules alcatraz \
533		"ether pass" \
534		"ether block in l3 from 192.0.2.1"
535
536	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
537
538	# Change IP address and we can ping again
539	ifconfig ${epair}a 192.0.2.3/24 up
540	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
541
542	# Test the 'to' keyword too
543	pft_set_rules alcatraz \
544		"ether pass" \
545		"ether block out l3 to 192.0.2.3"
546	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
547
548	# Test table
549	pft_set_rules alcatraz \
550		"table <tbl> { 192.0.2.3 }" \
551		"ether pass" \
552		"ether block out l3 to <tbl>"
553	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
554}
555
556ip_cleanup()
557{
558	pft_cleanup
559}
560
561atf_test_case "tag" "cleanup"
562tag_head()
563{
564	atf_set descr 'Test setting tags'
565	atf_set require.user root
566}
567
568tag_body()
569{
570	pft_init
571
572	epair=$(vnet_mkepair)
573
574	vnet_mkjail alcatraz ${epair}b
575	ifconfig ${epair}a 192.0.2.1/24 up
576	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
577
578	# Sanity check
579	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
580
581	jexec alcatraz pfctl -e
582	pft_set_rules alcatraz \
583		"ether pass in tag foo" \
584		"block in tagged foo"
585
586	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
587
588	pft_set_rules alcatraz \
589		"ether pass in tag bar" \
590		"block in tagged foo"
591
592	# Still passes when tagged differently
593	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
594}
595
596tag_cleanup()
597{
598	pft_cleanup
599}
600
601atf_test_case "match_tag" "cleanup"
602match_tag_head()
603{
604	atf_set descr 'Test matching tags'
605	atf_set require.user root
606}
607
608match_tag_body()
609{
610	pft_init
611
612	epair=$(vnet_mkepair)
613
614	vnet_mkjail alcatraz ${epair}b
615
616	ifconfig ${epair}a 192.0.2.1/24 up
617	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
618
619	# Sanity check
620	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
621
622	jexec alcatraz pfctl -e
623	pft_set_rules alcatraz \
624		"ether block out tagged foo" \
625		"pass in proto icmp tag foo"
626
627	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
628
629	pft_set_rules alcatraz \
630		"ether block out tagged bar" \
631		"pass in proto icmp tag foo"
632
633	# Still passes when tagged differently
634	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
635}
636
637match_tag_cleanup()
638{
639	pft_cleanup
640}
641
642atf_test_case "short_pkt" "cleanup"
643short_pkt_head()
644{
645	atf_set descr 'Test overly short Ethernet packets'
646	atf_set require.user root
647	atf_set require.progs scapy
648}
649
650short_pkt_body()
651{
652	pft_init
653
654	epair=$(vnet_mkepair)
655	ifconfig ${epair}a 192.0.2.1/24 up
656
657	vnet_mkjail alcatraz ${epair}b
658	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
659
660	jexec alcatraz pfctl -e
661	pft_set_rules alcatraz \
662		"ether pass in" \
663		"ether pass out" \
664		"ether pass in l3 from 192.0.2.1"
665
666	# Sanity check
667	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
668
669	jexec alcatraz pfctl -se -v
670
671	# Try sending ever shorter ping requests
672	# BPF won't let us send anything shorter than an Ethernet header, but
673	# that's good enough for this test
674	$(atf_get_srcdir)/pft_ether.py \
675	    --sendif ${epair}a \
676	    --to 192.0.2.2 \
677	    --len 14-64
678}
679
680short_pkt_cleanup()
681{
682	pft_cleanup
683}
684
685atf_test_case "bridge_to" "cleanup"
686bridge_to_head()
687{
688	atf_set descr 'Test bridge-to keyword'
689	atf_set require.user root
690	atf_set require.progs scapy
691}
692
693bridge_to_body()
694{
695	pft_init
696
697	epair_in=$(vnet_mkepair)
698	epair_out=$(vnet_mkepair)
699
700	ifconfig ${epair_in}a 192.0.2.1/24 up
701	ifconfig ${epair_out}a up
702
703	vnet_mkjail alcatraz ${epair_in}b ${epair_out}b
704	jexec alcatraz ifconfig ${epair_in}b 192.0.2.2/24 up
705	jexec alcatraz ifconfig ${epair_out}b up
706
707	# Sanity check
708	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
709	atf_check -s exit:1 -o ignore \
710		${common_dir}/pft_ping.py \
711		--sendif ${epair_in}a \
712		--to 192.0.2.2 \
713		--recvif ${epair_out}a
714
715	jexec alcatraz pfctl -e
716	pft_set_rules alcatraz \
717		"ether pass in on ${epair_in}b bridge-to ${epair_out}b"
718
719	# Now the packets go out epair_out rather than be processed locally
720	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
721	atf_check -s exit:0 -o ignore \
722		${common_dir}/pft_ping.py \
723		--sendif ${epair_in}a \
724		--to 192.0.2.2 \
725		--recvif ${epair_out}a
726}
727
728bridge_to_cleanup()
729{
730	pft_cleanup
731}
732
733atf_init_test_cases()
734{
735	atf_add_test_case "mac"
736	atf_add_test_case "proto"
737	atf_add_test_case "direction"
738	atf_add_test_case "captive"
739	atf_add_test_case "captive_long"
740	atf_add_test_case "dummynet"
741	atf_add_test_case "anchor"
742	atf_add_test_case "ip"
743	atf_add_test_case "tag"
744	atf_add_test_case "match_tag"
745	atf_add_test_case "short_pkt"
746	atf_add_test_case "bridge_to"
747}
748