diff options
author | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2012-07-10 12:43:55 +0000 |
---|---|---|
committer | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2012-07-10 12:43:55 +0000 |
commit | c18e9b2d0efdf416e7605c4c84c154458a34b0e5 (patch) | |
tree | 01f1c43f5ab94c84fbd5ee1f4f06000c734f7c72 /regress | |
parent | 11d1a156dbff60158586eb151499cf6a46104383 (diff) |
Add a test suite to route ip packets through a box running pf. You
have to setup four machines manually as described in the makefile.
The test uses netcat and scapy to send ping or udp echo packets or
tcp streams along the routers. It analyzes the returned reply or
icmp error packets. It tests the forward and net/rdr and net64
paths.
Diffstat (limited to 'regress')
-rw-r--r-- | regress/sys/net/pf_forward/Makefile | 193 | ||||
-rw-r--r-- | regress/sys/net/pf_forward/ping6_mtu.py | 24 | ||||
-rw-r--r-- | regress/sys/net/pf_forward/ping_mtu.py | 22 |
3 files changed, 239 insertions, 0 deletions
diff --git a/regress/sys/net/pf_forward/Makefile b/regress/sys/net/pf_forward/Makefile new file mode 100644 index 00000000000..0ef375ccdca --- /dev/null +++ b/regress/sys/net/pf_forward/Makefile @@ -0,0 +1,193 @@ +# $OpenBSD: Makefile,v 1.1 2012/07/10 12:43:54 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 + +# This test needs a manual setup of four machines +# The setup is the same as for regress/sys/net/pf_fragment +# Set up machines: SRC PF RT DST +# SRC is the machine where this makefile is running. +# PF is running OpenBSD forwarding through pf, it is the test target. +# RT is a router forwarding packets, maximum MTU is 1300. +# DST is reflecting the ping and UDP and TCP echo packets. +# RDR does not exist, PF redirects the traffic to DST. +# AF does not exist, PF translates address family and sends to DST. +# +# +---+ 1 +--+ 2 +--+ 3 +---+ 4 +---+ 5 +--+ +# |SRC| ----> |PF| ----> |RT| ----> |DST| |RDR| |AF| +# +---+ +--+ +--+ +---+ +---+ +--+ +# out in out in out in in in + +# Configure Addresses on the machines, there must be routes for the networks. +# Adapt interface and addresse variables to your local setup. +# +SRC_IF = tun0 +SRC_MAC = fe:e1:ba:d1:0a:dc +PF_MAC = 52:54:00:12:34:50 + +SRC_OUT = 10.188.211.10 +PF_IN = 10.188.211.50 +PF_OUT = 10.188.212.50 +RT_IN = 10.188.212.51 +RT_OUT = 10.188.213.51 +DST_IN = 10.188.213.52 +RDR_IN = 10.188.214.1 +AF_IN = 10.188.215.82 + +SRC_OUT6 = fdd7:e83e:66bc:211:fce1:baff:fed1:561f +PF_IN6 = fdd7:e83e:66bc:211:5054:ff:fe12:3450 +PF_OUT6 = fdd7:e83e:66bc:212:5054:ff:fe12:3450 +RT_IN6 = fdd7:e83e:66bc:212:5054:ff:fe12:3451 +RT_OUT6 = fdd7:e83e:66bc:213:5054:ff:fe12:3451 +DST_IN6 = fdd7:e83e:66bc:213:5054:ff:fe12:3452 +RDR_IN6 = fdd7:e83e:66bc:214::1 +AF_IN6 = fdd7:e83e:66bc:215:5054:ff:fe12:3434 + +# pf rules on PF must look like this: +# +# pass to { $PF_IN/24 $PF_IN6/64 } +# pass to { $RT_IN/24 $RT_IN6/64 } +# pass to { $DST_IN/24 $DST_IN6/64 } +# pass to { $RDR_IN/24 $RDR_IN6/64 } +# +# pass in to $RDR_IN/24 rdr-to $DST_IN tag rdr +# pass out nat-to $PF_OUT tagged rdr +# pass in to $RDR_IN6/64 rdr-to $DST_IN6 tag rdr +# pass out nat-to $PF_OUT6 tagged rdr + +# pass in to $AF_IN/24 af-to inet6 from $PF_OUT6 to $DST_IN6/120 tag af +# pass out inet6 tagged af +# pass in to $AF_IN6/64 af-to inet from $PF_OUT to $DST_IN/24 tag af +# pass out inet tagged af + +# Currently these test fail as pf does not fix the checksum of +# NATed packets inside of icmp packets. +# ping6-mtu + +depend: addr.py + +# Create python include file containing the addresses. +addr.py: Makefile + rm -f $@ $@.tmp + echo 'SRC_IF = "${SRC_IF}"' >>$@.tmp + echo 'SRC_MAC = "${SRC_MAC}"' >>$@.tmp + echo 'PF_MAC = "${PF_MAC}"' >>$@.tmp +.for var in SRC_OUT PF_IN PF_OUT RT_IN RT_OUT DST_IN RDR_IN AF_IN + echo '${var} = "${${var}}"' >>$@.tmp + echo '${var}6 = "${${var}6}"' >>$@.tmp +.endfor + mv $@.tmp $@ + +# Make sure that the routing table on the SRC machine is correct. +# All packets must be forwarded to PF target machine. +TARGETS += route route6 + +run-regress-route: + @echo '\n======== $@ ========' + @echo Check route SRC_OUT: + route -n get -inet ${SRC_OUT} | fgrep -q 'interface: lo0' + @echo Check route PF_IN: + route -n get -inet ${PF_IN} | fgrep -q 'if address: ${SRC_OUT}' +.for ip in PF_OUT RT_IN RT_OUT DST_IN RDR_IN AF_IN + @echo Check route ${ip}: + route -n get -inet ${${ip}} | fgrep -q 'gateway: ${PF_IN}' +.endfor + +run-regress-route6: + @echo '\n======== $@ ========' + @echo Check route SRC_OUT6: + route -n get -inet6 ${SRC_OUT6} | fgrep -q 'interface: lo0' +.for ip in PF_OUT RT_IN RT_OUT DST_IN RDR_IN AF_IN + @echo Check route ${ip}6: + route -n get -inet6 ${${ip}6} | fgrep -q 'gateway: ${PF_IN6}' +.endfor + +# Ping all addresses. This ensures that the ip addresses are configured +# and all routing table are set up to allow bidirectional packet flow. +# Note that RDR does not exist physically. So this traffic is rewritten +# by PF and handled by DST. +TARGETS += ping ping6 + +run-regress-ping: + @echo '\n======== $@ ========' +.for ip in SRC_OUT PF_IN PF_OUT RT_IN RT_OUT DST_IN RDR_IN AF_IN + @echo Check ping ${ip}: + ping -n -c 1 ${${ip}} +.endfor + +run-regress-ping6: + @echo '\n======== $@ ========' +.for ip in SRC_OUT PF_IN PF_OUT RT_IN RT_OUT DST_IN RDR_IN AF_IN + @echo Check ping ${ip}6: + ping6 -n -c 1 ${${ip}6} +.endfor + +# Send a large IPv4/ICMP-Echo-Request packet with enabled DF bit and +# parse response packet to determine MTU of the router. The MTU has +# to be 1300 octets. The MTU has to be defined at out interface of +# the router RT before. +TARGETS += ping-mtu ping6-mtu + +run-regress-ping-mtu: addr.py + @echo '\n======== $@ ========' +.for ip in DST_IN RDR_IN + @echo Check path MTU to ${ip} is 1300 + ${SUDO} python2.7 ping_mtu.py ${${ip}} 1300 +.endfor + @echo Check path MTU to AF_IN is 1280 + ${SUDO} python2.7 ping_mtu.py ${AF_IN} 1280 + +run-regress-ping6-mtu: addr.py + @echo '\n======== $@ ========' +.for ip in DST_IN RDR_IN + @echo Check path MTU to ${ip}6 is 1300 + ${SUDO} python2.7 ping6_mtu.py ${${ip}6} 1300 +.endfor + @echo Check path MTU to AF_IN6 is 1320 + ${SUDO} python2.7 ping6_mtu.py ${AF_IN6} 1320 + +# Send one udp echo port 7 packet to all destination addresses with netcat. +# The response must arrive in 1 second. +TARGETS += udp udp6 + +run-regress-udp: + @echo '\n======== $@ ========' +.for ip in DST_IN RDR_IN AF_IN + @echo Check udp ${ip}: + ( echo $$$$ | nc -u ${${ip}} 7 & sleep 1; kill $$! ) | grep $$$$ +.endfor + +run-regress-udp6: + @echo '\n======== $@ ========' +.for ip in DST_IN RDR_IN AF_IN + @echo Check udp ${ip}6: + ( echo $$$$ | nc -u ${${ip}6} 7 & sleep 1; kill $$! ) | grep $$$$ +.endfor + +# Send a data stream to tcp echo port 7 to all destination addresses +# with netcat. Use enough data to make sure PMTU discovery works. +# Count the reflected bytes and compare with the transmitted ones. +TARGETS += tcp tcp6 + +run-regress-tcp: + @echo '\n======== $@ ========' +.for ip in DST_IN RDR_IN AF_IN + @echo Check tcp ${ip}: + openssl rand 200000 | nc ${${ip}} 7 | wc -c | grep '200000$$' +.endfor + +run-regress-tcp6: + @echo '\n======== $@ ========' +.for ip in DST_IN RDR_IN AF_IN + @echo Check tcp ${ip}6: + openssl rand 200000 | nc ${${ip}6} 7 | wc -c | grep '200000$$' +.endfor + +REGRESS_TARGETS = ${TARGETS:S/^/run-regress-/} + +CLEANFILES += addr.py *.pyc *.log + +.include <bsd.regress.mk> diff --git a/regress/sys/net/pf_forward/ping6_mtu.py b/regress/sys/net/pf_forward/ping6_mtu.py new file mode 100644 index 00000000000..251a84c24a6 --- /dev/null +++ b/regress/sys/net/pf_forward/ping6_mtu.py @@ -0,0 +1,24 @@ +#!/usr/local/bin/python2.7 +# check wether path mtu to dst is as expected + +import os +from addr import * +from scapy.all import * + +dstaddr=sys.argv[1] +expect=int(sys.argv[2]) +pid=os.getpid() +payload="a" * 1452 +a=srp1(Ether(src=SRC_MAC, dst=PF_MAC)/IPv6(src=SRC_OUT6, dst=dstaddr)/ + ICMPv6EchoRequest(id=pid, data=payload), iface=SRC_IF, timeout=2) +if a and a.type == scapy.layers.dot11.ETHER_TYPES.IPv6 and \ + ipv6nh[a.payload.nh] == 'ICMPv6' and \ + icmp6types[a.payload.payload.type] == 'Packet too big': + mtu=a.payload.payload.mtu + print "mtu=%d" % (mtu) + if mtu == expect: + exit(0) + print "MTU!=%d" % (expect) + exit(1) +print "MTU=UNKNOWN" +exit(2) diff --git a/regress/sys/net/pf_forward/ping_mtu.py b/regress/sys/net/pf_forward/ping_mtu.py new file mode 100644 index 00000000000..76985ae867a --- /dev/null +++ b/regress/sys/net/pf_forward/ping_mtu.py @@ -0,0 +1,22 @@ +#!/usr/local/bin/python2.7 +# check wether path mtu to dst is as expected + +import os +from addr import * +from scapy.all import * + +dstaddr=sys.argv[1] +expect=int(sys.argv[2]) +pid=os.getpid() +payload="a" * 1452 +a=srp1(Ether(src=SRC_MAC, dst=PF_MAC)/IP(flags="DF", src=SRC_OUT, dst=dstaddr)/ + ICMP(id=pid)/payload, iface=SRC_IF, timeout=2) +if a and a.payload.payload.type==3 and a.payload.payload.code==4: + mtu=a.payload.payload.unused + print "mtu=%d" % (mtu) + if mtu == expect: + exit(0) + print "MTU!=%d" % (expect) + exit(1) +print "MTU=UNKNOWN" +exit(2) |