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