summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbluhm <bluhm@openbsd.org>2016-10-19 16:39:50 +0000
committerbluhm <bluhm@openbsd.org>2016-10-19 16:39:50 +0000
commit67cf0727096c9d385dc23ba2d512e862a4e3941d (patch)
tree08a53c11258a030553958219c1fd42523555a947
parentThere used to be disabled code that used /dev/tty to determine the (diff)
downloadwireguard-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/LICENSE14
-rw-r--r--regress/sys/net/pf_state/Makefile123
-rw-r--r--regress/sys/net/pf_state/README63
-rw-r--r--regress/sys/net/pf_state/challenge_ack.py68
-rw-r--r--regress/sys/net/pf_state/pf.conf3
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 }