1#	$OpenBSD: Makefile,v 1.10 2023/10/19 18:36:40 anton Exp $
2
3# Copyright (c) 2021 Alexander Bluhm <bluhm@openbsd.org>
4#
5# Permission to use, copy, modify, and distribute this software for any
6# purpose with or without fee is hereby granted, provided that the above
7# copyright notice and this permission notice appear in all copies.
8#
9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
17# Basic testing of the pflog(4) interface.  Create special routing
18# domain, load rules into pf(4) regress anchor, tcpdump on pflog,
19# send packets over lo(4), grep for expected result in tcpdump output.
20
21# This test uses routing domain 11 and pflog interface number 11, 12, 13.
22# Adjust it here, if you want to use something else.
23N1 =		11
24N2 =		12
25N3 =		13
26N =		${N1}
27NUMS =		${N1} ${N2} ${N3}
28IPS =		1 2 3 4 5 6 11 12 14
29
30UID !!=		id -u
31
32.include <bsd.own.mk>
33
34.if ! (make(clean) || make(cleandir) || make(obj))
35
36PF_STATUS !=	${SUDO} /sbin/pfctl -si | sed -n 's/^Status: \([^ ]*\) .*/\1/p'
37.if empty(PF_STATUS:MEnabled)
38regress:
39	@echo pf status: "${PF_STATUS}"
40	@echo Enable pf to run this regress.
41	@echo SKIPPED
42.endif
43
44PF_SKIP !=	${SUDO} /sbin/pfctl -sI -v | sed -n 's/ (skip)//p' | \
45		grep -w -e lo${N1} -e lo${N2} -e lo${N3} || :
46.if ! empty(PF_SKIP)
47regress:
48	@echo pf skip: "${PF_SKIP}"
49	@echo Do not set skip on interface lo, lo${N1}, lo${N2}, or lo${N3}.
50	@echo SKIPPED
51.endif
52
53PF_ANCHOR !=	${SUDO} /sbin/pfctl -sr |\
54		    sed -n 's/^anchor "\([^"]*\)" all$$/\1/p'
55.if empty(PF_ANCHOR:Mregress)
56regress:
57	@echo pf anchor: "${PF_ANCHOR}"
58	@echo Need anchor '"regress"' in pf.conf to load additional rules.
59	@echo SKIPPED
60.endif
61
62SYSCTL_FORWARDING !=	sysctl net.inet.ip.forwarding
63SYSCTL_FORWARDING6 !=	sysctl net.inet6.ip6.forwarding
64.if ${SYSCTL_FORWARDING:C/.*=//} != 1 || ${SYSCTL_FORWARDING6:C/.*=//} != 1
65# Do not skip, but run tests.  Although they fail, their packets are logged.
66REGRESS_EXPECTED_FAILURES =	run-ping-14 run-ping6-14
67.endif
68
69.endif
70
71.PHONY: busy-rdomains ifconfig unconfig pfctl
72
73REGRESS_SETUP_ONCE +=	busy-rdomains
74busy-rdomains:
75	# Check if rdomains are busy.
76	@if /sbin/ifconfig | grep -v '^lo$N:' | grep ' rdomain $N '; then\
77	    echo routing domain $N is already used >&2; exit 1; fi
78
79REGRESS_SETUP_ONCE +=	ifconfig
80ifconfig: unconfig
81	# Create and configure pflog and loopback interfaces.
82.for n in ${NUMS}
83	${SUDO} /sbin/ifconfig pflog$n create
84.endfor
85	${SUDO} /sbin/ifconfig lo$N rdomain $N
86	${SUDO} /sbin/ifconfig lo$N inet 127.0.0.1/8
87	${SUDO} /sbin/ifconfig lo$N inet6 ::1/128
88.for i in ${IPS} 21 22 23 24
89	${SUDO} /sbin/ifconfig lo$N inet 169.254.0.$i/32 alias
90	${SUDO} /sbin/ifconfig lo$N inet6 fc00::$i/128
91.endfor
92	# Wait until IPv6 addresses are no longer tentative.
93	for i in `jot 50`; do\
94	    if ! { /sbin/ifconfig pair${N1}; /sbin/ifconfig pair${N2};\
95		/sbin/ifconfig lo${N3}; } | fgrep -q tentative; then\
96		    break;\
97	    fi;\
98	    sleep .1;\
99	done
100	! { /sbin/ifconfig pair${N1}; /sbin/ifconfig pair${N2};\
101	    /sbin/ifconfig lo${N3}; } | fgrep tentative
102
103
104REGRESS_CLEANUP +=	unconfig
105unconfig: stamp-stop
106	# Destroy interfaces.
107	-${SUDO} /sbin/ifconfig lo$N rdomain $N
108.for i in ${IPS} 21 22 23 24
109	-${SUDO} /sbin/ifconfig lo$N inet 169.254.0.$i delete
110	-${SUDO} /sbin/ifconfig lo$N inet6 fc00::$i delete
111.endfor
112	-${SUDO} /sbin/ifconfig lo$N inet 127.0.0.1 delete
113	-${SUDO} /sbin/ifconfig lo$N inet6 ::1 delete
114.for n in ${NUMS}
115	-${SUDO} /sbin/ifconfig pflog$n destroy
116.endfor
117	-${SUDO} /sbin/ifconfig lo$N destroy
118	rm -f stamp-ifconfig
119
120addr.py: Makefile
121	# Create python include file containing the addresses.
122	rm -f $@ $@.tmp
123	echo 'N="$N"' >>$@.tmp
124	echo 'LO="lo$N"' >>$@.tmp
125.for var in N1 N2 N3
126	echo '${var}="${${var}}"' >>$@.tmp
127	echo 'PFLOG_${var}="pflog${${var}}"' >>$@.tmp
128.endfor
129	mv $@.tmp $@
130
131REGRESS_SETUP_ONCE +=	pfctl
132pfctl: addr.py pf.conf
133	# Load the pf rules into the kernel.
134	cat addr.py ${.CURDIR}/pf.conf | /sbin/pfctl -n -f -
135	cat addr.py ${.CURDIR}/pf.conf | ${SUDO} /sbin/pfctl -a regress -f -
136
137# Run tcpdump on pflog devices.
138DUMPCMD =	/usr/sbin/tcpdump -l -e -vvv -s 2048 -ni
139
140stamp-bpf: stamp-bpf-${N1} stamp-bpf-${N2} stamp-bpf-${N3}
141	sleep 2  # XXX
142	@date >$@
143
144.for n in ${NUMS}
145
146stamp-bpf-$n: stamp-ifconfig
147	rm -f pflog$n.tcpdump
148	${SUDO} pkill -f '^${DUMPCMD} pflog$n' || true
149	${SUDO} ${DUMPCMD} pflog$n >pflog$n.tcpdump &
150	rm -f stamp-stop
151	@date >$@
152
153.endfor
154
155stamp-stop:
156	sleep 2  # XXX
157	-${SUDO} pkill -f '^${DUMPCMD}'
158	rm -f stamp-bpf*
159	@date >$@
160
161.for i in ${IPS}
162REGRESS_TARGETS +=	run-ping-$i
163run-ping-$i: stamp-bpf
164	/sbin/ping -n -w 1 -c 1 -V $N 169.254.0.$i
165
166REGRESS_TARGETS +=	run-ping6-$i
167run-ping6-$i: stamp-bpf
168	/sbin/ping6 -n -w 1 -c 1 -V $N fc00::$i
169
170REGRESS_TARGETS +=	run-udp-$i
171run-udp-$i: stamp-bpf
172	# ignore errors, just send packet fast
173	echo foo | nc -u -w 1 -V $N 169.254.0.$i discard &
174
175REGRESS_TARGETS +=	run-udp6-$i
176run-udp6-$i: stamp-bpf
177	# ignore errors, just send packet fast
178	echo foo | nc -u -w 1 -V $N fc00::$i discard &
179.endfor
180
181REGRESS_TARGETS +=	run-ping6-0
182run-ping6-0: stamp-bpf
183	/sbin/ping6 -n -w 1 -c 1 -V $N ::1
184
185REGRESS_TARGETS +=	run-udp6-0
186run-udp6-0: stamp-bpf
187	echo foo | nc -u -w 1 -V $N ::1 discard
188
189.for n in ${NUMS}
190REGRESS_TARGETS +=	run-bpf-$n
191run-bpf-$n: stamp-stop
192	# show full logs
193	cat pflog$n.tcpdump
194.endfor
195
196REGRESS_TARGETS +=	run-bpf-nothing
197run-bpf-nothing: stamp-stop
198	# rule with pflog${N3} is never used
199	! grep . pflog${N3}.tcpdump
200
201REGRESS_TARGETS +=	run-bpf-everything
202run-bpf-everything: stamp-stop
203	# rule with pflog${N2} matches on every packet
204.for i in ${IPS}
205	grep 'regress\.1/.* > 169.254.0.$i:' pflog${N2}.tcpdump
206.endfor
207
208REGRESS_TARGETS +=	run-bpf-everything6
209run-bpf-everything6: stamp-stop
210	# rule with pflog${N2} matches on every packet
211.for i in ${IPS}
212	grep 'regress\.1/.* > fc00::$i:' pflog${N2}.tcpdump
213.endfor
214
215REGRESS_TARGETS +=	run-bpf-all
216run-bpf-all: stamp-stop
217	# reply without keep state
218	grep 'regress\.3/.* 169.254.0.1 > 169.254.0.1:\
219	    icmp: echo request' pflog${N1}.tcpdump
220	grep 'regress\.3/.* 169.254.0.1 > 169.254.0.1:\
221	    icmp: echo reply' pflog${N1}.tcpdump
222	# no reply with keep state and without all
223	grep 'regress\.4/.* 169.254.0.2 > 169.254.0.2:\
224	    icmp: echo request' pflog${N1}.tcpdump
225	! grep 'regress\.4/.* 169.254.0.2 >169.254.0.2:\
226	    icmp: echo reply' pflog${N1}.tcpdump
227	# reply with keep state and with all
228	grep 'regress\.5/.* 169.254.0.3 > 169.254.0.3:\
229	    icmp: echo request' pflog${N1}.tcpdump
230	# XXX anchor name missing
231	grep '/.* 169.254.0.3 > 169.254.0.3:\
232	    icmp: echo reply' pflog${N1}.tcpdump
233
234REGRESS_TARGETS +=	run-bpf-all6
235run-bpf-all6: stamp-stop
236	# reply without keep state
237	grep 'regress\.11/.* fc00::1 > fc00::1:\
238	    icmp6: echo request' pflog${N1}.tcpdump
239	grep 'regress\.11/.* fc00::1 > fc00::1:\
240	    icmp6: echo reply' pflog${N1}.tcpdump
241	# no reply with keep state and without all
242	grep 'regress\.12/.* fc00::2 > fc00::2:\
243	    icmp6: echo request' pflog${N1}.tcpdump
244	! grep 'regress\.12/.* fc00::2 > fc00::2:\
245	    icmp6: echo reply' pflog${N1}.tcpdump
246	# reply with keep state and with all
247	grep 'regress\.13/.* fc00::3 > fc00::3:\
248	    icmp6: echo request' pflog${N1}.tcpdump
249	# XXX anchor name missing
250	grep '/.* fc00::3 > fc00::3:\
251	    icmp6: echo reply' pflog${N1}.tcpdump
252
253REGRESS_TARGETS +=	run-bpf-user
254run-bpf-user: stamp-stop
255	# out rule creates log entry with uid
256	grep 'regress\.6/.* pass out on lo$N: \[uid ${UID}, pid [0-9]*\]\
257	    169.254.0.4\.[0-9]* > 169.254.0.4\.9:\
258	    .* udp [0-9]' pflog${N1}.tcpdump
259	# in rule has no uid at log entry
260	grep 'regress\.6/.* pass in on lo$N:\
261	    169.254.0.4\.[0-9]* > 169.254.0.4\.9:\
262	    .* udp [0-9]' pflog${N1}.tcpdump
263	# icmp has no uid at log entry
264	grep 'regress\.6/.* pass out on lo$N:\
265	    169.254.0.4 > 169.254.0\.4:\
266	    icmp: echo request' pflog${N1}.tcpdump
267	# rule without user has no uid in log entry
268	grep 'regress\.3/.* pass out on lo$N:\
269	    169.254.0.1\.[0-9]* > 169.254.0.1\.9:\
270	    .* udp [0-9]' pflog${N1}.tcpdump
271
272REGRESS_TARGETS +=	run-bpf-user6
273run-bpf-user6: stamp-stop
274	# out rule creates log entry with uid
275	grep 'regress\.14/.* pass out on lo$N: \[uid ${UID}, pid [0-9]*\]\
276	    fc00::4\.[0-9]* > fc00::4\.9:.* udp [0-9]' pflog${N1}.tcpdump
277	# in rule has no uid at log entry
278	grep 'regress\.14/.* pass in on lo$N:\
279	    fc00::4\.[0-9]* > fc00::4\.9:.* udp [0-9]' pflog${N1}.tcpdump
280	# icmp has no uid at log entry
281	grep 'regress\.14/.* pass out on lo$N:\
282	    fc00::4 > fc00::4: icmp6: echo request' pflog${N1}.tcpdump
283	# rule without user has no uid in log entry
284	grep 'regress\.11/.* pass out on lo$N:\
285	    fc00::1\.[0-9]* > fc00::1\.9:.* udp [0-9]' pflog${N1}.tcpdump
286
287run-bpf-matches run-bpf-matches6:
288	# XXX The log matches keyword seems to be totally broken.
289	# pf_log_matches() is never called.  Investigate later.
290	@echo DISABLED
291
292REGRESS_TARGETS +=	run-bpf-matches
293run-bpf-matches: stamp-stop
294	grep 'regress\.9/.* .*: 169.254.0.6 > 169.254.0.6:\
295	    icmp: echo request' pflog${N1}.tcpdump
296	! grep 'regress\.8/.* icmp: echo request' pflog${N1}.tcpdump
297	! grep 'regress\.7/.* icmp: echo request' pflog${N1}.tcpdump
298
299REGRESS_TARGETS +=	run-bpf-rdr
300run-bpf-rdr: stamp-stop
301	# loopback input logs redirected packet
302	grep 'regress\.2/.* pass in .*:.* 169.254.0.11 > 169.254.0.21:\
303	    icmp: echo request' pflog${N1}.tcpdump
304	# loopback output redirects and logs original packet
305	grep 'regress\.18/.* pass out .*:.* 169.254.0.11 > 169.254.0.11:\
306	    icmp: echo request' pflog${N1}.tcpdump
307
308REGRESS_TARGETS +=	run-bpf-rdr6
309run-bpf-rdr6: stamp-stop
310	# loopback input logs redirected packet
311	grep 'regress\.10/.* pass in .*:.* fc00::11 > fc00::21:\
312	    icmp6: echo request' pflog${N1}.tcpdump
313	# loopback output redirects and logs original packet
314	grep 'regress\.20/.* pass out .*:.* fc00::11 > fc00::11:\
315	    icmp6: echo request' pflog${N1}.tcpdump
316
317REGRESS_TARGETS +=	run-bpf-nat
318run-bpf-nat: stamp-stop
319	# loopback input logs redirected packet
320	grep 'regress\.2/.* pass in .*:.* 169.254.0.22 > 169.254.0.12:\
321	    icmp: echo request' pflog${N1}.tcpdump
322	# loopback output redirects and logs original packet
323	grep 'regress\.19/.* pass out .*:.* 169.254.0.12 > 169.254.0.12:\
324	    icmp: echo request' pflog${N1}.tcpdump
325
326REGRESS_TARGETS +=	run-bpf-nat6
327run-bpf-nat6: stamp-stop
328	# loopback input logs redirected packet
329	grep 'regress\.10/.* pass in .*:.* fc00::22 > fc00::12:\
330	    icmp6: echo request' pflog${N1}.tcpdump
331	# loopback output redirects and logs original packet
332	grep 'regress\.21/.* pass out .*:.* fc00::12 > fc00::12:\
333	    icmp6: echo request' pflog${N1}.tcpdump
334
335REGRESS_TARGETS +=	run-bpf-af
336run-bpf-af: stamp-stop
337	# pf in rule logs original IPv4 packet
338	grep 'regress\.22/.* pass in .*:.* 169.254.0.14 > 169.254.0.14:\
339	     icmp: echo request' pflog${N1}.tcpdump
340
341REGRESS_TARGETS +=	run-bpf-af6
342run-bpf-af6: stamp-stop
343	# pf in rule logs original IPv6 packet
344	grep 'regress\.23/.* pass in .*:.* fc00::14 > fc00::14:\
345	     icmp6: echo request' pflog${N1}.tcpdump
346
347REGRESS_TARGETS +=	run-bpf-rewrite
348run-bpf-rewrite: stamp-stop
349	# rdr-to address has been rewritten
350	grep '\[rewritten: src 169.254.0.11:[0-9]*, dst 169.254.0.21:[0-9]*\]\
351	    169.254.0.11 > 169.254.0.11' pflog${N1}.tcpdump
352	# nat-to address has been rewritten
353	grep '\[rewritten: src 169.254.0.22:[0-9]*, dst 169.254.0.12:[0-9]*\]\
354	    169.254.0.12 > 169.254.0.12' pflog${N1}.tcpdump
355	# af-to address has been rewritten
356	grep '\[rewritten: src fc00::23:[0-9]*, dst fc00::24:[0-9]*\]\
357	    169.254.0.14 > 169.254.0.14' pflog${N1}.tcpdump
358
359REGRESS_TARGETS +=	run-bpf-rewrite6
360run-bpf-rewrite6: stamp-stop
361	# rdr-to address has been rewritten
362	grep '\[rewritten: src fc00::11:[0-9]*, dst fc00::21:[0-9]*\]\
363	    fc00::11 > fc00::11' pflog${N1}.tcpdump
364	# nat-to address has been rewritten
365	grep '\[rewritten: src fc00::22:[0-9]*, dst fc00::12:[0-9]*\]\
366	    fc00::12 > fc00::12' pflog${N1}.tcpdump
367	# af-to address has been rewritten
368	grep '\[rewritten: src 169.254.0.23:[0-9]*, dst 169.254.0.24:[0-9]*\]\
369	    fc00::14 > fc00::14' pflog${N1}.tcpdump
370
371CLEANFILES +=	addr.py *.pyc *.tcpdump *.log stamp-*
372
373.include <bsd.regress.mk>
374