Index: head/tests/sys/netpfil/pf/forward.sh =================================================================== --- head/tests/sys/netpfil/pf/forward.sh (revision 324662) +++ head/tests/sys/netpfil/pf/forward.sh (revision 324663) @@ -1,67 +1,147 @@ # $FreeBSD$ . $(atf_get_srcdir)/utils.subr atf_test_case "v4" "cleanup" v4_head() { atf_set descr 'Basic forwarding test' atf_set require.user root # We need scapy to be installed for out test scripts to work atf_set require.progs scapy } v4_body() { pft_init epair_send=$(pft_mkepair) ifconfig ${epair_send}a 192.0.2.1/24 up epair_recv=$(pft_mkepair) ifconfig ${epair_recv}a up pft_mkjail alcatraz ${epair_send}b ${epair_recv}b jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up jexec alcatraz ifconfig ${epair_recv}b 198.51.100.2/24 up jexec alcatraz sysctl net.inet.ip.forwarding=1 jexec alcatraz arp -s 198.51.100.3 00:01:02:03:04:05 route add -net 198.51.100.0/24 192.0.2.2 # Sanity check, can we forward ICMP echo requests without pf? atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recvif ${epair_recv}a # Forward with pf enabled printf "block in\n" | jexec alcatraz pfctl -ef - atf_check -s exit:1 $(atf_get_srcdir)/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recvif ${epair_recv}a printf "block out\n" | jexec alcatraz pfctl -f - atf_check -s exit:1 $(atf_get_srcdir)/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recv ${epair_recv}a # Allow ICMP printf "block in\npass in proto icmp\n" | jexec alcatraz pfctl -f - atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recvif ${epair_recv}a } v4_cleanup() { pft_cleanup } +atf_test_case "v6" "cleanup" +v6_head() +{ + atf_set descr 'Basic IPv6 forwarding test' + atf_set require.user root + atf_set require.progs scapy +} + +v6_body() +{ + pft_init + + epair_send=$(pft_mkepair) + epair_recv=$(pft_mkepair) + + ifconfig ${epair_send}a inet6 2001:db8:42::1/64 up no_dad -ifdisabled + ifconfig ${epair_recv}a up + + pft_mkjail alcatraz ${epair_send}b ${epair_recv}b + + jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 up no_dad + jexec alcatraz ifconfig ${epair_recv}b inet6 2001:db8:43::2/64 up no_dad + jexec alcatraz sysctl net.inet6.ip6.forwarding=1 + jexec alcatraz ndp -s 2001:db8:43::3 00:01:02:03:04:05 + route add -6 2001:db8:43::/64 2001:db8:42::2 + + # Sanity check, can we forward ICMP echo requests without pf? + atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \ + --ip6 \ + --sendif ${epair_send}a \ + --to 2001:db8:43::3 \ + --recvif ${epair_recv}a + + jexec alcatraz pfctl -e + + # Block incoming echo request packets + pft_set_rules alcatraz \ + "block in inet6 proto icmp6 icmp6-type echoreq" + atf_check -s exit:1 $(atf_get_srcdir)/pft_ping.py \ + --ip6 \ + --sendif ${epair_send}a \ + --to 2001:db8:43::3 \ + --recvif ${epair_recv}a + + # Block outgoing echo request packets + pft_set_rules alcatraz \ + "block out inet6 proto icmp6 icmp6-type echoreq" + atf_check -s exit:1 -e ignore $(atf_get_srcdir)/pft_ping.py \ + --ip6 \ + --sendif ${epair_send}a \ + --to 2001:db8:43::3 \ + --recvif ${epair_recv}a + + # Allow ICMPv6 but nothing else + pft_set_rules alcatraz \ + "block out" \ + "pass out inet6 proto icmp6" + atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \ + --ip6 \ + --sendif ${epair_send}a \ + --to 2001:db8:43::3 \ + --recvif ${epair_recv}a + + # Allowing ICMPv4 does not allow ICMPv6 + pft_set_rules alcatraz \ + "block out inet6 proto icmp6 icmp6-type echoreq" \ + "pass in proto icmp" + atf_check -s exit:1 $(atf_get_srcdir)/pft_ping.py \ + --ip6 \ + --sendif ${epair_send}a \ + --to 2001:db8:43::3 \ + --recvif ${epair_recv}a +} + +v6_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "v4" + atf_add_test_case "v6" } Index: head/tests/sys/netpfil/pf/pft_ping.py =================================================================== --- head/tests/sys/netpfil/pf/pft_ping.py (revision 324662) +++ head/tests/sys/netpfil/pf/pft_ping.py (revision 324663) @@ -1,105 +1,145 @@ #!/usr/local/bin/python2.7 import argparse import scapy.all as sp import sys import threading PAYLOAD_MAGIC = 0x42c0ffee class Sniffer(threading.Thread): def __init__(self, recvif): threading.Thread.__init__(self) self._recvif = recvif self.start() def run(self): self.packets = sp.sniff(iface=self._recvif, timeout=3) def check_ping_request(packet, dst_ip, args): + if args.ip6: + return check_ping6_request(packet, dst_ip, args) + else: + return check_ping4_request(packet, dst_ip, args) + +def check_ping4_request(packet, dst_ip, args): """ Verify that the packet matches what we'd have sent """ ip = packet.getlayer(sp.IP) if not ip: return False if ip.dst != dst_ip: return False icmp = packet.getlayer(sp.ICMP) if not icmp: return False if sp.icmptypes[icmp.type] != 'echo-request': return False raw = packet.getlayer(sp.Raw) if not raw: return False if raw.load != str(PAYLOAD_MAGIC): return False # Wait to check expectations until we've established this is the packet we # sent. if args.expect_tos: if ip.tos != int(args.expect_tos[0]): print "Unexpected ToS value %d, expected %s" \ % (ip.tos, args.expect_tos[0]) return False return True +def check_ping6_request(packet, dst_ip, args): + """ + Verify that the packet matches what we'd have sent + """ + ip = packet.getlayer(sp.IPv6) + if not ip: + return False + if ip.dst != dst_ip: + return False + + icmp = packet.getlayer(sp.ICMPv6EchoRequest) + if not icmp: + return False + if icmp.data != str(PAYLOAD_MAGIC): + return False + + return True + def ping(send_if, dst_ip, args): ether = sp.Ether() ip = sp.IP(dst=dst_ip) icmp = sp.ICMP(type='echo-request') raw = sp.Raw(PAYLOAD_MAGIC) if args.send_tos: ip.tos = int(args.send_tos[0]) req = ether / ip / icmp / raw sp.sendp(req, iface=send_if, verbose=False) +def ping6(send_if, dst_ip, args): + ether = sp.Ether() + ip6 = sp.IPv6(dst=dst_ip) + icmp = sp.ICMPv6EchoRequest(data=PAYLOAD_MAGIC) + + req = ether / ip6 / icmp + sp.sendp(req, iface=send_if, verbose=False) + def main(): parser = argparse.ArgumentParser("pft_ping.py", description="Ping test tool") parser.add_argument('--sendif', nargs=1, required=True, help='The interface through which the packet(s) will be sent') parser.add_argument('--recvif', nargs=1, help='The interface on which to expect the ICMP echo response') + parser.add_argument('--ip6', action='store_true', + help='Use IPv6') parser.add_argument('--to', nargs=1, required=True, help='The destination IP address for the ICMP echo request') # Packet settings parser.add_argument('--send-tos', nargs=1, help='Set the ToS value for the transmitted packet') # Expectations parser.add_argument('--expect-tos', nargs=1, help='The expected ToS value in the received packet') args = parser.parse_args() + # We may not have a default route. Tell scapy where to start looking for routes + sp.conf.iface6 = args.sendif[0] + sniffer = None if not args.recvif is None: sniffer = Sniffer(args.recvif[0]) - ping(args.sendif[0], args.to[0], args) + if args.ip6: + ping6(args.sendif[0], args.to[0], args) + else: + ping(args.sendif[0], args.to[0], args) if sniffer: sniffer.join() for packet in sniffer.packets: if check_ping_request(packet, args.to[0], args): sys.exit(0) # We did not get the packet we expected sys.exit(1) if __name__ == '__main__': main() Index: head/tests/sys/netpfil/pf/utils.subr =================================================================== --- head/tests/sys/netpfil/pf/utils.subr (revision 324662) +++ head/tests/sys/netpfil/pf/utils.subr (revision 324663) @@ -1,53 +1,67 @@ # $FreeBSD$ # Utility functions ## pft_init() { if [ ! -c /dev/pf ]; then atf_skip "This test requires pf" fi if [ "`sysctl -i -n kern.features.vimage`" != 1 ]; then atf_skip "This test requires VIMAGE" fi } pft_mkepair() { ifname=$(ifconfig epair create) echo $ifname >> created_interfaces.lst echo ${ifname%a} } pft_mkjail() { jailname=$1 shift vnet_interfaces= for ifname in $@ do vnet_interfaces="${vnet_interfaces} vnet.interface=${ifname}" done jail -c name=${jailname} persist vnet ${vnet_interfaces} echo $jailname >> created_jails.lst } +pft_set_rules() +{ + jname=$1 + shift + + # Flush all states, rules, fragments, ... + jexec ${jname} pfctl -F all + + while [ $# -gt 0 ]; do + printf "$1\n" + shift + done | jexec ${jname} pfctl -f - +} + pft_cleanup() { if [ -f created_interfaces.lst ]; then for ifname in `cat created_interfaces.lst` do ifconfig ${ifname} destroy done fi if [ -f created_jails.lst ]; then for jailname in `cat created_jails.lst` do jail -r ${jailname} done fi }