xref: /freebsd/tests/sys/netpfil/pf/sctp.py (revision e4f2733df8c9d2fd0c5e8fdc8bec002bf39811f3)
11a28d5feSKristof Provostimport pytest
21a28d5feSKristof Provostimport ctypes
31a28d5feSKristof Provostimport socket
41a28d5feSKristof Provostimport ipaddress
51a28d5feSKristof Provostimport re
61a28d5feSKristof Provostfrom atf_python.sys.net.tools import ToolsHelper
71a28d5feSKristof Provostfrom atf_python.sys.net.vnet import VnetTestTemplate
81a28d5feSKristof Provost
91a28d5feSKristof Provostimport time
101a28d5feSKristof Provost
111a28d5feSKristof ProvostSCTP_UNORDERED = 0x0400
121a28d5feSKristof Provost
131a28d5feSKristof ProvostSCTP_NODELAY                 = 0x00000004
141a28d5feSKristof ProvostSCTP_SET_PEER_PRIMARY_ADDR   = 0x00000006
151a28d5feSKristof ProvostSCTP_PRIMARY_ADDR            = 0x00000007
161a28d5feSKristof Provost
171a28d5feSKristof ProvostSCTP_BINDX_ADD_ADDR          = 0x00008001
181a28d5feSKristof ProvostSCTP_BINDX_REM_ADDR          = 0x00008002
191a28d5feSKristof Provost
201a28d5feSKristof Provostclass sockaddr_in(ctypes.Structure):
211a28d5feSKristof Provost    _fields_ = [
221a28d5feSKristof Provost        ('sin_len', ctypes.c_uint8),
231a28d5feSKristof Provost        ('sin_family', ctypes.c_uint8),
241a28d5feSKristof Provost        ('sin_port', ctypes.c_uint16),
251a28d5feSKristof Provost        ('sin_addr', ctypes.c_uint32),
261a28d5feSKristof Provost        ('sin_zero', ctypes.c_int8 * 8)
271a28d5feSKristof Provost    ]
281a28d5feSKristof Provost
291a28d5feSKristof Provostclass sockaddr_in6(ctypes.Structure):
301a28d5feSKristof Provost    _fields_ = [
311a28d5feSKristof Provost        ('sin6_len',      ctypes.c_uint8),
321a28d5feSKristof Provost        ('sin6_family',   ctypes.c_uint8),
331a28d5feSKristof Provost        ('sin6_port',     ctypes.c_uint16),
341a28d5feSKristof Provost        ('sin6_flowinfo', ctypes.c_uint32),
351a28d5feSKristof Provost        ('sin6_addr',     ctypes.c_uint8 * 16),
361a28d5feSKristof Provost        ('sin6_scope_id', ctypes.c_uint32)
371a28d5feSKristof Provost    ]
381a28d5feSKristof Provost
391a28d5feSKristof Provostclass sockaddr_storage(ctypes.Union):
401a28d5feSKristof Provost    _fields_ = [
411a28d5feSKristof Provost        ("v4",    sockaddr_in),
421a28d5feSKristof Provost        ("v6",   sockaddr_in6)
431a28d5feSKristof Provost    ]
441a28d5feSKristof Provost
451a28d5feSKristof Provostclass sctp_sndrcvinfo(ctypes.Structure):
461a28d5feSKristof Provost    _fields_ = [
471a28d5feSKristof Provost        ('sinfo_stream',        ctypes.c_uint16),
481a28d5feSKristof Provost        ('sinfo_ssn',           ctypes.c_uint16),
491a28d5feSKristof Provost        ('sinfo_flags',         ctypes.c_uint16),
501a28d5feSKristof Provost        ('sinfo_ppid',          ctypes.c_uint32),
511a28d5feSKristof Provost        ('sinfo_context',       ctypes.c_uint32),
521a28d5feSKristof Provost        ('sinfo_timetolive',    ctypes.c_uint32),
531a28d5feSKristof Provost        ('sinfo_tsn',           ctypes.c_uint32),
541a28d5feSKristof Provost        ('sinfo_cumtsn',        ctypes.c_uint32),
551a28d5feSKristof Provost        ('sinfo_assoc_id',      ctypes.c_uint32),
561a28d5feSKristof Provost    ]
571a28d5feSKristof Provost
581a28d5feSKristof Provostclass sctp_setprim(ctypes.Structure):
591a28d5feSKristof Provost    _fields_ = [
601a28d5feSKristof Provost        ('ssp_addr',        sockaddr_storage),
611a28d5feSKristof Provost        ('ssp_pad',         ctypes.c_int8 * (128 - 16)),
621a28d5feSKristof Provost        ('ssp_assoc_id',    ctypes.c_uint32),
631a28d5feSKristof Provost        ('ssp_padding',     ctypes.c_uint32)
641a28d5feSKristof Provost    ]
651a28d5feSKristof Provost
661a28d5feSKristof Provostdef to_sockaddr(ip, port):
671a28d5feSKristof Provost    ip = ipaddress.ip_address(ip)
681a28d5feSKristof Provost
691a28d5feSKristof Provost    if ip.version == 4:
701a28d5feSKristof Provost        addr = sockaddr_in()
711a28d5feSKristof Provost        addr.sin_len = ctypes.sizeof(addr)
721a28d5feSKristof Provost        addr.sin_family = socket.AF_INET
731a28d5feSKristof Provost        addr.sin_port = socket.htons(port)
741a28d5feSKristof Provost        addr.sin_addr = socket.htonl(int.from_bytes(ip.packed, byteorder='big'))
751a28d5feSKristof Provost    else:
761a28d5feSKristof Provost        assert ip.version == 6
771a28d5feSKristof Provost
781a28d5feSKristof Provost        addr = sockaddr_in6()
791a28d5feSKristof Provost        addr.sin6_len = ctypes.sizeof(addr)
801a28d5feSKristof Provost        addr.sin6_family = socket.AF_INET6
811a28d5feSKristof Provost        addr.sin6_port = socket.htons(port)
821a28d5feSKristof Provost        for i in range(0, 16):
831a28d5feSKristof Provost            addr.sin6_addr[i] = ip.packed[i]
841a28d5feSKristof Provost
851a28d5feSKristof Provost    return addr
861a28d5feSKristof Provost
871a28d5feSKristof Provostclass SCTPServer:
881a28d5feSKristof Provost    def __init__(self, family, port=1234):
891a28d5feSKristof Provost        self._libc = ctypes.CDLL("libc.so.7", use_errno=True)
901a28d5feSKristof Provost
911a28d5feSKristof Provost        self._listen_fd = self._libc.socket(family, socket.SOCK_STREAM, socket.IPPROTO_SCTP)
921a28d5feSKristof Provost        if self._listen_fd == -1:
931a28d5feSKristof Provost            raise Exception("Failed to create socket")
941a28d5feSKristof Provost
951a28d5feSKristof Provost        if family == socket.AF_INET:
961a28d5feSKristof Provost            srvaddr = sockaddr_in()
971a28d5feSKristof Provost            srvaddr.sin_len = ctypes.sizeof(srvaddr)
981a28d5feSKristof Provost            srvaddr.sin_family = socket.AF_INET
991a28d5feSKristof Provost            srvaddr.sin_port = socket.htons(port)
1001a28d5feSKristof Provost            srvaddr.sin_addr = socket.INADDR_ANY
1011a28d5feSKristof Provost        else:
1021a28d5feSKristof Provost            srvaddr = sockaddr_in6()
1031a28d5feSKristof Provost            srvaddr.sin6_len = ctypes.sizeof(srvaddr)
1041a28d5feSKristof Provost            srvaddr.sin6_family = family
1051a28d5feSKristof Provost            srvaddr.sin6_port = socket.htons(port)
1061a28d5feSKristof Provost            # Leave sin_addr empty, because ANY is zero
1071a28d5feSKristof Provost
1081a28d5feSKristof Provost        ret = self._libc.bind(self._listen_fd, ctypes.pointer(srvaddr),
1091a28d5feSKristof Provost            ctypes.sizeof(srvaddr))
1101a28d5feSKristof Provost        if ret == -1:
1111a28d5feSKristof Provost            raise Exception("Failed to bind: %d" % ctypes.get_errno())
1121a28d5feSKristof Provost
1131a28d5feSKristof Provost        ret = self._libc.listen(self._listen_fd, 2)
1141a28d5feSKristof Provost        if ret == -1:
1151a28d5feSKristof Provost            raise Exception("Failed to listen")
1161a28d5feSKristof Provost
1171a28d5feSKristof Provost    def _to_string(self, buf):
1181a28d5feSKristof Provost        return ''.join([chr(int.from_bytes(i, byteorder='big')) for i in buf]).rstrip('\x00')
1191a28d5feSKristof Provost
1201a28d5feSKristof Provost    def accept(self, vnet):
1211a28d5feSKristof Provost        fd = self._libc.accept(self._listen_fd, 0, 0)
1221a28d5feSKristof Provost        if fd < 0:
1231a28d5feSKristof Provost            raise Exception("Failed to accept")
1241a28d5feSKristof Provost
1251a28d5feSKristof Provost        print("SCTPServer: connection opened")
1261a28d5feSKristof Provost        while True:
1271a28d5feSKristof Provost            rcvinfo = sctp_sndrcvinfo()
1281a28d5feSKristof Provost            flags = ctypes.c_int()
1291a28d5feSKristof Provost            buf = ctypes.create_string_buffer(128)
1301a28d5feSKristof Provost
1311a28d5feSKristof Provost            # Receive a single message, and inform the other vnet about it.
1321a28d5feSKristof Provost            ret = self._libc.sctp_recvmsg(fd, ctypes.cast(buf, ctypes.c_void_p), 128,
1331a28d5feSKristof Provost                0, 0, ctypes.pointer(rcvinfo), ctypes.pointer(flags))
1341a28d5feSKristof Provost            if ret < 0:
1351a28d5feSKristof Provost                print("SCTPServer: connection closed")
1361a28d5feSKristof Provost                return
1371a28d5feSKristof Provost            if ret == 0:
1381a28d5feSKristof Provost                continue
1391a28d5feSKristof Provost
1401a28d5feSKristof Provost            rcvd = {}
1411a28d5feSKristof Provost            rcvd['ppid'] = socket.ntohl(rcvinfo.sinfo_ppid)
1421a28d5feSKristof Provost            rcvd['data'] = self._to_string(buf)
1431a28d5feSKristof Provost            rcvd['len'] = ret
1441a28d5feSKristof Provost            print(rcvd)
1451a28d5feSKristof Provost            vnet.pipe.send(rcvd)
1461a28d5feSKristof Provost
1471a28d5feSKristof Provostclass SCTPClient:
1481a28d5feSKristof Provost    def __init__(self, ip, port=1234, fromaddr=None):
1491a28d5feSKristof Provost        self._libc = ctypes.CDLL("libc.so.7", use_errno=True)
1501a28d5feSKristof Provost
1511a28d5feSKristof Provost        if ipaddress.ip_address(ip).version == 4:
1521a28d5feSKristof Provost            family = socket.AF_INET
1531a28d5feSKristof Provost        else:
1541a28d5feSKristof Provost            family = socket.AF_INET6
1551a28d5feSKristof Provost
1561a28d5feSKristof Provost        self._fd = self._libc.socket(family, socket.SOCK_STREAM,
1571a28d5feSKristof Provost            socket.IPPROTO_SCTP)
1581a28d5feSKristof Provost        if self._fd == -1:
1591a28d5feSKristof Provost            raise Exception("Failed to open socket")
1601a28d5feSKristof Provost
1611a28d5feSKristof Provost        if fromaddr is not None:
1621a28d5feSKristof Provost            addr = to_sockaddr(fromaddr, 0)
1631a28d5feSKristof Provost
1641a28d5feSKristof Provost            ret = self._libc.bind(self._fd, ctypes.pointer(addr), ctypes.sizeof(addr))
1651a28d5feSKristof Provost            if ret != 0:
1661a28d5feSKristof Provost                print("bind() => %d", ctypes.get_errno())
1671a28d5feSKristof Provost                raise
1681a28d5feSKristof Provost
1691a28d5feSKristof Provost        addr = to_sockaddr(ip, port)
1701a28d5feSKristof Provost        ret = self._libc.connect(self._fd, ctypes.pointer(addr), ctypes.sizeof(addr))
1711a28d5feSKristof Provost        if ret == -1:
1721a28d5feSKristof Provost            raise Exception("Failed to connect")
1731a28d5feSKristof Provost
1741a28d5feSKristof Provost        # Enable NODELAY, because otherwise the sending host may wait for SACK
1751a28d5feSKristof Provost        # on a data chunk we've removed
1761a28d5feSKristof Provost        enable = ctypes.c_int(1)
1771a28d5feSKristof Provost        ret = self._libc.setsockopt(self._fd, socket.IPPROTO_SCTP,
1781a28d5feSKristof Provost                SCTP_NODELAY, ctypes.pointer(enable), 4)
1791a28d5feSKristof Provost
1801a28d5feSKristof Provost    def newpeer(self, addr):
1811a28d5feSKristof Provost        print("newpeer(%s)" % (addr))
1821a28d5feSKristof Provost
1831a28d5feSKristof Provost        setp = sctp_setprim()
1841a28d5feSKristof Provost        a = to_sockaddr(addr, 0)
1851a28d5feSKristof Provost        if type(a) is sockaddr_in:
1861a28d5feSKristof Provost            setp.ssp_addr.v4 = a
1871a28d5feSKristof Provost        else:
1881a28d5feSKristof Provost            assert type(a) is sockaddr_in6
1891a28d5feSKristof Provost            setp.ssp_addr.v6 = a
1901a28d5feSKristof Provost
1911a28d5feSKristof Provost        ret = self._libc.setsockopt(self._fd, socket.IPPROTO_SCTP,
1921a28d5feSKristof Provost            SCTP_PRIMARY_ADDR, ctypes.pointer(setp), ctypes.sizeof(setp))
1931a28d5feSKristof Provost        if ret != 0:
1941a28d5feSKristof Provost            print("errno %d" % ctypes.get_errno())
1951a28d5feSKristof Provost            raise Exception(ctypes.get_errno())
1961a28d5feSKristof Provost
1971a28d5feSKristof Provost    def newprimary(self, addr):
1981a28d5feSKristof Provost        print("newprimary(%s)" % (addr))
1991a28d5feSKristof Provost
2001a28d5feSKristof Provost        # Strictly speaking needs to be struct sctp_setpeerprim, but that's
2011a28d5feSKristof Provost        # identical to sctp_setprim
2021a28d5feSKristof Provost        setp = sctp_setprim()
2031a28d5feSKristof Provost        a = to_sockaddr(addr, 0)
2041a28d5feSKristof Provost        if type(a) is sockaddr_in:
2051a28d5feSKristof Provost            setp.ssp_addr.v4 = a
2061a28d5feSKristof Provost        else:
2071a28d5feSKristof Provost            assert type(a) is sockaddr_in6
2081a28d5feSKristof Provost            setp.ssp_addr.v6 = a
2091a28d5feSKristof Provost
2101a28d5feSKristof Provost        ret = self._libc.setsockopt(self._fd, socket.IPPROTO_SCTP,
2111a28d5feSKristof Provost            SCTP_SET_PEER_PRIMARY_ADDR, ctypes.pointer(setp), ctypes.sizeof(setp))
2121a28d5feSKristof Provost        if ret != 0:
2131a28d5feSKristof Provost            print("errno %d" % ctypes.get_errno())
2141a28d5feSKristof Provost            raise
2151a28d5feSKristof Provost
2161a28d5feSKristof Provost    def bindx(self, addr, add):
2171a28d5feSKristof Provost        print("bindx(%s, %s)" % (addr, add))
2181a28d5feSKristof Provost
2191a28d5feSKristof Provost        addr = to_sockaddr(addr, 0)
2201a28d5feSKristof Provost
2211a28d5feSKristof Provost        if add:
2221a28d5feSKristof Provost            flag = SCTP_BINDX_ADD_ADDR
2231a28d5feSKristof Provost        else:
2241a28d5feSKristof Provost            flag = SCTP_BINDX_REM_ADDR
2251a28d5feSKristof Provost        ret = self._libc.sctp_bindx(self._fd, ctypes.pointer(addr), 1, flag)
2261a28d5feSKristof Provost        if ret != 0:
2271a28d5feSKristof Provost            print("sctp_bindx() errno %d" % ctypes.get_errno())
2281a28d5feSKristof Provost            raise
2291a28d5feSKristof Provost
2301a28d5feSKristof Provost    def send(self, buf, ppid, ordered=False):
2311a28d5feSKristof Provost        flags = 0
2321a28d5feSKristof Provost
2331a28d5feSKristof Provost        if not ordered:
2341a28d5feSKristof Provost            flags = SCTP_UNORDERED
2351a28d5feSKristof Provost
2361a28d5feSKristof Provost        ppid = socket.htonl(ppid)
2371a28d5feSKristof Provost        ret = self._libc.sctp_sendmsg(self._fd, ctypes.c_char_p(buf), len(buf),
2381a28d5feSKristof Provost            ctypes.c_void_p(0), 0, ppid, flags, 0, 0, 0)
2391a28d5feSKristof Provost        if ret < 0:
2401a28d5feSKristof Provost            raise Exception("Failed to send message")
2411a28d5feSKristof Provost
2421a28d5feSKristof Provost    def close(self):
2431a28d5feSKristof Provost        self._libc.close(self._fd)
2441a28d5feSKristof Provost        self._fd = -1
2451a28d5feSKristof Provost
2461a28d5feSKristof Provostclass TestSCTP(VnetTestTemplate):
2471a28d5feSKristof Provost    REQUIRED_MODULES = ["sctp", "pf"]
2481a28d5feSKristof Provost    TOPOLOGY = {
2491a28d5feSKristof Provost        "vnet1": {"ifaces": ["if1"]},
2501a28d5feSKristof Provost        "vnet2": {"ifaces": ["if1"]},
2511a28d5feSKristof Provost        "if1": {"prefixes4": [("192.0.2.1/24", "192.0.2.2/24")]},
2521a28d5feSKristof Provost    }
2531a28d5feSKristof Provost
2541a28d5feSKristof Provost    def vnet2_handler(self, vnet):
2551a28d5feSKristof Provost        # Give ourself a second IP address, for multihome testing
2561a28d5feSKristof Provost        ifname = vnet.iface_alias_map["if1"].name
2571a28d5feSKristof Provost        ToolsHelper.print_output("/sbin/ifconfig %s inet alias 192.0.2.3/24" % ifname)
2581a28d5feSKristof Provost
2591a28d5feSKristof Provost        # Start an SCTP server process, pipe the ppid + data back to the other vnet?
2601a28d5feSKristof Provost        srv = SCTPServer(socket.AF_INET, port=1234)
2611a28d5feSKristof Provost        while True:
2621a28d5feSKristof Provost            srv.accept(vnet)
2631a28d5feSKristof Provost
2641a28d5feSKristof Provost    @pytest.mark.require_user("root")
2651a28d5feSKristof Provost    def test_multihome(self):
2661a28d5feSKristof Provost        srv_vnet = self.vnet_map["vnet2"]
2671a28d5feSKristof Provost
2681a28d5feSKristof Provost        ToolsHelper.print_output("/sbin/pfctl -e")
2691a28d5feSKristof Provost        ToolsHelper.pf_rules([
2701a28d5feSKristof Provost            "block proto sctp",
271c22c9879SKristof Provost            "pass inet proto sctp to 192.0.2.0/24",
272c22c9879SKristof Provost            "pass on lo"])
2731a28d5feSKristof Provost
2741a28d5feSKristof Provost        # Sanity check, we can communicate with the primary address.
2751a28d5feSKristof Provost        client = SCTPClient("192.0.2.3", 1234)
2761a28d5feSKristof Provost        client.send(b"hello", 0)
2771a28d5feSKristof Provost        rcvd = self.wait_object(srv_vnet.pipe)
2781a28d5feSKristof Provost        print(rcvd)
2791a28d5feSKristof Provost        assert rcvd['ppid'] == 0
2801a28d5feSKristof Provost        assert rcvd['data'] == "hello"
2811a28d5feSKristof Provost
2821a28d5feSKristof Provost        try:
2831a28d5feSKristof Provost            client.newpeer("192.0.2.2")
2841a28d5feSKristof Provost            client.send(b"world", 0)
2851a28d5feSKristof Provost            rcvd = self.wait_object(srv_vnet.pipe)
2861a28d5feSKristof Provost            print(rcvd)
2871a28d5feSKristof Provost            assert rcvd['ppid'] == 0
2881a28d5feSKristof Provost            assert rcvd['data'] == "world"
2891a28d5feSKristof Provost        finally:
2901a28d5feSKristof Provost            # Debug output
2911a28d5feSKristof Provost            ToolsHelper.print_output("/sbin/pfctl -ss")
2921a28d5feSKristof Provost            ToolsHelper.print_output("/sbin/pfctl -sr -vv")
2931a28d5feSKristof Provost
2941a28d5feSKristof Provost        # Check that we have a state for 192.0.2.3 and 192.0.2.2 to 192.0.2.1
2951a28d5feSKristof Provost        states = ToolsHelper.get_output("/sbin/pfctl -ss")
2961a28d5feSKristof Provost        assert re.search(r"all sctp 192.0.2.1:.*192.0.2.3:1234", states)
2971a28d5feSKristof Provost        assert re.search(r"all sctp 192.0.2.1:.*192.0.2.2:1234", states)
2981a28d5feSKristof Provost
2991a28d5feSKristof Provost    @pytest.mark.require_user("root")
3001a28d5feSKristof Provost    def test_multihome_asconf(self):
3011a28d5feSKristof Provost        srv_vnet = self.vnet_map["vnet2"]
3021a28d5feSKristof Provost
3031a28d5feSKristof Provost        # Assign a second IP to ourselves
3041a28d5feSKristof Provost        ToolsHelper.print_output("/sbin/ifconfig %s inet alias 192.0.2.10/24"
3051a28d5feSKristof Provost            % self.vnet.iface_alias_map["if1"].name)
3061a28d5feSKristof Provost        ToolsHelper.print_output("/sbin/pfctl -e")
3071a28d5feSKristof Provost        ToolsHelper.pf_rules([
3081a28d5feSKristof Provost            "block proto sctp",
309c22c9879SKristof Provost            "pass on lo",
3101a28d5feSKristof Provost            "pass inet proto sctp from 192.0.2.0/24"])
3111a28d5feSKristof Provost
3121a28d5feSKristof Provost        # Sanity check, we can communicate with the primary address.
3131a28d5feSKristof Provost        client = SCTPClient("192.0.2.3", 1234, "192.0.2.1")
3141a28d5feSKristof Provost        client.send(b"hello", 0)
3151a28d5feSKristof Provost        rcvd = self.wait_object(srv_vnet.pipe)
3161a28d5feSKristof Provost        print(rcvd)
3171a28d5feSKristof Provost        assert rcvd['ppid'] == 0
3181a28d5feSKristof Provost        assert rcvd['data'] == "hello"
3191a28d5feSKristof Provost
3201a28d5feSKristof Provost        # Now add our second address to the connection
3211a28d5feSKristof Provost        client.bindx("192.0.2.10", True)
3221a28d5feSKristof Provost
3231a28d5feSKristof Provost        # We can still communicate
3241a28d5feSKristof Provost        client.send(b"world", 0)
3251a28d5feSKristof Provost        rcvd = self.wait_object(srv_vnet.pipe)
3261a28d5feSKristof Provost        print(rcvd)
3271a28d5feSKristof Provost        assert rcvd['ppid'] == 0
3281a28d5feSKristof Provost        assert rcvd['data'] == "world"
3291a28d5feSKristof Provost
3301a28d5feSKristof Provost        # Now change to a different peer address
3311a28d5feSKristof Provost        try:
3321a28d5feSKristof Provost            client.newprimary("192.0.2.10")
3331a28d5feSKristof Provost            client.send(b"!", 0)
3341a28d5feSKristof Provost            rcvd = self.wait_object(srv_vnet.pipe, 5)
3351a28d5feSKristof Provost            print(rcvd)
3361a28d5feSKristof Provost            assert rcvd['ppid'] == 0
3371a28d5feSKristof Provost            assert rcvd['data'] == "!"
3381a28d5feSKristof Provost        finally:
3391a28d5feSKristof Provost            # Debug output
3401a28d5feSKristof Provost            ToolsHelper.print_output("/sbin/pfctl -ss -vv")
3411a28d5feSKristof Provost
3421a28d5feSKristof Provost        # Ensure we have the states we'd expect
3431a28d5feSKristof Provost        states = ToolsHelper.get_output("/sbin/pfctl -ss")
3441a28d5feSKristof Provost        assert re.search(r"all sctp 192.0.2.1:.*192.0.2.3:1234", states)
3451a28d5feSKristof Provost        assert re.search(r"all sctp 192.0.2.10:.*192.0.2.3:1234", states)
3461a28d5feSKristof Provost
34797340b68SKristof Provost        # Now remove 192.0.2.1 as an address
34897340b68SKristof Provost        client.bindx("192.0.2.1", False)
34997340b68SKristof Provost
35097340b68SKristof Provost        # We can still communicate
35197340b68SKristof Provost        try:
35297340b68SKristof Provost            client.send(b"More data", 0)
35397340b68SKristof Provost            rcvd = self.wait_object(srv_vnet.pipe, 5)
35497340b68SKristof Provost            print(rcvd)
35597340b68SKristof Provost            assert rcvd['ppid'] == 0
35697340b68SKristof Provost            assert rcvd['data'] =="More data"
35797340b68SKristof Provost        finally:
35897340b68SKristof Provost            # Debug output
35997340b68SKristof Provost            ToolsHelper.print_output("/sbin/pfctl -ss -vv")
36097340b68SKristof Provost
36197340b68SKristof Provost        # Verify that state is closing
36297340b68SKristof Provost        states = ToolsHelper.get_output("/sbin/pfctl -ss")
36397340b68SKristof Provost        assert re.search(r"all sctp 192.0.2.1:.*192.0.2.3:1234.*SHUTDOWN", states)
36497340b68SKristof Provost
365483d5c40SKristof Provost
366483d5c40SKristof Provost    @pytest.mark.require_user("root")
367c22c9879SKristof Provost    def test_permutation_if_bound(self):
368483d5c40SKristof Provost        # Test that we generate all permutations of src/dst addresses.
369483d5c40SKristof Provost        # Assign two addresses to each end, and check for the expected states
370483d5c40SKristof Provost        srv_vnet = self.vnet_map["vnet2"]
371483d5c40SKristof Provost
372483d5c40SKristof Provost        ifname = self.vnet_map["vnet1"].iface_alias_map["if1"].name
373483d5c40SKristof Provost        ToolsHelper.print_output("/sbin/ifconfig %s inet alias 192.0.2.4/24" % ifname)
374483d5c40SKristof Provost
375483d5c40SKristof Provost        ToolsHelper.print_output("/sbin/pfctl -e")
376483d5c40SKristof Provost        ToolsHelper.pf_rules([
3770fe663b2SKristof Provost            "set state-policy if-bound",
378483d5c40SKristof Provost            "block proto sctp",
379c22c9879SKristof Provost            "pass on lo",
380483d5c40SKristof Provost            "pass inet proto sctp to 192.0.2.0/24"])
381483d5c40SKristof Provost
382483d5c40SKristof Provost        # Sanity check, we can communicate with the primary address.
383483d5c40SKristof Provost        client = SCTPClient("192.0.2.3", 1234)
384483d5c40SKristof Provost        client.send(b"hello", 0)
385483d5c40SKristof Provost        rcvd = self.wait_object(srv_vnet.pipe)
386483d5c40SKristof Provost        print(rcvd)
387483d5c40SKristof Provost        assert rcvd['ppid'] == 0
388483d5c40SKristof Provost        assert rcvd['data'] == "hello"
389483d5c40SKristof Provost
390483d5c40SKristof Provost        # Check that we have a state for 192.0.2.3 and 192.0.2.2 to 192.0.2.1, but also to 192.0.2.4
391483d5c40SKristof Provost        states = ToolsHelper.get_output("/sbin/pfctl -ss")
392483d5c40SKristof Provost        print(states)
393c22c9879SKristof Provost        assert re.search(r"epair.*sctp 192.0.2.1:.*192.0.2.3:1234", states)
394c22c9879SKristof Provost        assert re.search(r"epair.*sctp 192.0.2.1:.*192.0.2.2:1234", states)
395c22c9879SKristof Provost        assert re.search(r"epair.*sctp 192.0.2.4:.*192.0.2.3:1234", states)
396c22c9879SKristof Provost        assert re.search(r"epair.*sctp 192.0.2.4:.*192.0.2.2:1234", states)
397c22c9879SKristof Provost
398c22c9879SKristof Provost    @pytest.mark.require_user("root")
399c22c9879SKristof Provost    def test_permutation_floating(self):
400c22c9879SKristof Provost        # Test that we generate all permutations of src/dst addresses.
401c22c9879SKristof Provost        # Assign two addresses to each end, and check for the expected states
402c22c9879SKristof Provost        srv_vnet = self.vnet_map["vnet2"]
403c22c9879SKristof Provost
404c22c9879SKristof Provost        ifname = self.vnet_map["vnet1"].iface_alias_map["if1"].name
405c22c9879SKristof Provost        ToolsHelper.print_output("/sbin/ifconfig %s inet alias 192.0.2.4/24" % ifname)
406c22c9879SKristof Provost
407c22c9879SKristof Provost        ToolsHelper.print_output("/sbin/pfctl -e")
408c22c9879SKristof Provost        ToolsHelper.pf_rules([
409c22c9879SKristof Provost            "block proto sctp",
410c22c9879SKristof Provost            "pass on lo",
411c22c9879SKristof Provost            "pass inet proto sctp to 192.0.2.0/24"])
412c22c9879SKristof Provost
413c22c9879SKristof Provost        # Sanity check, we can communicate with the primary address.
414c22c9879SKristof Provost        client = SCTPClient("192.0.2.3", 1234)
415c22c9879SKristof Provost        client.send(b"hello", 0)
416c22c9879SKristof Provost        rcvd = self.wait_object(srv_vnet.pipe)
417c22c9879SKristof Provost        print(rcvd)
418c22c9879SKristof Provost        assert rcvd['ppid'] == 0
419c22c9879SKristof Provost        assert rcvd['data'] == "hello"
420c22c9879SKristof Provost
421c22c9879SKristof Provost        # Check that we have a state for 192.0.2.3 and 192.0.2.2 to 192.0.2.1, but also to 192.0.2.4
422c22c9879SKristof Provost        states = ToolsHelper.get_output("/sbin/pfctl -ss")
423c22c9879SKristof Provost        print(states)
424c22c9879SKristof Provost        assert re.search(r"all sctp 192.0.2.1:.*192.0.2.3:1234", states)
425483d5c40SKristof Provost        assert re.search(r"all sctp 192.0.2.1:.*192.0.2.2:1234", states)
426c22c9879SKristof Provost        assert re.search(r"all sctp 192.0.2.4:.*192.0.2.3:1234", states)
427483d5c40SKristof Provost        assert re.search(r"all sctp 192.0.2.4:.*192.0.2.2:1234", states)
428483d5c40SKristof Provost
429*e4f2733dSKristof Provost    @pytest.mark.require_user("root")
430*e4f2733dSKristof Provost    def test_disallow_related(self):
431*e4f2733dSKristof Provost        srv_vnet = self.vnet_map["vnet2"]
432*e4f2733dSKristof Provost
433*e4f2733dSKristof Provost        ToolsHelper.print_output("/sbin/pfctl -e")
434*e4f2733dSKristof Provost        ToolsHelper.pf_rules([
435*e4f2733dSKristof Provost            "block proto sctp",
436*e4f2733dSKristof Provost            "pass inet proto sctp to 192.0.2.3",
437*e4f2733dSKristof Provost            "pass on lo"])
438*e4f2733dSKristof Provost
439*e4f2733dSKristof Provost        # Sanity check, we can communicate with the primary address.
440*e4f2733dSKristof Provost        client = SCTPClient("192.0.2.3", 1234)
441*e4f2733dSKristof Provost        client.send(b"hello", 0)
442*e4f2733dSKristof Provost        rcvd = self.wait_object(srv_vnet.pipe)
443*e4f2733dSKristof Provost        print(rcvd)
444*e4f2733dSKristof Provost        assert rcvd['ppid'] == 0
445*e4f2733dSKristof Provost        assert rcvd['data'] == "hello"
446*e4f2733dSKristof Provost
447*e4f2733dSKristof Provost        # This shouldn't work
448*e4f2733dSKristof Provost        success=False
449*e4f2733dSKristof Provost        try:
450*e4f2733dSKristof Provost            client.newpeer("192.0.2.2")
451*e4f2733dSKristof Provost            client.send(b"world", 0)
452*e4f2733dSKristof Provost            rcvd = self.wait_object(srv_vnet.pipe)
453*e4f2733dSKristof Provost            print(rcvd)
454*e4f2733dSKristof Provost            assert rcvd['ppid'] == 0
455*e4f2733dSKristof Provost            assert rcvd['data'] == "world"
456*e4f2733dSKristof Provost            success=True
457*e4f2733dSKristof Provost        except:
458*e4f2733dSKristof Provost            success=False
459*e4f2733dSKristof Provost        assert not success
460*e4f2733dSKristof Provost
461*e4f2733dSKristof Provost        # Check that we have a state for 192.0.2.3, but not 192.0.2.2 to 192.0.2.1
462*e4f2733dSKristof Provost        states = ToolsHelper.get_output("/sbin/pfctl -ss")
463*e4f2733dSKristof Provost        assert re.search(r"all sctp 192.0.2.1:.*192.0.2.3:1234", states)
464*e4f2733dSKristof Provost        assert not re.search(r"all sctp 192.0.2.1:.*192.0.2.2:1234", states)
465*e4f2733dSKristof Provost
466*e4f2733dSKristof Provost    @pytest.mark.require_user("root")
467*e4f2733dSKristof Provost    def test_allow_related(self):
468*e4f2733dSKristof Provost        srv_vnet = self.vnet_map["vnet2"]
469*e4f2733dSKristof Provost
470*e4f2733dSKristof Provost        ToolsHelper.print_output("/sbin/pfctl -e")
471*e4f2733dSKristof Provost        ToolsHelper.pf_rules([
472*e4f2733dSKristof Provost            "set state-policy if-bound",
473*e4f2733dSKristof Provost            "block proto sctp",
474*e4f2733dSKristof Provost            "pass inet proto sctp to 192.0.2.3 keep state (allow-related)",
475*e4f2733dSKristof Provost            "pass on lo"])
476*e4f2733dSKristof Provost
477*e4f2733dSKristof Provost        # Sanity check, we can communicate with the primary address.
478*e4f2733dSKristof Provost        client = SCTPClient("192.0.2.3", 1234)
479*e4f2733dSKristof Provost        client.send(b"hello", 0)
480*e4f2733dSKristof Provost        rcvd = self.wait_object(srv_vnet.pipe)
481*e4f2733dSKristof Provost        print(rcvd)
482*e4f2733dSKristof Provost        assert rcvd['ppid'] == 0
483*e4f2733dSKristof Provost        assert rcvd['data'] == "hello"
484*e4f2733dSKristof Provost
485*e4f2733dSKristof Provost        success=False
486*e4f2733dSKristof Provost        try:
487*e4f2733dSKristof Provost            client.newpeer("192.0.2.2")
488*e4f2733dSKristof Provost            client.send(b"world", 0)
489*e4f2733dSKristof Provost            rcvd = self.wait_object(srv_vnet.pipe)
490*e4f2733dSKristof Provost            print(rcvd)
491*e4f2733dSKristof Provost            assert rcvd['ppid'] == 0
492*e4f2733dSKristof Provost            assert rcvd['data'] == "world"
493*e4f2733dSKristof Provost            success=True
494*e4f2733dSKristof Provost        finally:
495*e4f2733dSKristof Provost            # Debug output
496*e4f2733dSKristof Provost            ToolsHelper.print_output("/sbin/pfctl -ss")
497*e4f2733dSKristof Provost            ToolsHelper.print_output("/sbin/pfctl -sr -vv")
498*e4f2733dSKristof Provost        assert success
499*e4f2733dSKristof Provost
500*e4f2733dSKristof Provost        # Check that we have a state for 192.0.2.3 and 192.0.2.2 to 192.0.2.1
501*e4f2733dSKristof Provost        states = ToolsHelper.get_output("/sbin/pfctl -ss")
502*e4f2733dSKristof Provost        assert re.search(r"epair.*sctp 192.0.2.1:.*192.0.2.3:1234", states)
503*e4f2733dSKristof Provost        assert re.search(r"epair.*sctp 192.0.2.1:.*192.0.2.2:1234", states)
504*e4f2733dSKristof Provost
5051a28d5feSKristof Provostclass TestSCTPv6(VnetTestTemplate):
5061a28d5feSKristof Provost    REQUIRED_MODULES = ["sctp", "pf"]
5071a28d5feSKristof Provost    TOPOLOGY = {
5081a28d5feSKristof Provost        "vnet1": {"ifaces": ["if1"]},
5091a28d5feSKristof Provost        "vnet2": {"ifaces": ["if1"]},
5101a28d5feSKristof Provost        "if1": {"prefixes6": [("2001:db8::1/64", "2001:db8::2/64")]},
5111a28d5feSKristof Provost    }
5121a28d5feSKristof Provost
5131a28d5feSKristof Provost    def vnet2_handler(self, vnet):
5141a28d5feSKristof Provost        # Give ourself a second IP address, for multihome testing
5151a28d5feSKristof Provost        ifname = vnet.iface_alias_map["if1"].name
5161a28d5feSKristof Provost        ToolsHelper.print_output("/sbin/ifconfig %s inet6 alias 2001:db8::3/64" % ifname)
5171a28d5feSKristof Provost
5181a28d5feSKristof Provost        # Start an SCTP server process, pipe the ppid + data back to the other vnet?
5191a28d5feSKristof Provost        srv = SCTPServer(socket.AF_INET6, port=1234)
5201a28d5feSKristof Provost        while True:
5211a28d5feSKristof Provost            srv.accept(vnet)
5221a28d5feSKristof Provost
5231a28d5feSKristof Provost    @pytest.mark.require_user("root")
5241a28d5feSKristof Provost    def test_multihome(self):
5251a28d5feSKristof Provost        srv_vnet = self.vnet_map["vnet2"]
5261a28d5feSKristof Provost
5271a28d5feSKristof Provost        ToolsHelper.print_output("/sbin/pfctl -e")
5281a28d5feSKristof Provost        ToolsHelper.pf_rules([
5291a28d5feSKristof Provost            "block proto sctp",
530c22c9879SKristof Provost            "pass on lo",
5311a28d5feSKristof Provost            "pass inet6 proto sctp to 2001:db8::0/64"])
5321a28d5feSKristof Provost
5331a28d5feSKristof Provost        # Sanity check, we can communicate with the primary address.
5341a28d5feSKristof Provost        client = SCTPClient("2001:db8::3", 1234)
5351a28d5feSKristof Provost        client.send(b"hello", 0)
5361a28d5feSKristof Provost        rcvd = self.wait_object(srv_vnet.pipe)
5371a28d5feSKristof Provost        print(rcvd)
5381a28d5feSKristof Provost        assert rcvd['ppid'] == 0
5391a28d5feSKristof Provost        assert rcvd['data'] == "hello"
5401a28d5feSKristof Provost
5411a28d5feSKristof Provost        # Now change to a different peer address
5421a28d5feSKristof Provost        try:
5431a28d5feSKristof Provost            client.newpeer("2001:db8::2")
5441a28d5feSKristof Provost            client.send(b"world", 0)
5451a28d5feSKristof Provost            rcvd = self.wait_object(srv_vnet.pipe)
5461a28d5feSKristof Provost            print(rcvd)
5471a28d5feSKristof Provost            assert rcvd['ppid'] == 0
5481a28d5feSKristof Provost            assert rcvd['data'] == "world"
5491a28d5feSKristof Provost        finally:
5501a28d5feSKristof Provost            # Debug output
5511a28d5feSKristof Provost            ToolsHelper.print_output("/sbin/pfctl -ss -vv")
5521a28d5feSKristof Provost
5531a28d5feSKristof Provost        # Check that we have the expected states
5541a28d5feSKristof Provost        states = ToolsHelper.get_output("/sbin/pfctl -ss")
5551a28d5feSKristof Provost        assert re.search(r"all sctp 2001:db8::1\[.*2001:db8::3\[1234\]", states)
5561a28d5feSKristof Provost        assert re.search(r"all sctp 2001:db8::1\[.*2001:db8::2\[1234\]", states)
5571a28d5feSKristof Provost
5581a28d5feSKristof Provost    @pytest.mark.require_user("root")
5591a28d5feSKristof Provost    def test_multihome_asconf(self):
5601a28d5feSKristof Provost        srv_vnet = self.vnet_map["vnet2"]
5611a28d5feSKristof Provost
5621a28d5feSKristof Provost        # Assign a second IP to ourselves
5631a28d5feSKristof Provost        ToolsHelper.print_output("/sbin/ifconfig %s inet6 alias 2001:db8::10/64"
5641a28d5feSKristof Provost            % self.vnet.iface_alias_map["if1"].name)
5651a28d5feSKristof Provost        ToolsHelper.print_output("/sbin/pfctl -e")
5661a28d5feSKristof Provost        ToolsHelper.pf_rules([
5671a28d5feSKristof Provost            "block proto sctp",
568c22c9879SKristof Provost            "pass on lo",
5691a28d5feSKristof Provost            "pass inet6 proto sctp from 2001:db8::/64"])
5701a28d5feSKristof Provost
5711a28d5feSKristof Provost        # Sanity check, we can communicate with the primary address.
5721a28d5feSKristof Provost        client = SCTPClient("2001:db8::3", 1234, "2001:db8::1")
5731a28d5feSKristof Provost        client.send(b"hello", 0)
5741a28d5feSKristof Provost        rcvd = self.wait_object(srv_vnet.pipe)
5751a28d5feSKristof Provost        print(rcvd)
5761a28d5feSKristof Provost        assert rcvd['ppid'] == 0
5771a28d5feSKristof Provost        assert rcvd['data'] == "hello"
5781a28d5feSKristof Provost
5791a28d5feSKristof Provost        # Now add our second address to the connection
5801a28d5feSKristof Provost        client.bindx("2001:db8::10", True)
5811a28d5feSKristof Provost
5821a28d5feSKristof Provost        # We can still communicate
5831a28d5feSKristof Provost        client.send(b"world", 0)
5841a28d5feSKristof Provost        rcvd = self.wait_object(srv_vnet.pipe)
5851a28d5feSKristof Provost        print(rcvd)
5861a28d5feSKristof Provost        assert rcvd['ppid'] == 0
5871a28d5feSKristof Provost        assert rcvd['data'] == "world"
5881a28d5feSKristof Provost
5891a28d5feSKristof Provost        # Now change to a different peer address
5901a28d5feSKristof Provost        try:
5911a28d5feSKristof Provost            client.newprimary("2001:db8::10")
5921a28d5feSKristof Provost            client.send(b"!", 0)
5931a28d5feSKristof Provost            rcvd = self.wait_object(srv_vnet.pipe, 5)
5941a28d5feSKristof Provost            print(rcvd)
5951a28d5feSKristof Provost            assert rcvd['ppid'] == 0
5961a28d5feSKristof Provost            assert rcvd['data'] == "!"
5971a28d5feSKristof Provost        finally:
5981a28d5feSKristof Provost            # Debug output
5991a28d5feSKristof Provost            ToolsHelper.print_output("/sbin/pfctl -ss -vv")
6001a28d5feSKristof Provost
6011a28d5feSKristof Provost        # Check that we have the expected states
6021a28d5feSKristof Provost        states = ToolsHelper.get_output("/sbin/pfctl -ss")
6031a28d5feSKristof Provost        assert re.search(r"all sctp 2001:db8::1\[.*2001:db8::3\[1234\]", states)
6041a28d5feSKristof Provost        assert re.search(r"all sctp 2001:db8::10\[.*2001:db8::3\[1234\]", states)
60597340b68SKristof Provost
60697340b68SKristof Provost        # Now remove 2001:db8::1 as an address
60797340b68SKristof Provost        client.bindx("2001:db8::1", False)
60897340b68SKristof Provost
60997340b68SKristof Provost        # Wecan still communicate
61097340b68SKristof Provost        try:
61197340b68SKristof Provost            client.send(b"More data", 0)
61297340b68SKristof Provost            rcvd = self.wait_object(srv_vnet.pipe, 5)
61397340b68SKristof Provost            print(rcvd)
61497340b68SKristof Provost            assert rcvd['ppid'] == 0
61597340b68SKristof Provost            assert rcvd['data'] == "More data"
61697340b68SKristof Provost        finally:
61797340b68SKristof Provost            # Debug output
61897340b68SKristof Provost            ToolsHelper.print_output("/sbin/pfctl -ss -vv")
61997340b68SKristof Provost
62097340b68SKristof Provost        # Verify that the state is closing
62197340b68SKristof Provost        states = ToolsHelper.get_output("/sbin/pfctl -ss")
62297340b68SKristof Provost        assert re.search(r"all sctp 2001:db8::1\[.*2001:db8::3\[1234\].*SHUTDOWN", states)
623483d5c40SKristof Provost
624483d5c40SKristof Provost    @pytest.mark.require_user("root")
625483d5c40SKristof Provost    def test_permutation(self):
626483d5c40SKristof Provost        # Test that we generate all permutations of src/dst addresses.
627483d5c40SKristof Provost        # Assign two addresses to each end, and check for the expected states
628483d5c40SKristof Provost        srv_vnet = self.vnet_map["vnet2"]
629483d5c40SKristof Provost
630483d5c40SKristof Provost        ifname = self.vnet_map["vnet1"].iface_alias_map["if1"].name
631483d5c40SKristof Provost        ToolsHelper.print_output("/sbin/ifconfig %s inet6 alias 2001:db8::4/64" % ifname)
632483d5c40SKristof Provost
633483d5c40SKristof Provost        ToolsHelper.print_output("/sbin/pfctl -e")
634483d5c40SKristof Provost        ToolsHelper.pf_rules([
635c22c9879SKristof Provost            "set state-policy if-bound",
636483d5c40SKristof Provost            "block proto sctp",
637c22c9879SKristof Provost            "pass on lo",
638c22c9879SKristof Provost            "pass inet6 proto sctp to 2001:db8::0/64"])
639c22c9879SKristof Provost
640c22c9879SKristof Provost        # Sanity check, we can communicate with the primary address.
641c22c9879SKristof Provost        client = SCTPClient("2001:db8::3", 1234)
642c22c9879SKristof Provost        client.send(b"hello", 0)
643c22c9879SKristof Provost        rcvd = self.wait_object(srv_vnet.pipe)
644c22c9879SKristof Provost        print(rcvd)
645c22c9879SKristof Provost        assert rcvd['ppid'] == 0
646c22c9879SKristof Provost        assert rcvd['data'] == "hello"
647c22c9879SKristof Provost
648c22c9879SKristof Provost        # Check that we have a state for 2001:db8::3 and 2001:db8::2 to 2001:db8::1, but also to 2001:db8::4
649c22c9879SKristof Provost        states = ToolsHelper.get_output("/sbin/pfctl -ss")
650c22c9879SKristof Provost        print(states)
651c22c9879SKristof Provost        assert re.search(r"epair.*sctp 2001:db8::1\[.*2001:db8::2\[1234\]", states)
652c22c9879SKristof Provost        assert re.search(r"epair.*sctp 2001:db8::1\[.*2001:db8::3\[1234\]", states)
653c22c9879SKristof Provost        assert re.search(r"epair.*sctp 2001:db8::4\[.*2001:db8::2\[1234\]", states)
654c22c9879SKristof Provost        assert re.search(r"epair.*sctp 2001:db8::4\[.*2001:db8::3\[1234\]", states)
655c22c9879SKristof Provost
656c22c9879SKristof Provost    @pytest.mark.require_user("root")
657c22c9879SKristof Provost    def test_permutation_floating(self):
658c22c9879SKristof Provost        # Test that we generate all permutations of src/dst addresses.
659c22c9879SKristof Provost        # Assign two addresses to each end, and check for the expected states
660c22c9879SKristof Provost        srv_vnet = self.vnet_map["vnet2"]
661c22c9879SKristof Provost
662c22c9879SKristof Provost        ifname = self.vnet_map["vnet1"].iface_alias_map["if1"].name
663c22c9879SKristof Provost        ToolsHelper.print_output("/sbin/ifconfig %s inet6 alias 2001:db8::4/64" % ifname)
664c22c9879SKristof Provost
665c22c9879SKristof Provost        ToolsHelper.print_output("/sbin/pfctl -e")
666c22c9879SKristof Provost        ToolsHelper.pf_rules([
667c22c9879SKristof Provost            "block proto sctp",
668c22c9879SKristof Provost            "pass on lo",
669483d5c40SKristof Provost            "pass inet6 proto sctp to 2001:db8::0/64"])
670483d5c40SKristof Provost
671483d5c40SKristof Provost        # Sanity check, we can communicate with the primary address.
672483d5c40SKristof Provost        client = SCTPClient("2001:db8::3", 1234)
673483d5c40SKristof Provost        client.send(b"hello", 0)
674483d5c40SKristof Provost        rcvd = self.wait_object(srv_vnet.pipe)
675483d5c40SKristof Provost        print(rcvd)
676483d5c40SKristof Provost        assert rcvd['ppid'] == 0
677483d5c40SKristof Provost        assert rcvd['data'] == "hello"
678483d5c40SKristof Provost
679483d5c40SKristof Provost        # Check that we have a state for 2001:db8::3 and 2001:db8::2 to 2001:db8::1, but also to 2001:db8::4
680483d5c40SKristof Provost        states = ToolsHelper.get_output("/sbin/pfctl -ss")
681483d5c40SKristof Provost        print(states)
682483d5c40SKristof Provost        assert re.search(r"all sctp 2001:db8::1\[.*2001:db8::2\[1234\]", states)
683483d5c40SKristof Provost        assert re.search(r"all sctp 2001:db8::1\[.*2001:db8::3\[1234\]", states)
684483d5c40SKristof Provost        assert re.search(r"all sctp 2001:db8::4\[.*2001:db8::2\[1234\]", states)
685483d5c40SKristof Provost        assert re.search(r"all sctp 2001:db8::4\[.*2001:db8::3\[1234\]", states)
686