xref: /freebsd/tests/sys/netpfil/pf/ether.sh (revision c700393934285ea84696a17bd951b20e439edfc3)
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 -p ${PWD}/echo_inetd.pid $(atf_get_srcdir)/echo_inetd.conf
290	sleep 1
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 -p ${PWD}/echo_inetd.pid $(atf_get_srcdir)/echo_inetd.conf
309	sleep 1
310
311	# And now we can talk to that one.
312	atf_check -s exit:0 -o match:"^foo$" -x "echo foo | nc -N 198.51.100.2 7"
313}
314
315captive_cleanup()
316{
317	pft_cleanup
318}
319
320atf_test_case "captive_long" "cleanup"
321captive_long_head()
322{
323	atf_set descr 'More complex captive portal setup'
324	atf_set require.user root
325}
326
327captive_long_body()
328{
329	# Host is client, jail 'gw' is the captive portal gateway, jail 'srv'
330	# is a random (web)server. We use the echo protocol rather than http
331	# for the test, because that's easier.
332	dummynet_init
333
334	epair_gw=$(vnet_mkepair)
335	epair_srv=$(vnet_mkepair)
336	epair_gw_a_mac=$(ifconfig ${epair_gw}a ether | awk '/ether/ { print $2; }')
337
338	vnet_mkjail gw ${epair_gw}b ${epair_srv}a
339	vnet_mkjail srv ${epair_srv}b
340
341	ifconfig ${epair_gw}a 192.0.2.2/24 up
342	route add -net 198.51.100.0/24 192.0.2.1
343	jexec gw ifconfig ${epair_gw}b 192.0.2.1/24 up
344	jexec gw ifconfig lo0 127.0.0.1/8 up
345	jexec gw sysctl net.inet.ip.forwarding=1
346
347	jexec gw ifconfig ${epair_srv}a 198.51.100.1/24 up
348	jexec srv ifconfig ${epair_srv}b 198.51.100.2/24 up
349	jexec srv route add -net 192.0.2.0/24 198.51.100.1
350
351	jexec gw dnctl pipe 1 config bw 300KByte/s
352
353	# Sanity check
354	atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.2
355
356	pft_set_rules gw \
357		"ether anchor \"captiveportal\" on { ${epair_gw}b } {" \
358			"ether pass quick proto { 0x0806, 0x8035, 0x888e, 0x88c7, 0x8863, 0x8864 }" \
359			"ether pass tag \"captive\"" \
360		"}" \
361		"rdr on ${epair_gw}b proto tcp to port daytime tagged captive -> 127.0.0.1 port echo"
362	jexec gw pfctl -e
363
364	# ICMP should still work, because we don't redirect it.
365	atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.2
366
367	jexec gw /usr/sbin/inetd -p ${PWD}/gw.pid $(atf_get_srcdir)/echo_inetd.conf
368	jexec srv /usr/sbin/inetd -p ${PWD}/srv.pid $(atf_get_srcdir)/daytime_inetd.conf
369	sleep p1
370
371	echo foo | nc -N 198.51.100.2 13
372
373	# Confirm that we're getting redirected
374	atf_check -s exit:0 -o match:"^foo$" -x "echo foo | nc -N 198.51.100.2 13"
375
376	# Now update the rules to allow our client to pass without redirect
377	pft_set_rules gw \
378		"ether anchor \"captiveportal\" on { ${epair_gw}b } {" \
379			"ether pass quick proto { 0x0806, 0x8035, 0x888e, 0x88c7, 0x8863, 0x8864 }" \
380			"ether pass quick from { ${epair_gw_a_mac} } dnpipe 1" \
381			"ether pass tag \"captive\"" \
382		"}" \
383		"rdr on ${epair_gw}b proto tcp to port daytime tagged captive -> 127.0.0.1 port echo"
384
385	# We're not being redirected and get datime information now
386	atf_check -s exit:0 -o match:"^(Mon|Tue|Wed|Thu|Fri|Sat|Sun)" -x "echo foo | nc -N 198.51.100.2 13"
387
388	jexec gw killall inetd
389	jexec srv killall inetd
390}
391
392captive_long_cleanup()
393{
394	pft_cleanup
395}
396
397atf_test_case "dummynet" "cleanup"
398dummynet_head()
399{
400	atf_set descr 'Test dummynet for L2 traffic'
401	atf_set require.user root
402}
403
404dummynet_body()
405{
406	pft_init
407
408	if ! kldstat -q -m dummynet; then
409		atf_skip "This test requires dummynet"
410	fi
411
412	epair=$(vnet_mkepair)
413	vnet_mkjail alcatraz ${epair}b
414
415	ifconfig ${epair}a 192.0.2.1/24 up
416	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
417
418	# Sanity check
419	atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
420
421	jexec alcatraz dnctl pipe 1 config bw 300Byte/s
422	jexec alcatraz pfctl -e
423	pft_set_rules alcatraz \
424		"ether pass in dnpipe 1"
425
426	# Ensure things don't break if non-IP(v4/v6) traffic hits dummynet
427	arp -d 192.0.2.2
428
429	# single ping succeeds just fine
430	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
431
432	# Saturate the link
433	ping -i .1 -c 5 -s 1200 192.0.2.2
434
435	# We should now be hitting the limits and get this packet dropped.
436	atf_check -s exit:2 -o ignore ping -c 1 -t 1 -s 1200 192.0.2.2
437
438	# We can now also dummynet outbound traffic!
439	pft_set_rules alcatraz \
440		"ether pass out dnpipe 1"
441
442	# We should still be hitting the limits and get this packet dropped.
443	atf_check -s exit:2 -o ignore ping -c 1 -t 1 -s 1200 192.0.2.2
444}
445
446dummynet_cleanup()
447{
448	pft_cleanup
449}
450
451atf_test_case "anchor" "cleanup"
452anchor_head()
453{
454	atf_set descr 'Test ether anchors'
455	atf_set require.user root
456}
457
458anchor_body()
459{
460	pft_init
461
462	epair=$(vnet_mkepair)
463	epair_a_mac=$(ifconfig ${epair}a ether | awk '/ether/ { print $2; }')
464
465	vnet_mkjail alcatraz ${epair}b
466
467	ifconfig ${epair}a 192.0.2.1/24 up
468	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
469
470	# Sanity check
471	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
472
473	jexec alcatraz pfctl -e
474	pft_set_rules alcatraz \
475		"ether anchor \"foo\" in on lo0 {" \
476			"ether block" \
477		"}"
478
479	# That only filters on lo0, so we should still be able to pass traffic
480	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
481
482	pft_set_rules alcatraz \
483		"ether block in" \
484		"ether anchor \"foo\" in on ${epair}b {" \
485			"ether pass" \
486		"}"
487	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
488
489	pft_set_rules alcatraz \
490		"ether pass" \
491		"ether anchor \"bar\" in on ${epair}b {" \
492			"ether block" \
493		"}"
494	atf_check -s exit:2 -o ignore ping -c 1 -t 2 192.0.2.2
495
496	pft_set_rules alcatraz \
497		"ether block in" \
498		"ether anchor \"baz\" on ${epair}b {" \
499			"ether pass in from 01:02:03:04:05:06" \
500		"}" \
501		"ether pass in from ${epair_a_mac}"
502	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
503
504	atf_check -s exit:0 -o match:'baz' jexec alcatraz pfctl -sA
505}
506
507anchor_cleanup()
508{
509	pft_cleanup
510}
511
512atf_test_case "ip" "cleanup"
513ip_head()
514{
515	atf_set descr 'Test filtering based on IP source/destination'
516	atf_set require.user root
517}
518
519ip_body()
520{
521	pft_init
522
523	epair=$(vnet_mkepair)
524
525	vnet_mkjail alcatraz ${epair}b
526
527	ifconfig ${epair}a 192.0.2.1/24 up
528	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
529
530	# Sanity check
531	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
532
533	jexec alcatraz pfctl -e
534	pft_set_rules alcatraz \
535		"ether pass" \
536		"ether block in l3 from 192.0.2.1"
537
538	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
539
540	# Change IP address and we can ping again
541	ifconfig ${epair}a 192.0.2.3/24 up
542	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
543
544	# Test the 'to' keyword too
545	pft_set_rules alcatraz \
546		"ether pass" \
547		"ether block out l3 to 192.0.2.3"
548	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
549
550	# Test table
551	pft_set_rules alcatraz \
552		"table <tbl> { 192.0.2.3 }" \
553		"ether pass" \
554		"ether block out l3 to <tbl>"
555	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
556}
557
558ip_cleanup()
559{
560	pft_cleanup
561}
562
563atf_test_case "tag" "cleanup"
564tag_head()
565{
566	atf_set descr 'Test setting tags'
567	atf_set require.user root
568}
569
570tag_body()
571{
572	pft_init
573
574	epair=$(vnet_mkepair)
575
576	vnet_mkjail alcatraz ${epair}b
577	ifconfig ${epair}a 192.0.2.1/24 up
578	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
579
580	# Sanity check
581	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
582
583	jexec alcatraz pfctl -e
584	pft_set_rules alcatraz \
585		"ether pass in tag foo" \
586		"block in tagged foo"
587
588	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
589
590	pft_set_rules alcatraz \
591		"ether pass in tag bar" \
592		"block in tagged foo"
593
594	# Still passes when tagged differently
595	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
596}
597
598tag_cleanup()
599{
600	pft_cleanup
601}
602
603atf_test_case "match_tag" "cleanup"
604match_tag_head()
605{
606	atf_set descr 'Test matching tags'
607	atf_set require.user root
608}
609
610match_tag_body()
611{
612	pft_init
613
614	epair=$(vnet_mkepair)
615
616	vnet_mkjail alcatraz ${epair}b
617
618	ifconfig ${epair}a 192.0.2.1/24 up
619	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
620
621	# Sanity check
622	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
623
624	jexec alcatraz pfctl -e
625	pft_set_rules alcatraz \
626		"ether block out tagged foo" \
627		"pass in proto icmp tag foo"
628
629	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
630
631	pft_set_rules alcatraz \
632		"ether block out tagged bar" \
633		"pass in proto icmp tag foo"
634
635	# Still passes when tagged differently
636	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
637}
638
639match_tag_cleanup()
640{
641	pft_cleanup
642}
643
644atf_test_case "short_pkt" "cleanup"
645short_pkt_head()
646{
647	atf_set descr 'Test overly short Ethernet packets'
648	atf_set require.user root
649	atf_set require.progs python3 scapy
650}
651
652short_pkt_body()
653{
654	pft_init
655
656	epair=$(vnet_mkepair)
657	ifconfig ${epair}a 192.0.2.1/24 up
658
659	vnet_mkjail alcatraz ${epair}b
660	jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
661
662	jexec alcatraz pfctl -e
663	pft_set_rules alcatraz \
664		"ether pass in" \
665		"ether pass out" \
666		"ether pass in l3 from 192.0.2.1"
667
668	# Sanity check
669	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
670
671	jexec alcatraz pfctl -se -v
672
673	# Try sending ever shorter ping requests
674	# BPF won't let us send anything shorter than an Ethernet header, but
675	# that's good enough for this test
676	$(atf_get_srcdir)/pft_ether.py \
677	    --sendif ${epair}a \
678	    --to 192.0.2.2 \
679	    --len 14-64
680}
681
682short_pkt_cleanup()
683{
684	pft_cleanup
685}
686
687atf_test_case "bridge_to" "cleanup"
688bridge_to_head()
689{
690	atf_set descr 'Test bridge-to keyword'
691	atf_set require.user root
692	atf_set require.progs python3 scapy
693}
694
695bridge_to_body()
696{
697	pft_init
698
699	epair_in=$(vnet_mkepair)
700	epair_out=$(vnet_mkepair)
701
702	ifconfig ${epair_in}a 192.0.2.1/24 up
703	ifconfig ${epair_out}a up
704
705	vnet_mkjail alcatraz ${epair_in}b ${epair_out}b
706	jexec alcatraz ifconfig ${epair_in}b 192.0.2.2/24 up
707	jexec alcatraz ifconfig ${epair_out}b up
708
709	# Sanity check
710	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
711	atf_check -s exit:1 -o ignore \
712		${common_dir}/pft_ping.py \
713		--sendif ${epair_in}a \
714		--to 192.0.2.2 \
715		--recvif ${epair_out}a
716
717	jexec alcatraz pfctl -e
718	pft_set_rules alcatraz \
719		"ether pass in on ${epair_in}b bridge-to ${epair_out}b"
720
721	# Now the packets go out epair_out rather than be processed locally
722	atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
723	atf_check -s exit:0 -o ignore \
724		${common_dir}/pft_ping.py \
725		--sendif ${epair_in}a \
726		--to 192.0.2.2 \
727		--recvif ${epair_out}a
728}
729
730bridge_to_cleanup()
731{
732	pft_cleanup
733}
734
735atf_init_test_cases()
736{
737	atf_add_test_case "mac"
738	atf_add_test_case "proto"
739	atf_add_test_case "direction"
740	atf_add_test_case "captive"
741	atf_add_test_case "captive_long"
742	atf_add_test_case "dummynet"
743	atf_add_test_case "anchor"
744	atf_add_test_case "ip"
745	atf_add_test_case "tag"
746	atf_add_test_case "match_tag"
747	atf_add_test_case "short_pkt"
748	atf_add_test_case "bridge_to"
749}
750