diff options
author | 2016-10-19 16:39:50 +0000 | |
---|---|---|
committer | 2016-10-19 16:39:50 +0000 | |
commit | 67cf0727096c9d385dc23ba2d512e862a4e3941d (patch) | |
tree | 08a53c11258a030553958219c1fd42523555a947 | |
parent | There used to be disabled code that used /dev/tty to determine the (diff) | |
download | wireguard-openbsd-67cf0727096c9d385dc23ba2d512e862a4e3941d.tar.xz wireguard-openbsd-67cf0727096c9d385dc23ba2d512e862a4e3941d.zip |
Add a test suite for pf state handling. It needs a remote machine
to exchange packets, the remote kernel is tested. For now it only
contains a challenge ack test written by sashan@.
OK sashan@
-rw-r--r-- | regress/sys/net/pf_state/LICENSE | 14 | ||||
-rw-r--r-- | regress/sys/net/pf_state/Makefile | 123 | ||||
-rw-r--r-- | regress/sys/net/pf_state/README | 63 | ||||
-rw-r--r-- | regress/sys/net/pf_state/challenge_ack.py | 68 | ||||
-rw-r--r-- | regress/sys/net/pf_state/pf.conf | 3 |
5 files changed, 271 insertions, 0 deletions
diff --git a/regress/sys/net/pf_state/LICENSE b/regress/sys/net/pf_state/LICENSE new file mode 100644 index 00000000000..d38be67275f --- /dev/null +++ b/regress/sys/net/pf_state/LICENSE @@ -0,0 +1,14 @@ +# Copyright (c) 2016 Alexandr Nedvedicky <sashan@openbsd.org> +# Copyright (c) 2015-2016 Alexander Bluhm <bluhm@openbsd.org> +# +# 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. diff --git a/regress/sys/net/pf_state/Makefile b/regress/sys/net/pf_state/Makefile new file mode 100644 index 00000000000..fa58810dd72 --- /dev/null +++ b/regress/sys/net/pf_state/Makefile @@ -0,0 +1,123 @@ +# $OpenBSD: Makefile,v 1.1.1.1 2016/10/19 16:39:50 bluhm Exp $ + +# The following ports must be installed: +# +# python-2.7 interpreted object-oriented programming language +# py-libdnet python interface to libdnet +# scapy powerful interactive packet manipulation in python + +.if ! (make(clean) || make(cleandir) || make(obj)) +# Check wether all required python packages are installed. If some +# are missing print a warning and skip the tests, but do not fail. +PYTHON_IMPORT != python2.7 -c 'from scapy.all import *' 2>&1 || true +.endif +.if ! empty(PYTHON_IMPORT) +regress: + @echo '${PYTHON_IMPORT}' + @echo install python and the scapy module for additional tests + @echo SKIPPED +.endif + +# This test needs a manual setup of two machines +# Set up machines: LOCAL REMOTE +# LOCAL is the machine where this makefile is running. +# REMOTE is running OpenBSD with echo and chargen server to test PMTU +# FAKE is an non existing machine in a non existing network. +# REMOTE_SSH is the hostname to log in on the REMOTE machine. + +# Configure Addresses on the machines. +# Adapt interface and addresse variables to your local setup. +# +LOCAL_IF ?= +REMOTE_SSH ?= + +LOCAL_ADDR ?= +REMOTE_ADDR ?= +FAKE_NET ?= +FAKE_NET_ADDR ?= + +.if empty (LOCAL_IF) || empty (REMOTE_SSH) || \ + empty (LOCAL_ADDR) || \ + empty (REMOTE_ADDR) || \ + empty (FAKE_NET) || \ + empty (FAKE_NET_ADDR) +regress: + @echo This tests needs a remote machine to operate on + @echo LOCAL_IF REMOTE_SSH LOCAL_ADDR REMOTE_ADDR FAKE_NET FAKE_NET_ADDR + @echo are empty. Fill out these variables for additional tests. + @echo SKIPPED +.endif + +.MAIN: all + +.if make (regress) || make (all) +.BEGIN: pf.conf addr.py + @echo + ${SUDO} true + ssh -t ${REMOTE_SSH} ${SUDO} true +.endif + +depend: addr.py + +# Create python include file containing the addresses. +addr.py: Makefile + rm -f $@ $@.tmp + echo 'LOCAL_IF = "${LOCAL_IF}"' >>$@.tmp +.for var in LOCAL REMOTE FAKE_NET + echo '${var}_ADDR = "${${var}_ADDR}"' >>$@.tmp +.endfor + echo 'FAKE_NET = "${FAKE_NET}"' >>$@.tmp + mv $@.tmp $@ + +# load the pf rules into the kernel of the REMOTE machine +stamp-pfctl: addr.py pf.conf + cat addr.py ${.CURDIR}/pf.conf | pfctl -n -f - + cat addr.py ${.CURDIR}/pf.conf | \ + ssh ${REMOTE_SSH} ${SUDO} pfctl -a regress -f - + @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 = python2.7 -u ./ +.else +PYTHON = PYTHONPATH=${.OBJDIR} python2.7 -u ${.CURDIR}/ +.endif + +TARGETS += challenge-ack +run-regress-challenge-ack: stamp-pfctl + @echo '\n======== $@ ========' + ${SUDO} ${PYTHON}challenge_ack.py ${FAKE_NET_ADDR} ${REMOTE_ADDR} + +REGRESS_TARGETS = ${TARGETS:S/^/run-regress-/} + +CLEANFILES += addr.py *.pyc *.log stamp-* + +.PHONY: check-setup check-setup-local check-setup-remote + +# Check wether the address, route and remote setup is correct +check-setup: check-setup-local check-setup-remote + +check-setup-local: + @echo '\n======== $@ ========' + ping -n -c 1 ${LOCAL_ADDR} # LOCAL_ADDR + route -n get -inet ${LOCAL_ADDR} | grep -q 'flags: .*LOCAL' # LOCAL_ADDR + ping -n -c 1 ${REMOTE_ADDR} # REMOTE_ADDR + route -n get -inet ${REMOTE_ADDR} | fgrep -q 'interface: ${LOCAL_IF}' # REMOTE_ADDR LOCAL_IF + ! ping -n -c 1 -w 1 ${FAKE_NET_ADDR} # FAKE_NET_ADDR + route -n get -inet ${FAKE_NET_ADDR} | grep -q 'flags: .*BLACKHOLE' # FAKE_NET_ADDR + route -n get -inet -net ${FAKE_NET} | grep -q 'flags: .*BLACKHOLE' # FAKE_NET + +check-setup-remote: + @echo '\n======== $@ ========' + ssh ${REMOTE_SSH} ping -n -c 1 ${REMOTE_ADDR} # REMOTE_ADDR + ssh ${REMOTE_SSH} route -n get -inet ${REMOTE_ADDR} | grep -q 'flags: .*LOCAL' # REMOTE_ADDR + ssh ${REMOTE_SSH} ping -n -c 1 ${LOCAL_ADDR} # LOCAL_ADDR +.for ip in FAKE_NET FAKE_NET_ADDR + ssh ${REMOTE_SSH} route -n get -inet ${${ip}} | fgrep -q 'gateway: ${LOCAL_ADDR}' # ${ip} LOCAL_ADDR +.endfor + ssh ${ECO_SSH} netstat -a -f inet -p tcp | fgrep ' *.echo ' + ssh ${REMOTE_SSH} ${SUDO} pfctl -sr | grep '^anchor "regress" all$$' + ssh ${REMOTE_SSH} ${SUDO} pfctl -si | grep '^Status: Enabled ' + +.include <bsd.regress.mk> diff --git a/regress/sys/net/pf_state/README b/regress/sys/net/pf_state/README new file mode 100644 index 00000000000..d3c57f58b6a --- /dev/null +++ b/regress/sys/net/pf_state/README @@ -0,0 +1,63 @@ +Regression tests for pf tcp state. + +The test suite runs on the machine LOCAL, the kernel under test is +running on REMOTE. On LOCAL a Scapy program is simulating a +connection to REMOTE TCP echo service. The source address is a non +existing address on FAKE_NET. The LOCAL machine acts as a router +between REMOTE and virtual FAKE_NET_ADDR and can create ICMP packets. + +The run-regress-challenge-ack subtest checks that the pf firewall +sends TCP a challenge Ack if the sequence number is out of range. + +EXAMPLE + +To run this test I use the following configuration files. +You should choose a different set of MAC and IP addresses. + +- My local machine where I run the regression test: + +/etc/hosts +# to login to qemu with SSH via IPv6 link-local +fe80::725f:caff:fe21:8d70%tap0 q70 + +cat /etc/hostname.tap4 +lladdr fe:e1:ba:d0:d5:6d up +inet 10.188.211.17 255.255.255.0 +inet6 fdd7:e83e:66bc:211::17 +!route add -inet 10.188.219.0/24 127.0.0.1 -blackhole +!route add -inet6 fdd7:e83e:66bc:219::/64 ::1 -blackhole + +- My qemu where the kernel under test is running + +/etc/hostname.vio1 +lladdr 70:5f:ca:21:8d:80 +inet 10.188.211.70 255.255.255.0 +inet6 fdd7:e83e:66bc:211::70 +!route add -inet 10.188.219.0/24 10.188.211.17 +!route add -inet6 fdd7:e83e:66bc:219::/64 fdd7:e83e:66bc:211::17 + +/etc/inetd.conf +chargen stream tcp nowait root internal +chargen stream tcp6 nowait root internal +echo dgram udp6 wait root internal + +/etc/rc.conf.local +inetd_flags= +sshd_flags= + +LOCAL_IF=tap4 +LOCAL_MAC=fe:e1:ba:d0:d5:6d +REMOTE_MAC=70:5f:ca:21:8d:80 +REMOTE_SSH=q70 + +LOCAL_ADDR=10.188.211.17 +REMOTE_ADDR=10.188.211.70 +FAKE_NET=10.188.219.0/24 +FAKE_NET_ADDR=10.188.219.188 + +LOCAL_ADDR6=fdd7:e83e:66bc:211::17 +REMOTE_ADDR6=fdd7:e83e:66bc:211::70 +FAKE_NET6=fdd7:e83e:66bc:219::/64 +FAKE_NET_ADDR6=fdd7:e83e:66bc:219::188 + +- Fix your configuration until make check-setup passes diff --git a/regress/sys/net/pf_state/challenge_ack.py b/regress/sys/net/pf_state/challenge_ack.py new file mode 100644 index 00000000000..1e73492b877 --- /dev/null +++ b/regress/sys/net/pf_state/challenge_ack.py @@ -0,0 +1,68 @@ +#!/usr/local/bin/python2.7 +# check wether path mtu to dst is as expected + +import os +import threading +from addr import * +from scapy.all import * + +# usage: challenge_ack.py src dst + +# +# we can not use scapy's sr() function as receive side +# ignores the packet we expect to see. Packet is ignored +# due to mismatching sequence numbers. 'bogus_syn' is using +# seq = 1000000, while response sent back by PF has ack, +# which fits regular session opened by 'syn'. +# +class Sniff(threading.Thread): + captured = None + def run(self): + self.captured = sniff(iface=LOCAL_IF, + filter='tcp src port 7', timeout=3) + +srcaddr=sys.argv[1] +dstaddr=sys.argv[2] +port=os.getpid() & 0xffff + +ip=IP(src=srcaddr, dst=dstaddr) + +print "Send SYN packet, receive SYN+ACK" +syn=TCP(sport=port, dport='echo', seq=1, flags='S', window=(2**16)-1) +synack=sr1(ip/syn, iface=LOCAL_IF, timeout=5) + +print "Send ACK packet to finish handshake." +ack=TCP(sport=synack.dport, dport=synack.sport, seq=2, flags='A', + ack=synack.seq+1) +send(ip/ack, iface=LOCAL_IF) + +print "Connection is established, send bogus SYN, expect challenge ACK" +bogus_syn=TCP(sport=port, dport='echo', seq=1000000, flags='S', + window=(2**16)-1) +sniffer = Sniff(); +sniffer.start() +challenge_ack=send(ip/bogus_syn, iface=LOCAL_IF) +sniffer.join(timeout=5) + +if sniffer.captured == None: + print "ERROR: no packet received" + exit(1) + +challenge_ack = None + +for p in sniffer.captured: + if p.haslayer(TCP) and p.getlayer(TCP).sport == 7 and \ + p.getlayer(TCP).flags == 16: + challenge_ack = p + break + +if challenge_ack == None: + print "No ACK has been seen" + exit(1) + +if challenge_ack.getlayer(TCP).seq != (synack.seq + 1): + print "ERROR: expecting seq %d got %d in challange ack" % \ + (challenge_ack.getlayer(TCP).seq, (synack.seq + 1)) + exit(1) + +exit(0) diff --git a/regress/sys/net/pf_state/pf.conf b/regress/sys/net/pf_state/pf.conf new file mode 100644 index 00000000000..ba910a003ac --- /dev/null +++ b/regress/sys/net/pf_state/pf.conf @@ -0,0 +1,3 @@ +# pf on PF must have these rules in the regress anchor + +pass to { $REMOTE_ADDR/24 } |