xref: /freebsd/tests/sys/netpfil/pf/fragmentation_pass.sh (revision 7ef62cebc2f965b0f640263e179276928885e33d)
1# $FreeBSD$
2#
3# SPDX-License-Identifier: BSD-2-Clause
4#
5# Copyright (c) 2017 Kristof Provost <kp@FreeBSD.org>
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 "too_many_fragments" "cleanup"
33
34too_many_fragments_head()
35{
36	atf_set descr 'IPv4 fragment limitation test'
37	atf_set require.user root
38}
39
40too_many_fragments_body()
41{
42	pft_init
43
44	epair=$(vnet_mkepair)
45	vnet_mkjail alcatraz ${epair}a
46
47	ifconfig ${epair}b inet 192.0.2.1/24 up
48	jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up
49
50	ifconfig ${epair}b mtu 200
51	jexec alcatraz ifconfig ${epair}a mtu 200
52
53	jexec alcatraz pfctl -e
54	pft_set_rules alcatraz \
55		"set reassemble yes" \
56		"pass keep state"
57
58	# So we know pf is limiting things
59	jexec alcatraz sysctl net.inet.ip.maxfragsperpacket=1024
60
61	# Sanity check
62	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
63
64	# We can ping with < 64 fragments
65	atf_check -s exit:0 -o ignore ping -c 1 -s 800 192.0.2.2
66
67	# Too many fragments should fail
68	atf_check -s exit:2 -o ignore ping -c 1 -s 20000 192.0.2.2
69}
70
71too_many_fragments_cleanup()
72{
73	pft_cleanup
74}
75
76atf_test_case "v6" "cleanup"
77v6_head()
78{
79	atf_set descr 'IPv6 fragmentation test'
80	atf_set require.user root
81	atf_set require.progs scapy
82}
83
84v6_body()
85{
86	pft_init
87
88	epair_send=$(vnet_mkepair)
89	epair_link=$(vnet_mkepair)
90
91	vnet_mkjail alcatraz ${epair_send}b ${epair_link}a
92	vnet_mkjail singsing ${epair_link}b
93
94	ifconfig ${epair_send}a inet6 2001:db8:42::1/64 no_dad up
95
96	jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 no_dad up
97	jexec alcatraz ifconfig ${epair_link}a inet6 2001:db8:43::2/64 no_dad up
98	jexec alcatraz sysctl net.inet6.ip6.forwarding=1
99
100	jexec singsing ifconfig ${epair_link}b inet6 2001:db8:43::3/64 no_dad up
101	jexec singsing route add -6 2001:db8:42::/64 2001:db8:43::2
102	route add -6 2001:db8:43::/64 2001:db8:42::2
103
104	jexec alcatraz ifconfig ${epair_send}b inet6 -ifdisabled
105	jexec alcatraz ifconfig ${epair_link}a inet6 -ifdisabled
106	jexec singsing ifconfig ${epair_link}b inet6 -ifdisabled
107	ifconfig ${epair_send}a inet6 -ifdisabled
108
109	ifconfig ${epair_send}a
110	jexec alcatraz ifconfig ${epair_send}b
111	lladdr=$(jexec alcatraz ifconfig ${epair_send}b | awk '/ scopeid / { print($2); }' | cut -f 1 -d %)
112
113	jexec alcatraz pfctl -e
114	pft_set_rules alcatraz \
115		"set reassemble yes" \
116		"pass keep state" \
117		"block in" \
118		"pass in inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
119		"pass in inet6 proto icmp6 icmp6-type { echoreq, echorep }" \
120		"set skip on lo"
121
122	# Host test
123	atf_check -s exit:0 -o ignore \
124		ping -6 -c 1 2001:db8:42::2
125
126	atf_check -s exit:0 -o ignore \
127		ping -6 -c 1 -s 4500 2001:db8:42::2
128
129	atf_check -s exit:0 -o ignore\
130		ping -6 -c 1 -b 70000 -s 65000 2001:db8:42::2
131
132	# Force an NDP lookup
133	ping -6 -c 1 ${lladdr}%${epair_send}a
134
135	atf_check -s exit:0 -o ignore\
136		ping -6 -c 1 -b 70000 -s 65000 ${lladdr}%${epair_send}a
137
138	# Forwarding test
139	atf_check -s exit:0 -o ignore \
140		ping -6 -c 1 2001:db8:43::3
141
142	atf_check -s exit:0 -o ignore \
143		ping -6 -c 1 -s 4500 2001:db8:43::3
144
145	atf_check -s exit:0 -o ignore\
146		ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3
147
148	$(atf_get_srcdir)/CVE-2019-5597.py \
149		${epair_send}a \
150		2001:db8:42::1 \
151		2001:db8:43::3
152}
153
154v6_cleanup()
155{
156	pft_cleanup
157}
158
159atf_test_case "mtu_diff" "cleanup"
160mtu_diff_head()
161{
162	atf_set descr 'Test reassembly across different MTUs, PR #255432'
163	atf_set require.user root
164}
165
166mtu_diff_body()
167{
168	pft_init
169
170	epair_small=$(vnet_mkepair)
171	epair_large=$(vnet_mkepair)
172
173	vnet_mkjail first ${epair_small}b ${epair_large}a
174	vnet_mkjail second ${epair_large}b
175
176	ifconfig ${epair_small}a 192.0.2.1/25 up
177	jexec first ifconfig ${epair_small}b 192.0.2.2/25 up
178
179	jexec first sysctl net.inet.ip.forwarding=1
180	jexec first ifconfig ${epair_large}a 192.0.2.130/25 up
181	jexec first ifconfig ${epair_large}a mtu 9000
182	jexec second ifconfig ${epair_large}b 192.0.2.131/25 up
183	jexec second ifconfig ${epair_large}b mtu 9000
184	jexec second route add default 192.0.2.130
185
186	route add 192.0.2.128/25 192.0.2.2
187
188	jexec first pfctl -e
189	pft_set_rules first \
190		"set reassemble yes" \
191		"pass keep state"
192
193	# Sanity checks
194	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
195	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.130
196	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.131
197
198	# Large packet that'll get reassembled and sent out in one on the large
199	# epair
200	atf_check -s exit:0 -o ignore ping -c 1 -s 8000 192.0.2.131
201}
202
203mtu_diff_cleanup()
204{
205	pft_cleanup
206}
207
208frag_common()
209{
210	name=$1
211
212	pft_init
213
214	epair=$(vnet_mkepair)
215	vnet_mkjail alcatraz ${epair}a
216
217	ifconfig ${epair}b inet 192.0.2.1/24 up
218	jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up
219
220	jexec alcatraz pfctl -e
221	pft_set_rules alcatraz \
222		"set reassemble yes" \
223		"pass keep state"
224
225	# Sanity check
226	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
227
228	atf_check -s exit:0 -o ignore $(atf_get_srcdir)/frag-${1}.py \
229		--to 192.0.2.2 \
230		--fromaddr 192.0.2.1 \
231		--sendif ${epair}b \
232		--recvif ${epair}b
233}
234
235atf_test_case "overreplace" "cleanup"
236overreplace_head()
237{
238	atf_set descr 'ping fragment that overlaps fragment at index boundary and replace it'
239	atf_set require.user root
240	atf_set require.progs scapy
241}
242
243overreplace_body()
244{
245	frag_common overreplace
246}
247
248overreplace_cleanup()
249{
250	pft_cleanup
251}
252
253atf_test_case "overindex" "cleanup"
254overindex_head()
255{
256	atf_set descr 'ping fragment that overlaps the first fragment at index boundary'
257	atf_set require.user root
258	atf_set require.progs scapy
259}
260
261overindex_body()
262{
263	frag_common overindex
264}
265
266overindex_cleanup()
267{
268	pft_cleanup
269}
270
271atf_test_case "overlimit" "cleanup"
272overlimit_head()
273{
274	atf_set descr 'ping fragment at index boundary that cannot be requeued'
275	atf_set require.user root
276	atf_set require.progs scapy
277}
278
279overlimit_body()
280{
281	frag_common overlimit
282}
283
284overlimit_cleanup()
285{
286	pft_cleanup
287}
288
289atf_test_case "reassemble" "cleanup"
290reassemble_head()
291{
292	atf_set descr 'Test reassembly'
293	atf_set require.user root
294}
295
296reassemble_body()
297{
298	pft_init
299
300	epair=$(vnet_mkepair)
301	vnet_mkjail alcatraz ${epair}a
302
303	ifconfig ${epair}b inet 192.0.2.1/24 up
304	jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up
305
306	# Sanity check
307	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
308
309	jexec alcatraz pfctl -e
310	pft_set_rules alcatraz \
311		"pass out" \
312		"block in" \
313		"pass in inet proto icmp all icmp-type echoreq"
314
315	# Single fragment passes
316	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
317
318	# But a fragmented ping does not
319	atf_check -s exit:2 -o ignore ping -c 1 -s 2000 192.0.2.2
320
321	pft_set_rules alcatraz \
322		"set reassemble yes" \
323		"pass out" \
324		"block in" \
325		"pass in inet proto icmp all icmp-type echoreq"
326
327	# Both single packet & fragmented pass when we scrub
328	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
329	atf_check -s exit:0 -o ignore ping -c 1 -s 2000 192.0.2.2
330}
331
332reassemble_cleanup()
333{
334	pft_cleanup
335}
336
337atf_test_case "no_df" "cleanup"
338no_df_head()
339{
340	atf_set descr 'Test removing of DF flag'
341	atf_set require.user root
342}
343
344no_df_body()
345{
346	setup_router_server_ipv4
347
348	ifconfig ${epair_tester}a mtu 9000
349	jexec router ifconfig ${epair_tester}b mtu 9000
350	jexec router ifconfig ${epair_server}a mtu 1500
351	jexec server ifconfig ${epair_server}b mtu 1500
352
353	# Sanity check.
354	ping_server_check_reply exit:0 --ping-type=icmp
355
356	pft_set_rules router \
357		"set reassemble no" \
358		"pass out" \
359		"block in" \
360		"pass in inet proto icmp all icmp-type echoreq"
361
362	# Ping with normal, fragmentable packets.
363	ping_server_check_reply exit:1 --ping-type=icmp --send-length=2000
364
365	pft_set_rules router \
366		"set reassemble yes" \
367		"pass out" \
368		"block in" \
369		"pass in inet proto icmp all icmp-type echoreq"
370
371	# Ping with normal, fragmentable packets.
372	ping_server_check_reply exit:0 --ping-type=icmp --send-length=2000
373
374	# Ping with non-fragmentable packets.
375	ping_server_check_reply exit:1 --ping-type=icmp --send-length=2000 --send-flags DF
376
377	pft_set_rules router \
378		"set reassemble yes no-df" \
379		"pass out" \
380		"block in" \
381		"pass in inet proto icmp all icmp-type echoreq"
382
383	# Ping with non-fragmentable packets again.
384	# This time pf will strip the DF flag.
385	ping_server_check_reply exit:0 --ping-type=icmp --send-length=2000 --send-flags DF
386}
387no_df_cleanup()
388{
389	pft_cleanup
390}
391
392atf_test_case "no_df" "cleanup"
393no_df_head()
394{
395	atf_set descr 'Test removing of DF flag'
396	atf_set require.user root
397}
398
399no_df_body()
400{
401	setup_router_server_ipv4
402
403	# Tester can send long packets which will get fragmented by the router.
404	# Replies from server will come in fragments which might get
405	# reassembled resulting in a long reply packet sent back to tester.
406	ifconfig ${epair_tester}a mtu 9000
407	jexec router ifconfig ${epair_tester}b mtu 9000
408	jexec router ifconfig ${epair_server}a mtu 1500
409	jexec server ifconfig ${epair_server}b mtu 1500
410
411	# Sanity check.
412	ping_server_check_reply exit:0 --ping-type=icmp
413
414	# Enable packet reassembly with clearing of the no-df flag.
415	pft_set_rules router \
416		"scrub all fragment reassemble no-df" \
417		"block" \
418		"pass inet proto icmp all icmp-type echoreq"
419	# Ping with non-fragmentable packets.
420	# pf will strip the DF flag resulting in fragmentation and packets
421	# getting properly forwarded.
422	ping_server_check_reply exit:0 --ping-type=icmp --send-length=2000 --send-flags DF
423}
424no_df_cleanup()
425{
426	pft_cleanup
427}
428
429atf_test_case "reassemble_slowpath" "cleanup"
430reassemble_slowpath_head()
431{
432	atf_set descr 'Test reassembly on the slow path'
433	atf_set require.user root
434}
435
436reassemble_slowpath_body()
437{
438	if ! sysctl -q kern.features.ipsec >/dev/null ; then
439		atf_skip "This test requires ipsec"
440	fi
441
442	setup_router_server_ipv4
443
444	# Now define an ipsec policy so we end up taking the slow path.
445	# We don't actually need the traffic to go through ipsec, we just don't
446	# want to go through ip_tryforward().
447	echo "flush;
448	spdflush;
449	spdadd 203.0.113.1/32 203.0.113.2/32 any -P out ipsec esp/transport//require;
450	add 203.0.113.1 203.0.113.2 esp 0x1001 -E aes-gcm-16 \"12345678901234567890\";" \
451	    | jexec router setkey -c
452
453	# Sanity check.
454	ping_server_check_reply exit:0 --ping-type=icmp
455
456	# Enable packet reassembly with clearing of the no-df flag.
457	pft_set_rules router \
458		"scrub in on ${epair_tester}b fragment no reassemble" \
459		"scrub on ${epair_server}a fragment reassemble" \
460		"pass"
461
462	# Ensure that the packet makes it through the slow path
463	atf_check -s exit:0 -o ignore \
464	    ping -c 1 -s 2000 198.51.100.2
465}
466
467reassemble_slowpath_cleanup()
468{
469	pft_cleanup
470}
471
472atf_init_test_cases()
473{
474	atf_add_test_case "too_many_fragments"
475	atf_add_test_case "v6"
476	atf_add_test_case "mtu_diff"
477	atf_add_test_case "overreplace"
478	atf_add_test_case "overindex"
479	atf_add_test_case "overlimit"
480	atf_add_test_case "reassemble"
481	atf_add_test_case "no_df"
482	atf_add_test_case "reassemble_slowpath"
483}
484