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