Page MenuHomeFreeBSD

D11401.id30498.diff
No OneTemporary

D11401.id30498.diff

Index: tests/sys/netpfil/pf/Makefile
===================================================================
--- /dev/null
+++ tests/sys/netpfil/pf/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+TESTSDIR= ${TESTSBASE}/sys/netpfil/pf
+BINDIR= ${TESTSDIR}
+
+ATF_TESTS_SH= pf_test
+
+SUBDIR+= files
+
+.include <bsd.test.mk>
Index: tests/sys/netpfil/pf/files/Makefile
===================================================================
--- /dev/null
+++ tests/sys/netpfil/pf/files/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+TESTSDIR= ${TESTSBASE}/sys/netpfil/pf/files
+BINDIR= ${TESTSDIR}
+
+FILES= pf_test_conf.sh scrub.py conf.py
+
+.include <bsd.progs.mk>
Index: tests/sys/netpfil/pf/files/conf.py
===================================================================
--- /dev/null
+++ tests/sys/netpfil/pf/files/conf.py
@@ -0,0 +1,10 @@
+# python2
+
+# Read conf variables from pf_test_conf.sh.
+
+conffile = open('pf_test_conf.sh')
+
+for line in conffile:
+ # Simple test that line is of the form var=val.
+ if len(line.split('=', 1)) == 2:
+ exec(line)
Index: tests/sys/netpfil/pf/files/pf_test_conf.sh
===================================================================
--- /dev/null
+++ tests/sys/netpfil/pf/files/pf_test_conf.sh
@@ -0,0 +1,23 @@
+# You need to set these variables for the tests to work.
+# Keep the contents of the file in the form 'var=val' as below,
+# as this file will be read from python as well.
+
+SSH='root@192.168.0.2'
+
+LOCAL_IF_1='tap1'
+REMOTE_IF_1='vtnet1'
+LOCAL_MAC_1='00:bd:6e:8c:ff:01'
+REMOTE_MAC_1='00:a0:98:eb:76:05'
+LOCAL_ADDR_1='192.168.1.1'
+REMOTE_ADDR_1='192.168.1.2'
+
+LOCAL_IF_2='tap2'
+REMOTE_IF_2='vtnet2'
+LOCAL_MAC_2='00:bd:54:7d:98:02'
+REMOTE_MAC_2='00:a0:98:8f:42:f7'
+LOCAL_ADDR_2='192.168.2.1'
+REMOTE_ADDR_2='192.168.2.2'
+
+LOCAL_IF_3='tap3'
+LOCAL_ADDR_2='192.168.2.1'
+LOCAL_ADDR_3='192.168.3.1'
Index: tests/sys/netpfil/pf/files/scrub.py
===================================================================
--- /dev/null
+++ tests/sys/netpfil/pf/files/scrub.py
@@ -0,0 +1,59 @@
+# /usr/bin/env python2
+
+import multiprocessing as mp
+import scapy.all as sp
+import conf
+import time
+import random
+import util
+
+raw_500 = ('abcdefghijklmnopqrstuvwxyz' * 22)[random.randrange(26):][:500]
+
+ether1 = sp.Ether(src=conf.LOCAL_MAC_1, dst=conf.REMOTE_MAC_1)
+ether2 = sp.Ether(src=conf.LOCAL_MAC_2, dst=conf.REMOTE_MAC_2)
+ip1 = sp.IP(src=conf.LOCAL_ADDR_1,
+ dst=conf.LOCAL_ADDR_3, id=random.randrange(1 << 16))
+ip2 = sp.IP(src=conf.LOCAL_ADDR_2,
+ dst=conf.LOCAL_ADDR_3, id=random.randrange(1 << 16))
+icmp = sp.ICMP(type='echo-request',
+ id=random.randrange(1 << 16), seq=random.randrange(1 << 16))
+
+p1 = ether1 / ip1 / icmp / raw_500
+p2 = ether2 / ip2 / icmp / raw_500
+
+def sendpackets():
+ time.sleep(1)
+ sp.sendp(sp.fragment(p1, 300), iface=conf.LOCAL_IF_1, verbose=False)
+ sp.sendp(sp.fragment(p2, 300), iface=conf.LOCAL_IF_2, verbose=False)
+
+sender = mp.Process(target=sendpackets)
+sender.start()
+
+sniffed = []
+sp.sniff(iface=conf.LOCAL_IF_3, prn=sniffed.append, timeout=5)
+
+sender.join()
+
+success1, success2 = False, False
+
+defr = util.Defragmenter()
+pp1, pp2 = p1.payload, p2.payload # IP layer
+k1, k2 = util.pkey(pp1), util.pkey(pp2)
+for p in sniffed:
+ pp = defr.more(p)
+ if pp is None:
+ continue
+ k = util.pkey(pp)
+
+ # Success for interface 1 if packet received in 1 fragment,
+ # i.e. scrub active on remote side.
+ success1 = success1 or (k == k1 and defr.stats[k] == 1 and
+ str(pp.payload) == str(pp1.payload))
+
+ # Success for interface 2 if packet received in 2 fragments,
+ # i.e. no scrub on remote side.
+ success2 = success2 or (k == k2 and defr.stats[k] == 2 and
+ str(pp.payload) == str(pp2.payload))
+
+if not (success1 and success2):
+ exit(1)
Index: tests/sys/netpfil/pf/files/util.py
===================================================================
--- /dev/null
+++ tests/sys/netpfil/pf/files/util.py
@@ -0,0 +1,63 @@
+# python2
+
+import scapy.all as sp
+
+def pkey(packet):
+ '''Packet key.'''
+ return (packet.src, packet.dst, packet.proto, packet.id)
+
+class Defragmenter(object):
+ def __init__(self):
+ self.frags = dict()
+ self.stats = dict()
+ def more(self, packet):
+ '''Add fragmented packet, return whole packet if complete.'''
+
+ # Find IP layer.
+ p = packet
+ while p.name != 'NoPayload':
+ if p.name == 'IP':
+ break
+ p = p.payload
+ else:
+ return
+
+ # # Return directly if not fragmented.
+ # if not ((p.flags & 1) or p.frag): # & 1 for MF
+ # return p
+
+ # Add fragment to its packet group.
+ key, val = pkey(p), (p.frag, p)
+ if key in self.frags:
+ self.frags[key].append(val)
+ self.stats[key] += 1
+ else:
+ self.frags[key] = [val]
+ self.stats[key] = 1
+ frag = self.frags[key]
+ frag.sort()
+
+ # Now all fragments in the group are sorted,
+ # go through them and connect them.
+ i = 0
+ while i + 1 < len(frag):
+ f1, p1 = frag[i]
+ f2, p2 = frag[i + 1]
+ len1, len2 = len(p1.payload), len(p2.payload)
+ if len1 == (f2 - f1) * 8:
+ header1 = sp.IP(tos=p1.tos, flags=p1.flags, ttl=p1.ttl,
+ src=p1.src, dst=p1.dst,
+ proto=p1.proto, id=p1.id)
+ # Now copy MF flag from p2.
+ header1.flags = (header1.flags & ~1) | (p2.flags & 1)
+ p = header1 / (str(p1.payload) + str(p2.payload))
+ frag[i:i + 2] = [(f1, p)]
+ else:
+ i += 1
+
+ # Return packet if complete.
+ p = frag[0][1]
+ isfirst, islast = (not p.frag), (not (p.flags & 1))
+ if len(frag) == 1 and isfirst and islast:
+ del self.frags[key]
+ return p
Index: tests/sys/netpfil/pf/pf_test.sh
===================================================================
--- /dev/null
+++ tests/sys/netpfil/pf/pf_test.sh
@@ -0,0 +1,125 @@
+# Make will add a shebang line at the top of this file.
+
+# These tests connect to a remote test machine, load a rules file,
+# possibly start some services, and run some tests. The tests cleanup
+# the test machine in the end.
+#
+# SSH root access to the test machine is required for the tests to
+# work.
+
+. "$(atf_get_srcdir)/files/pf_test_conf.sh"
+
+# Starts two instances of nc on the remote machine, listening on two
+# different ports, of which one port is blocked-with-return by the
+# remote pf. The test tries then to connect to the two instances from
+# the local machine. The test succeeds if one connection succeeds but
+# the other one fails.
+atf_test_case block_return cleanup
+block_return_head () {
+ atf_set descr 'Block-with-return a port and test that it is blocked.'
+}
+block_return_body () {
+ rules="block return in on $REMOTE_IF_1 proto tcp to port 50000"
+ atf_check ssh "$SSH" kldload -n pf
+ echo "$rules" | atf_check -e ignore ssh "$SSH" pfctl -ef -
+ atf_check daemon -p nc.50000.pid ssh "$SSH" nc -l 50000
+ atf_check daemon -p nc.50001.pid ssh "$SSH" nc -l 50001
+ atf_check -s exit:1 -e empty nc -z "$REMOTE_ADDR_1" 50000
+ atf_check -s exit:0 -e ignore nc -z "$REMOTE_ADDR_1" 50001
+}
+block_return_cleanup () {
+ atf_check -e ignore ssh "$SSH" pfctl -dFa
+ [ -e nc.50000.pid ] && kill `cat nc.50000.pid`
+ [ -e nc.50001.pid ] && kill `cat nc.50001.pid`
+}
+
+atf_test_case block_drop cleanup
+block_drop_head () {
+ atf_set descr 'Block-with-drop a port and test that it is blocked.'
+}
+block_drop_body () {
+ rules="block drop in on $REMOTE_IF_1 proto tcp to port 50000"
+ atf_check ssh "$SSH" kldload -n pf
+ echo "$rules" | atf_check -e ignore ssh "$SSH" pfctl -ef -
+ atf_check daemon -p nc.50000.pid ssh "$SSH" nc -l 50000
+ atf_check daemon -p nc.50001.pid ssh "$SSH" nc -l 50001
+ atf_check -s exit:1 -e empty nc -z -w 4 "$REMOTE_ADDR_1" 50000
+ atf_check -s exit:0 -e ignore nc -z "$REMOTE_ADDR_1" 50001
+}
+block_drop_cleanup () {
+ atf_check -e ignore ssh "$SSH" pfctl -dFa
+ [ -e nc.50000.pid ] && kill `cat nc.50000.pid`
+ [ -e nc.50001.pid ] && kill `cat nc.50001.pid`
+}
+
+# # This test uses 2 interfaces to connect to the test machine,
+# # $REMOTE_IF_1 and $REMOTE_IF_2. The test machine is doing reassembly
+# # on one of the two interfaces. We send one echo request on each
+# # interface of size 3000, which will be fragmented before being sent.
+# # We capture the traffic on the test machine's pflog and transfer the
+# # capture file to the host machine for processing. The capture file
+# # should show a reassembled echo request packet on one interface and
+# # the original fragmented set of packets on the other.
+# atf_test_case scrub_todo cleanup
+# scrub_todo_head () {
+# atf_set descr 'Scrub on one of two interfaces and test difference.'
+# }
+# scrub_todo_body () {
+# # files to be used in local directory: tempdir.var tcpdump.pid
+# # files to be used in remote temporary directory: pflog.pcap
+# rules="scrub in on $REMOTE_IF_1 all fragment reassemble
+# pass log (all, to pflog0) on { $REMOTE_IF_1 $REMOTE_IF_2 }"
+# atf_check ssh "$SSH" kldload -n pf pflog
+# echo "$rules" | atf_check -e ignore ssh "$SSH" pfctl -ef -
+# # TODO not sure why this doesn't work with atf_check
+# #atf_check -o file:tempdir.var ssh "$SSH" mktemp -dt pf_test.tmp
+# ssh "$SSH" mktemp -dt pf_test.tmp > tempdir.var
+# tempdir="`cat tempdir.var`"
+# atf_check daemon -p tcpdump.pid \
+# ssh "$SSH" tcpdump -U -i pflog0 -w "$tempdir/pflog.pcap"
+# atf_check -o ignore ping -c1 -s3000 "$REMOTE_ADDR_1"
+# atf_check -o ignore ping -c1 -s3000 "$REMOTE_ADDR_2"
+# sleep 2 # wait for tcpdump to pick up everything
+# kill "`cat tcpdump.pid`"
+# sleep 2 # wait for tcpdump to write out everything
+# atf_check scp "$SSH:$tempdir/pflog.pcap" ./
+# # TODO following will be removed when the test is complete, but
+# # since processing isn't implemented yet, we just save the file
+# # for now.
+# atf_check cp pflog.pcap "$(atf_get_srcdir)/"
+# # TODO process pflog.pcap for verification
+# }
+# scrub_todo_cleanup () {
+# kill "`cat tcpdump.pid`"
+# tempdir="`cat tempdir.var`"
+# ssh "$SSH" "rm -r \"$tempdir\" ;
+# pfctl -dFa"
+# }
+
+atf_test_case scrub_forward cleanup
+scrub_forward_head () {
+ atf_set descr 'Scrub defrag with forward on one \
+of two interfaces and test difference.'
+}
+scrub_forward_body () {
+ rules="scrub in on $REMOTE_IF_1 all fragment reassemble
+ pass log (all, to pflog0) on { $REMOTE_IF_1 $REMOTE_IF_2 }"
+ cd "$(atf_get_srcdir)"
+ atf_check ssh "$SSH" kldload -n pf
+ echo "$rules" | atf_check -e ignore ssh "$SSH" pfctl -ef -
+ atf_check -o ignore ssh "$SSH" sysctl net.inet.ip.forwarding=1
+ cd files &&
+ atf_check python2 scrub.py &&
+ cd ..
+}
+scrub_forward_cleanup () {
+ ssh "$SSH" "pfctl -dFa ;
+ sysctl net.inet.ip.forwarding=0"
+}
+
+atf_init_test_cases () {
+ atf_add_test_case block_return
+ atf_add_test_case block_drop
+ # atf_add_test_case scrub_todo
+ atf_add_test_case scrub_forward
+}

File Metadata

Mime Type
text/plain
Expires
Sat, Mar 28, 12:28 PM (15 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30472389
Default Alt Text
D11401.id30498.diff (11 KB)

Event Timeline