# $OpenBSD: Makefile,v 1.7 2023/10/11 18:07:56 anton Exp $ # Copyright (c) 2022 Alexander Bluhm # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # Set up two loopback interfaces in different routing domains. # One loopback interface has a allow-opts pf rule, the other has # default pass policy. Send packets with IP options and IPv6 # option header and check wheter tcpdump finds them on lo or pflog. # The following ports must be installed: # # scapy powerful interactive packet manipulation in python .if ! exists(/usr/local/bin/scapy) regress: @echo Install scapy package to run this regress. @echo SKIPPED .endif # This test uses routing domain and interface number 11 and 12. # Adjust it here, if you want to use something else. N1 = 11 N2 = 12 NUMS = ${N1} ${N2} .include .if ! (make(clean) || make(cleandir) || make(obj)) PF_STATUS != ${SUDO} /sbin/pfctl -si | sed -n 's/^Status: \([^ ]*\) .*/\1/p' .if empty(PF_STATUS:MEnabled) regress: @echo pf status: "${PF_STATUS}" @echo Enable pf to run this regress. @echo SKIPPED .endif PF_SKIP != ${SUDO} /sbin/pfctl -sI -v | sed -n 's/ (skip)//p' | \ grep -w -e lo${N1} -e lo${N2} || : .if ! empty(PF_SKIP) regress: @echo pf skip: "${PF_SKIP}" @echo Do not set skip on interface lo, lo${N1}, or lo${N2}. @echo SKIPPED .endif PF_ANCHOR != ${SUDO} /sbin/pfctl -sr |\ sed -n 's/^anchor "\([^"]*\)" all$$/\1/p' .if empty(PF_ANCHOR:Mregress) regress: @echo pf anchor: "${PF_ANCHOR}" @echo Need anchor '"regress"' in pf.conf to load additional rules. @echo SKIPPED .endif .endif .PHONY: busy-rdomains ifconfig unconfig pfctl REGRESS_SETUP_ONCE += busy-rdomains busy-rdomains: # Check if rdomains are busy. .for n in ${NUMS} @if /sbin/ifconfig | grep -v '^lo$n:' | grep ' rdomain $n '; then\ echo routing domain $n is already used >&2; exit 1; fi .endfor REGRESS_SETUP_ONCE += ifconfig ifconfig: unconfig # Create and configure loopback interfaces. .for n in ${NUMS} ${SUDO} /sbin/ifconfig lo$n rdomain $n ${SUDO} /sbin/ifconfig lo$n inet 127.0.0.1/8 ${SUDO} /sbin/ifconfig lo$n inet 127.0.0.$n alias ${SUDO} /sbin/ifconfig lo$n inet6 ::1/128 ${SUDO} /sbin/ifconfig lo$n inet6 fe80::$n/64 ${SUDO} /sbin/route -n -T $n add -inet 224.0.0.0/4 127.0.0.1 .endfor # Wait until IPv6 addresses are no longer tentative. for i in `jot 50`; do\ if ! { /sbin/ifconfig lo${N1}; /sbin/ifconfig lo${N2}; }\ | fgrep -q tentative; then\ break;\ fi;\ sleep .1;\ done ! { /sbin/ifconfig lo${N1}; /sbin/ifconfig lo${N2}; }\ | fgrep tentative REGRESS_CLEANUP += unconfig unconfig: stamp-stop # Destroy interfaces. .for n in ${NUMS} -${SUDO} /sbin/ifconfig lo$n rdomain $n -${SUDO} /sbin/ifconfig lo$n inet 127.0.0.1 delete -${SUDO} /sbin/ifconfig lo$n inet 127.0.0.$n delete -${SUDO} /sbin/ifconfig lo$n inet6 ::1 delete -${SUDO} /sbin/ifconfig lo$n inet6 fe80::$n/64 delete .endfor rm -f stamp-ifconfig addr.py: Makefile # Create python include file containing the addresses. rm -f $@ $@.tmp .for var in N1 N2 echo '${var}="${${var}}"' >>$@.tmp echo 'IF_${var}="lo${${var}}"' >>$@.tmp echo 'ADDR_${var}="127.0.0.${${var}}"' >>$@.tmp echo 'ADDR6_${var}="fe80::${${var}}"' >>$@.tmp .endfor mv $@.tmp $@ REGRESS_SETUP_ONCE += pfctl pfctl: addr.py pf.conf # Load the pf rules into the kernel. cat addr.py ${.CURDIR}/pf.conf | /sbin/pfctl -n -f - cat addr.py ${.CURDIR}/pf.conf | ${SUDO} /sbin/pfctl -a regress -f - # run tcpdump on lo and pflog device DUMPCMD = /usr/sbin/tcpdump -l -e -vvv -s 2048 -ni stamp-bpf: stamp-bpf-lo${N1} stamp-bpf-lo${N2} stamp-bpf-pflog0 sleep 2 # XXX @date >$@ .for i in lo${N1} lo${N2} pflog0 stamp-bpf-$i: stamp-ifconfig rm -f $i.tcpdump ${SUDO} pkill -f '^${DUMPCMD} $i' || true ${SUDO} ${DUMPCMD} $i >$i.tcpdump & rm -f stamp-stop @date >$@ .endfor stamp-stop: sleep 2 # XXX -${SUDO} pkill -f '^${DUMPCMD}' rm -f stamp-bpf* @date >$@ # Set variables so that make runs with and without obj directory. # Only do that if necessary to keep visible output short. .if ${.CURDIR} == ${.OBJDIR} PYTHON = python3 -u ./ .else PYTHON = env PYTHONPATH=${.OBJDIR} python3 -u ${.CURDIR}/ .endif # ping REGRESS_TARGETS += run-ping run-ping: stamp-bpf # Ping localhost on loopback /sbin/ping -n -w 1 -c 1 -V ${N1} 127.0.0.${N1} /sbin/ping -n -w 1 -c 1 -V ${N2} 127.0.0.${N2} REGRESS_TARGETS += run-ping6 run-ping6: stamp-bpf # Ping localhost on loopback /sbin/ping6 -n -w 1 -c 1 -V ${N1} fe80::${N1}%lo${N1} /sbin/ping6 -n -w 1 -c 1 -V ${N2} fe80::${N2}%lo${N2} REGRESS_TARGETS += run-bpf-ping run-bpf-ping: stamp-stop # Check that ping packet went through loopback. grep ' 127.0.0.${N1}: icmp: echo request' lo${N1}.tcpdump grep ' 127.0.0.${N2}: icmp: echo request' lo${N2}.tcpdump grep ' fe80:.*::${N1}: icmp6: echo request' lo${N1}.tcpdump grep ' fe80:.*::${N2}: icmp6: echo request' lo${N2}.tcpdump ! grep ': icmp: echo request' pflog0.tcpdump ! grep ': icmp6: echo request' pflog0.tcpdump # ping with RR option REGRESS_TARGETS += run-ping-record run-ping-record: stamp-bpf # Ping localhost with record route option /sbin/ping -n -w 1 -c 1 -V ${N1} -R 127.0.0.${N1} ! /sbin/ping -n -w 1 -c 1 -V ${N2} -R 127.0.0.${N2} REGRESS_TARGETS += run-bpf-ping-record run-bpf-ping-record: stamp-stop # Check that ping packet with options is in pflog0. grep ' 127.0.0.${N1}: icmp: echo request .*\ optlen=40 RR' lo${N1}.tcpdump grep ' 127.0.0.${N2}: icmp: echo request .*\ optlen=40 RR' pflog0.tcpdump # icmp REGRESS_TARGETS += run-icmp run-icmp: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp.py N2 REGRESS_TARGETS += run-icmp6 run-icmp6: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp6.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp6.py N2 REGRESS_TARGETS += run-bpf-icmp run-bpf-icmp: stamp-stop # Check that icmp packet went through loopback. grep ' 127.0.0.${N1}: icmp: type-#6' lo${N1}.tcpdump grep ' 127.0.0.${N2}: icmp: type-#6' lo${N2}.tcpdump grep ' fe80::${N1}: icmp6: type-#6' lo${N1}.tcpdump grep ' fe80::${N2}: icmp6: type-#6' lo${N2}.tcpdump ! grep ': icmp: type-#6' pflog0.tcpdump ! grep ': icmp6: type-#6' pflog0.tcpdump # option extension header REGRESS_TARGETS += run-icmp6-hop run-icmp6-hop: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp6_hop.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp6_hop.py N2 REGRESS_TARGETS += run-icmp6-dst run-icmp6-dst: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp6_dst.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp6_dst.py N2 REGRESS_TARGETS += run-bpf-ext run-bpf-ext: stamp-stop # Check that icmp6 packet with extension headers were blocked fgrep ' fe80::${N2}: HBH icmp6:' pflog0.tcpdump fgrep ' fe80::${N2}: DSTOPT icmp6:' pflog0.tcpdump ! grep fe80::${N1} pflog0.tcpdump # icmp with options REGRESS_TARGETS += run-icmp-pad run-icmp-pad: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp_pad.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp_pad.py N2 REGRESS_TARGETS += run-icmp-eol run-icmp-eol: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp_eol.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp_eol.py N2 REGRESS_TARGETS += run-icmp6-pad run-icmp6-pad: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp6_hop_pad.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp6_hop_pad.py N2 REGRESS_TARGETS += run-icmp-max run-icmp-max: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp_max.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp_max.py N2 REGRESS_TARGETS += run-icmp6-max run-icmp6-max: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp6_hop_max.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp6_hop_max.py N2 REGRESS_TARGETS += run-icmp-ra run-icmp-ra: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp_ra.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp_ra.py N2 REGRESS_TARGETS += run-icmp6-ra run-icmp6-ra: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp6_hop_ra.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp6_hop_ra.py N2 REGRESS_TARGETS += run-icmp-bad run-icmp-bad: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp_bad.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp_bad.py N2 REGRESS_TARGETS += run-icmp6-bad run-icmp6-bad: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp6_hop_bad.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp6_hop_bad.py N2 REGRESS_TARGETS += run-bpf-opts run-bpf-opts: stamp-stop # Check that icmp packet with options were blocked grep ' 127.0.0.${N2}:.* optlen=4 NOP NOP NOP NOP)' pflog0.tcpdump grep ' 127.0.0.${N2}:.* optlen=4 NOP EOL-2)' pflog0.tcpdump grep ' 127.0.0.${N2}:.* optlen=40 NOP ' pflog0.tcpdump grep ' 127.0.0.${N2}:.* optlen=8 NOP IPOPT-148{4} NOP ' pflog0.tcpdump grep ' 127.0.0.${N2}:.* optlen=4 IPOPT-3{4})' pflog0.tcpdump grep ' fe80::${N2}: HBH icmp6:.* (len 28,' pflog0.tcpdump grep ' fe80::${N2}: HBH icmp6:.* (len 284,' pflog0.tcpdump grep ' fe80::${N2}: HBH (rtalert: 0x0000) icmp6:' pflog0.tcpdump grep ' fe80::${N2}: HBH (type 0x03: len=0) icmp6:' pflog0.tcpdump ! grep '127.0.0.${N1}' pflog0.tcpdump ! grep 'fe80::${N1}' pflog0.tcpdump # multicast with router alert REGRESS_TARGETS += run-igmp run-igmp: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}igmp_ra.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}igmp_ra.py N2 REGRESS_TARGETS += run-icmp6-mld run-icmp6-mld: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp6_mld_ra.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp6_mld_ra.py N2 REGRESS_TARGETS += run-bpf-mcast run-bpf-mcast: stamp-stop # Check that multicast protocol packet with router alert passed grep '127.0.0.${N2} > 224.0.0.1:\ igmp query .* IPOPT-148{4}' lo${N2}.tcpdump grep 'fe80::${N2} > ff02::1:\ HBH (rtalert:.* icmp6: multicast ' lo${N2}.tcpdump ! grep '127.0.0.${N1}' pflog0.tcpdump ! grep 'fe80::${N1}' pflog0.tcpdump ! grep '127.0.0.${N2}' pflog0.tcpdump ! grep 'fe80::${N2}' pflog0.tcpdump ! grep '224.0.0.1' pflog0.tcpdump ! grep 'ff02::1' pflog0.tcpdump REGRESS_TARGETS += run-igmp-bad run-igmp-bad: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}igmp_bad.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}igmp_bad.py N2 REGRESS_TARGETS += run-icmp6-mld-bad run-icmp6-mld-bad: stamp-bpf ${SUDO} /sbin/route -T ${N1} exec ${PYTHON}icmp6_mld_bad.py N1 ${SUDO} /sbin/route -T ${N2} exec ${PYTHON}icmp6_mld_bad.py N2 REGRESS_TARGETS += run-bpf-mcast-bad run-bpf-mcast-bad: stamp-stop # Check that multicast protocol packet with options were blocked grep '127.0.0.${N2} > 224.0.0.1:\ igmp query .* IPOPT-3{4}' pflog0.tcpdump grep 'fe80::${N2} > ff02::1:\ HBH (type 0x03:.* icmp6: multicast ' pflog0.tcpdump ! grep '127.0.0.${N1}' pflog0.tcpdump ! grep 'fe80::${N1}' pflog0.tcpdump CLEANFILES += addr.py *.pyc *.tcpdump *.log stamp-* .include