1ce414d02SAlexander V. Chernikovimport errno 2ce414d02SAlexander V. Chernikovimport ipaddress 3ce414d02SAlexander V. Chernikovimport socket 4ce414d02SAlexander V. Chernikovimport struct 5ce414d02SAlexander V. Chernikovimport time 6ce414d02SAlexander V. Chernikovfrom ctypes import c_byte 7ce414d02SAlexander V. Chernikovfrom ctypes import c_uint 8ce414d02SAlexander V. Chernikovfrom ctypes import Structure 9ce414d02SAlexander V. Chernikov 10ce414d02SAlexander V. Chernikovimport pytest 11ce414d02SAlexander V. Chernikovfrom atf_python.sys.net.rtsock import SaHelper 12ce414d02SAlexander V. Chernikovfrom atf_python.sys.net.tools import ToolsHelper 13ce414d02SAlexander V. Chernikovfrom atf_python.sys.net.vnet import run_cmd 14ce414d02SAlexander V. Chernikovfrom atf_python.sys.net.vnet import SingleVnetTestTemplate 15ce414d02SAlexander V. Chernikovfrom atf_python.sys.net.vnet import VnetTestTemplate 16ce414d02SAlexander V. Chernikov 17ce414d02SAlexander V. Chernikov 18ce414d02SAlexander V. Chernikovclass In6Pktinfo(Structure): 19ce414d02SAlexander V. Chernikov _fields_ = [ 20ce414d02SAlexander V. Chernikov ("ipi6_addr", c_byte * 16), 21ce414d02SAlexander V. Chernikov ("ipi6_ifindex", c_uint), 22ce414d02SAlexander V. Chernikov ] 23ce414d02SAlexander V. Chernikov 24ce414d02SAlexander V. Chernikov 25ce414d02SAlexander V. Chernikovclass VerboseSocketServer: 26ce414d02SAlexander V. Chernikov def __init__(self, ip: str, port: int, ifname: str = None): 27ce414d02SAlexander V. Chernikov self.ip = ip 28ce414d02SAlexander V. Chernikov self.port = port 29ce414d02SAlexander V. Chernikov 30ce414d02SAlexander V. Chernikov s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 31ce414d02SAlexander V. Chernikov s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVPKTINFO, 1) 32ce414d02SAlexander V. Chernikov addr = ipaddress.ip_address(ip) 33ce414d02SAlexander V. Chernikov if addr.is_link_local and ifname: 34ce414d02SAlexander V. Chernikov ifindex = socket.if_nametoindex(ifname) 35ce414d02SAlexander V. Chernikov addr_tuple = (ip, port, 0, ifindex) 36ce414d02SAlexander V. Chernikov elif addr.is_multicast and ifname: 37ce414d02SAlexander V. Chernikov ifindex = socket.if_nametoindex(ifname) 38ce414d02SAlexander V. Chernikov mreq = socket.inet_pton(socket.AF_INET6, ip) + struct.pack("I", ifindex) 39ce414d02SAlexander V. Chernikov s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) 40ce414d02SAlexander V. Chernikov print("## JOINED group {} % {}".format(ip, ifname)) 41ce414d02SAlexander V. Chernikov addr_tuple = ("::", port, 0, ifindex) 42ce414d02SAlexander V. Chernikov else: 43ce414d02SAlexander V. Chernikov addr_tuple = (ip, port, 0, 0) 44ce414d02SAlexander V. Chernikov print("## Listening on [{}]:{}".format(addr_tuple[0], port)) 45ce414d02SAlexander V. Chernikov s.bind(addr_tuple) 46ce414d02SAlexander V. Chernikov self.socket = s 47ce414d02SAlexander V. Chernikov 48ce414d02SAlexander V. Chernikov def recv(self): 49ce414d02SAlexander V. Chernikov # data = self.socket.recv(4096) 50ce414d02SAlexander V. Chernikov # print("RX: " + data) 51ce414d02SAlexander V. Chernikov data, ancdata, msg_flags, address = self.socket.recvmsg(4096, 128) 52ce414d02SAlexander V. Chernikov # Assume ancdata has just 1 item 53ce414d02SAlexander V. Chernikov info = In6Pktinfo.from_buffer_copy(ancdata[0][2]) 54ce414d02SAlexander V. Chernikov dst_ip = socket.inet_ntop(socket.AF_INET6, info.ipi6_addr) 55ce414d02SAlexander V. Chernikov dst_iface = socket.if_indextoname(info.ipi6_ifindex) 56ce414d02SAlexander V. Chernikov 57ce414d02SAlexander V. Chernikov tx_obj = { 58ce414d02SAlexander V. Chernikov "data": data, 59ce414d02SAlexander V. Chernikov "src_ip": address[0], 60ce414d02SAlexander V. Chernikov "dst_ip": dst_ip, 61ce414d02SAlexander V. Chernikov "dst_iface": dst_iface, 62ce414d02SAlexander V. Chernikov } 63ce414d02SAlexander V. Chernikov return tx_obj 64ce414d02SAlexander V. Chernikov 65ce414d02SAlexander V. Chernikov 66ce414d02SAlexander V. Chernikovclass BaseTestIP6Ouput(VnetTestTemplate): 67ce414d02SAlexander V. Chernikov TOPOLOGY = { 68ce414d02SAlexander V. Chernikov "vnet1": {"ifaces": ["if1", "if2", "if3"]}, 69ce414d02SAlexander V. Chernikov "vnet2": {"ifaces": ["if1", "if2", "if3"]}, 70ce414d02SAlexander V. Chernikov "if1": {"prefixes6": [("2001:db8:a::1/64", "2001:db8:a::2/64")]}, 71ce414d02SAlexander V. Chernikov "if2": {"prefixes6": [("2001:db8:b::1/64", "2001:db8:b::2/64")]}, 72ce414d02SAlexander V. Chernikov "if3": {"prefixes6": [("2001:db8:c::1/64", "2001:db8:c::2/64")]}, 73ce414d02SAlexander V. Chernikov } 74ce414d02SAlexander V. Chernikov DEFAULT_PORT = 45365 75ce414d02SAlexander V. Chernikov 76*f63825ffSAlexander V. Chernikov def _vnet2_handler(self, vnet, ip: str, os_ifname: str = None): 77ce414d02SAlexander V. Chernikov """Generic listener that sends first received packet with metadata 78ce414d02SAlexander V. Chernikov back to the sender via pipw 79ce414d02SAlexander V. Chernikov """ 80ce414d02SAlexander V. Chernikov ll_data = ToolsHelper.get_linklocals() 81ce414d02SAlexander V. Chernikov # Start listener 82ce414d02SAlexander V. Chernikov ss = VerboseSocketServer(ip, self.DEFAULT_PORT, os_ifname) 83*f63825ffSAlexander V. Chernikov vnet.pipe.send(ll_data) 84ce414d02SAlexander V. Chernikov 85ce414d02SAlexander V. Chernikov tx_obj = ss.recv() 86ce414d02SAlexander V. Chernikov tx_obj["dst_iface_alias"] = vnet.iface_map[tx_obj["dst_iface"]].alias 87*f63825ffSAlexander V. Chernikov vnet.pipe.send(tx_obj) 88ce414d02SAlexander V. Chernikov 89ce414d02SAlexander V. Chernikov 90ce414d02SAlexander V. Chernikovclass TestIP6Output(BaseTestIP6Ouput): 91*f63825ffSAlexander V. Chernikov def vnet2_handler(self, vnet): 92ce414d02SAlexander V. Chernikov ip = str(vnet.iface_alias_map["if2"].first_ipv6.ip) 93*f63825ffSAlexander V. Chernikov self._vnet2_handler(vnet, ip, None) 94ce414d02SAlexander V. Chernikov 95ce414d02SAlexander V. Chernikov @pytest.mark.require_user("root") 96ce414d02SAlexander V. Chernikov def test_output6_base(self): 97ce414d02SAlexander V. Chernikov """Tests simple UDP output""" 98ce414d02SAlexander V. Chernikov second_vnet = self.vnet_map["vnet2"] 99ce414d02SAlexander V. Chernikov 100ce414d02SAlexander V. Chernikov # Pick target on if2 vnet2's end 101ce414d02SAlexander V. Chernikov ifaddr = ipaddress.ip_interface(self.TOPOLOGY["if2"]["prefixes6"][0][1]) 102ce414d02SAlexander V. Chernikov ip = str(ifaddr.ip) 103ce414d02SAlexander V. Chernikov 104ce414d02SAlexander V. Chernikov s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 105ce414d02SAlexander V. Chernikov data = bytes("AAAA", "utf-8") 106ce414d02SAlexander V. Chernikov print("## TX packet to {},{}".format(ip, self.DEFAULT_PORT)) 107ce414d02SAlexander V. Chernikov 108ce414d02SAlexander V. Chernikov # Wait for the child to become ready 109ce414d02SAlexander V. Chernikov self.wait_object(second_vnet.pipe) 110ce414d02SAlexander V. Chernikov s.sendto(data, (ip, self.DEFAULT_PORT)) 111ce414d02SAlexander V. Chernikov 112ce414d02SAlexander V. Chernikov # Wait for the received object 113ce414d02SAlexander V. Chernikov rx_obj = self.wait_object(second_vnet.pipe) 114ce414d02SAlexander V. Chernikov assert rx_obj["dst_ip"] == ip 115ce414d02SAlexander V. Chernikov assert rx_obj["dst_iface_alias"] == "if2" 116ce414d02SAlexander V. Chernikov 117ce414d02SAlexander V. Chernikov @pytest.mark.require_user("root") 118ce414d02SAlexander V. Chernikov def test_output6_nhop(self): 119ce414d02SAlexander V. Chernikov """Tests UDP output with custom nhop set""" 120ce414d02SAlexander V. Chernikov second_vnet = self.vnet_map["vnet2"] 121ce414d02SAlexander V. Chernikov 122ce414d02SAlexander V. Chernikov # Pick target on if2 vnet2's end 123ce414d02SAlexander V. Chernikov ifaddr = ipaddress.ip_interface(self.TOPOLOGY["if2"]["prefixes6"][0][1]) 124ce414d02SAlexander V. Chernikov ip_dst = str(ifaddr.ip) 125ce414d02SAlexander V. Chernikov # Pick nexthop on if1 126ce414d02SAlexander V. Chernikov ifaddr = ipaddress.ip_interface(self.TOPOLOGY["if1"]["prefixes6"][0][1]) 127ce414d02SAlexander V. Chernikov ip_next = str(ifaddr.ip) 128ce414d02SAlexander V. Chernikov sin6_next = SaHelper.ip6_sa(ip_next, 0) 129ce414d02SAlexander V. Chernikov 130ce414d02SAlexander V. Chernikov s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, 0) 131ce414d02SAlexander V. Chernikov s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_NEXTHOP, sin6_next) 132ce414d02SAlexander V. Chernikov 133ce414d02SAlexander V. Chernikov # Wait for the child to become ready 134ce414d02SAlexander V. Chernikov self.wait_object(second_vnet.pipe) 135ce414d02SAlexander V. Chernikov data = bytes("AAAA", "utf-8") 136ce414d02SAlexander V. Chernikov s.sendto(data, (ip_dst, self.DEFAULT_PORT)) 137ce414d02SAlexander V. Chernikov 138ce414d02SAlexander V. Chernikov # Wait for the received object 139ce414d02SAlexander V. Chernikov rx_obj = self.wait_object(second_vnet.pipe) 140ce414d02SAlexander V. Chernikov assert rx_obj["dst_ip"] == ip_dst 141ce414d02SAlexander V. Chernikov assert rx_obj["dst_iface_alias"] == "if1" 142ce414d02SAlexander V. Chernikov 143ce414d02SAlexander V. Chernikov @pytest.mark.parametrize( 144ce414d02SAlexander V. Chernikov "params", 145ce414d02SAlexander V. Chernikov [ 146ce414d02SAlexander V. Chernikov # esrc: src-ip, if: src-interface, esrc: expected-src, 147ce414d02SAlexander V. Chernikov # eif: expected-rx-interface 148ce414d02SAlexander V. Chernikov pytest.param({"esrc": "2001:db8:b::1", "eif": "if2"}, id="empty"), 149ce414d02SAlexander V. Chernikov pytest.param( 150ce414d02SAlexander V. Chernikov {"src": "2001:db8:c::1", "esrc": "2001:db8:c::1", "eif": "if2"}, 151ce414d02SAlexander V. Chernikov id="iponly1", 152ce414d02SAlexander V. Chernikov ), 153ce414d02SAlexander V. Chernikov pytest.param( 154ce414d02SAlexander V. Chernikov { 155ce414d02SAlexander V. Chernikov "src": "2001:db8:c::1", 156ce414d02SAlexander V. Chernikov "if": "if3", 157ce414d02SAlexander V. Chernikov "ex": errno.EHOSTUNREACH, 158ce414d02SAlexander V. Chernikov }, 159ce414d02SAlexander V. Chernikov id="ipandif", 160ce414d02SAlexander V. Chernikov ), 161ce414d02SAlexander V. Chernikov pytest.param( 162ce414d02SAlexander V. Chernikov { 163ce414d02SAlexander V. Chernikov "src": "2001:db8:c::aaaa", 164ce414d02SAlexander V. Chernikov "ex": errno.EADDRNOTAVAIL, 165ce414d02SAlexander V. Chernikov }, 166ce414d02SAlexander V. Chernikov id="nolocalip", 167ce414d02SAlexander V. Chernikov ), 168ce414d02SAlexander V. Chernikov pytest.param( 169ce414d02SAlexander V. Chernikov {"if": "if2", "src": "2001:db8:b::1", "eif": "if2"}, id="ifsame" 170ce414d02SAlexander V. Chernikov ), 171ce414d02SAlexander V. Chernikov ], 172ce414d02SAlexander V. Chernikov ) 173ce414d02SAlexander V. Chernikov @pytest.mark.require_user("root") 174ce414d02SAlexander V. Chernikov def test_output6_pktinfo(self, params): 175ce414d02SAlexander V. Chernikov """Tests simple UDP output""" 176ce414d02SAlexander V. Chernikov second_vnet = self.vnet_map["vnet2"] 177ce414d02SAlexander V. Chernikov vnet = self.vnet 178ce414d02SAlexander V. Chernikov 179ce414d02SAlexander V. Chernikov # Pick target on if2 vnet2's end 180ce414d02SAlexander V. Chernikov ifaddr = ipaddress.ip_interface(self.TOPOLOGY["if2"]["prefixes6"][0][1]) 181ce414d02SAlexander V. Chernikov dst_ip = str(ifaddr.ip) 182ce414d02SAlexander V. Chernikov 183ce414d02SAlexander V. Chernikov src_ip = params.get("src", "") 184ce414d02SAlexander V. Chernikov src_ifname = params.get("if", "") 185ce414d02SAlexander V. Chernikov expected_ip = params.get("esrc", "") 186ce414d02SAlexander V. Chernikov expected_ifname = params.get("eif", "") 187ce414d02SAlexander V. Chernikov errno = params.get("ex", 0) 188ce414d02SAlexander V. Chernikov 189ce414d02SAlexander V. Chernikov pktinfo = In6Pktinfo() 190ce414d02SAlexander V. Chernikov if src_ip: 191ce414d02SAlexander V. Chernikov for i, b in enumerate(socket.inet_pton(socket.AF_INET6, src_ip)): 192ce414d02SAlexander V. Chernikov pktinfo.ipi6_addr[i] = b 193ce414d02SAlexander V. Chernikov if src_ifname: 194ce414d02SAlexander V. Chernikov os_ifname = vnet.iface_alias_map[src_ifname].name 195ce414d02SAlexander V. Chernikov pktinfo.ipi6_ifindex = socket.if_nametoindex(os_ifname) 196ce414d02SAlexander V. Chernikov 197ce414d02SAlexander V. Chernikov # Wait for the child to become ready 198ce414d02SAlexander V. Chernikov self.wait_object(second_vnet.pipe) 199ce414d02SAlexander V. Chernikov data = bytes("AAAA", "utf-8") 200ce414d02SAlexander V. Chernikov 201ce414d02SAlexander V. Chernikov s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, 0) 202ce414d02SAlexander V. Chernikov try: 203ce414d02SAlexander V. Chernikov s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_PKTINFO, bytes(pktinfo)) 204ce414d02SAlexander V. Chernikov aux = (socket.IPPROTO_IPV6, socket.IPV6_PKTINFO, bytes(pktinfo)) 205ce414d02SAlexander V. Chernikov s.sendto(data, (dst_ip, self.DEFAULT_PORT)) 206ce414d02SAlexander V. Chernikov except OSError as e: 207ce414d02SAlexander V. Chernikov if not errno: 208ce414d02SAlexander V. Chernikov raise 209ce414d02SAlexander V. Chernikov assert e.errno == errno 210ce414d02SAlexander V. Chernikov print("Correctly raised {}".format(e)) 211ce414d02SAlexander V. Chernikov return 212ce414d02SAlexander V. Chernikov 213ce414d02SAlexander V. Chernikov # Wait for the received object 214ce414d02SAlexander V. Chernikov rx_obj = self.wait_object(second_vnet.pipe) 215ce414d02SAlexander V. Chernikov 216ce414d02SAlexander V. Chernikov assert rx_obj["dst_ip"] == dst_ip 217ce414d02SAlexander V. Chernikov if expected_ip: 218ce414d02SAlexander V. Chernikov assert rx_obj["src_ip"] == expected_ip 219ce414d02SAlexander V. Chernikov if expected_ifname: 220ce414d02SAlexander V. Chernikov assert rx_obj["dst_iface_alias"] == expected_ifname 221ce414d02SAlexander V. Chernikov 222ce414d02SAlexander V. Chernikov 223ce414d02SAlexander V. Chernikovclass TestIP6OutputLL(BaseTestIP6Ouput): 224*f63825ffSAlexander V. Chernikov def vnet2_handler(self, vnet): 225ce414d02SAlexander V. Chernikov """Generic listener that sends first received packet with metadata 226ce414d02SAlexander V. Chernikov back to the sender via pipw 227ce414d02SAlexander V. Chernikov """ 228ce414d02SAlexander V. Chernikov os_ifname = vnet.iface_alias_map["if2"].name 229ce414d02SAlexander V. Chernikov ll_data = ToolsHelper.get_linklocals() 230ce414d02SAlexander V. Chernikov ll_ip, _ = ll_data[os_ifname][0] 231*f63825ffSAlexander V. Chernikov self._vnet2_handler(vnet, ll_ip, os_ifname) 232ce414d02SAlexander V. Chernikov 233ce414d02SAlexander V. Chernikov @pytest.mark.require_user("root") 234ce414d02SAlexander V. Chernikov def test_output6_linklocal(self): 235ce414d02SAlexander V. Chernikov """Tests simple UDP output""" 236ce414d02SAlexander V. Chernikov second_vnet = self.vnet_map["vnet2"] 237ce414d02SAlexander V. Chernikov 238ce414d02SAlexander V. Chernikov # Wait for the child to become ready 239ce414d02SAlexander V. Chernikov ll_data = self.wait_object(second_vnet.pipe) 240ce414d02SAlexander V. Chernikov 241ce414d02SAlexander V. Chernikov # Pick LL address on if2 vnet2's end 242ce414d02SAlexander V. Chernikov ip, _ = ll_data[second_vnet.iface_alias_map["if2"].name][0] 243ce414d02SAlexander V. Chernikov # Get local interface scope 244ce414d02SAlexander V. Chernikov os_ifname = self.vnet.iface_alias_map["if2"].name 245ce414d02SAlexander V. Chernikov scopeid = socket.if_nametoindex(os_ifname) 246ce414d02SAlexander V. Chernikov 247ce414d02SAlexander V. Chernikov s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 248ce414d02SAlexander V. Chernikov data = bytes("AAAA", "utf-8") 249ce414d02SAlexander V. Chernikov target = (ip, self.DEFAULT_PORT, 0, scopeid) 250ce414d02SAlexander V. Chernikov print("## TX packet to {}%{},{}".format(ip, scopeid, target[1])) 251ce414d02SAlexander V. Chernikov 252ce414d02SAlexander V. Chernikov s.sendto(data, target) 253ce414d02SAlexander V. Chernikov 254ce414d02SAlexander V. Chernikov # Wait for the received object 255ce414d02SAlexander V. Chernikov rx_obj = self.wait_object(second_vnet.pipe) 256ce414d02SAlexander V. Chernikov assert rx_obj["dst_ip"] == ip 257ce414d02SAlexander V. Chernikov assert rx_obj["dst_iface_alias"] == "if2" 258ce414d02SAlexander V. Chernikov 259ce414d02SAlexander V. Chernikov 260ce414d02SAlexander V. Chernikovclass TestIP6OutputNhopLL(BaseTestIP6Ouput): 261*f63825ffSAlexander V. Chernikov def vnet2_handler(self, vnet): 262ce414d02SAlexander V. Chernikov """Generic listener that sends first received packet with metadata 263ce414d02SAlexander V. Chernikov back to the sender via pipw 264ce414d02SAlexander V. Chernikov """ 265ce414d02SAlexander V. Chernikov ip = str(vnet.iface_alias_map["if2"].first_ipv6.ip) 266*f63825ffSAlexander V. Chernikov self._vnet2_handler(vnet, ip, None) 267ce414d02SAlexander V. Chernikov 268ce414d02SAlexander V. Chernikov @pytest.mark.require_user("root") 269ce414d02SAlexander V. Chernikov def test_output6_nhop_linklocal(self): 270ce414d02SAlexander V. Chernikov """Tests UDP output with custom link-local nhop set""" 271ce414d02SAlexander V. Chernikov second_vnet = self.vnet_map["vnet2"] 272ce414d02SAlexander V. Chernikov 273ce414d02SAlexander V. Chernikov # Wait for the child to become ready 274ce414d02SAlexander V. Chernikov ll_data = self.wait_object(second_vnet.pipe) 275ce414d02SAlexander V. Chernikov 276ce414d02SAlexander V. Chernikov # Pick target on if2 vnet2's end 277ce414d02SAlexander V. Chernikov ifaddr = ipaddress.ip_interface(self.TOPOLOGY["if2"]["prefixes6"][0][1]) 278ce414d02SAlexander V. Chernikov ip_dst = str(ifaddr.ip) 279ce414d02SAlexander V. Chernikov # Pick nexthop on if1 280ce414d02SAlexander V. Chernikov ip_next, _ = ll_data[second_vnet.iface_alias_map["if1"].name][0] 281ce414d02SAlexander V. Chernikov # Get local interfaces 282ce414d02SAlexander V. Chernikov os_ifname = self.vnet.iface_alias_map["if1"].name 283ce414d02SAlexander V. Chernikov scopeid = socket.if_nametoindex(os_ifname) 284ce414d02SAlexander V. Chernikov sin6_next = SaHelper.ip6_sa(ip_next, scopeid) 285ce414d02SAlexander V. Chernikov 286ce414d02SAlexander V. Chernikov s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, 0) 287ce414d02SAlexander V. Chernikov s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_NEXTHOP, sin6_next) 288ce414d02SAlexander V. Chernikov 289ce414d02SAlexander V. Chernikov data = bytes("AAAA", "utf-8") 290ce414d02SAlexander V. Chernikov s.sendto(data, (ip_dst, self.DEFAULT_PORT)) 291ce414d02SAlexander V. Chernikov 292ce414d02SAlexander V. Chernikov # Wait for the received object 293ce414d02SAlexander V. Chernikov rx_obj = self.wait_object(second_vnet.pipe) 294ce414d02SAlexander V. Chernikov assert rx_obj["dst_ip"] == ip_dst 295ce414d02SAlexander V. Chernikov assert rx_obj["dst_iface_alias"] == "if1" 296ce414d02SAlexander V. Chernikov 297ce414d02SAlexander V. Chernikov 29878d11a35SAlexander V. Chernikovclass TestIP6OutputScope(BaseTestIP6Ouput): 299*f63825ffSAlexander V. Chernikov def vnet2_handler(self, vnet): 30078d11a35SAlexander V. Chernikov """Generic listener that sends first received packet with metadata 30178d11a35SAlexander V. Chernikov back to the sender via pipw 30278d11a35SAlexander V. Chernikov """ 303*f63825ffSAlexander V. Chernikov bind_ip, bind_ifp = self.wait_object(vnet.pipe) 30478d11a35SAlexander V. Chernikov if bind_ip is None: 30578d11a35SAlexander V. Chernikov os_ifname = vnet.iface_alias_map[bind_ifp].name 30678d11a35SAlexander V. Chernikov ll_data = ToolsHelper.get_linklocals() 30778d11a35SAlexander V. Chernikov bind_ip, _ = ll_data[os_ifname][0] 30878d11a35SAlexander V. Chernikov if bind_ifp is not None: 30978d11a35SAlexander V. Chernikov bind_ifp = vnet.iface_alias_map[bind_ifp].name 31078d11a35SAlexander V. Chernikov print("## BIND {}%{}".format(bind_ip, bind_ifp)) 311*f63825ffSAlexander V. Chernikov self._vnet2_handler(vnet, bind_ip, bind_ifp) 31278d11a35SAlexander V. Chernikov 31378d11a35SAlexander V. Chernikov @pytest.mark.parametrize( 31478d11a35SAlexander V. Chernikov "params", 31578d11a35SAlexander V. Chernikov [ 31678d11a35SAlexander V. Chernikov # sif/dif: source/destination interface (for link-local addr) 31778d11a35SAlexander V. Chernikov # sip/dip: source/destination ip (for non-LL addr) 31878d11a35SAlexander V. Chernikov # ex: OSError errno that sendto() must raise 31978d11a35SAlexander V. Chernikov pytest.param({"sif": "if2", "dif": "if2"}, id="same"), 32078d11a35SAlexander V. Chernikov pytest.param( 32178d11a35SAlexander V. Chernikov { 32278d11a35SAlexander V. Chernikov "sif": "if1", 32378d11a35SAlexander V. Chernikov "dif": "if2", 32478d11a35SAlexander V. Chernikov "ex": errno.EHOSTUNREACH, 32578d11a35SAlexander V. Chernikov }, 32678d11a35SAlexander V. Chernikov id="ll_differentif1", 32778d11a35SAlexander V. Chernikov ), 32878d11a35SAlexander V. Chernikov pytest.param( 32978d11a35SAlexander V. Chernikov { 33078d11a35SAlexander V. Chernikov "sif": "if1", 33178d11a35SAlexander V. Chernikov "dip": "2001:db8:b::2", 33278d11a35SAlexander V. Chernikov "ex": errno.EHOSTUNREACH, 33378d11a35SAlexander V. Chernikov }, 33478d11a35SAlexander V. Chernikov id="ll_differentif2", 33578d11a35SAlexander V. Chernikov ), 33678d11a35SAlexander V. Chernikov pytest.param( 33778d11a35SAlexander V. Chernikov { 33878d11a35SAlexander V. Chernikov "sip": "2001:db8:a::1", 33978d11a35SAlexander V. Chernikov "dif": "if2", 34078d11a35SAlexander V. Chernikov }, 34178d11a35SAlexander V. Chernikov id="gu_to_ll", 34278d11a35SAlexander V. Chernikov ), 34378d11a35SAlexander V. Chernikov ], 34478d11a35SAlexander V. Chernikov ) 34578d11a35SAlexander V. Chernikov @pytest.mark.require_user("root") 34678d11a35SAlexander V. Chernikov def test_output6_linklocal_scope(self, params): 34778d11a35SAlexander V. Chernikov """Tests simple UDP output""" 34878d11a35SAlexander V. Chernikov second_vnet = self.vnet_map["vnet2"] 34978d11a35SAlexander V. Chernikov 35078d11a35SAlexander V. Chernikov src_ifp = params.get("sif") 35178d11a35SAlexander V. Chernikov src_ip = params.get("sip") 35278d11a35SAlexander V. Chernikov dst_ifp = params.get("dif") 35378d11a35SAlexander V. Chernikov dst_ip = params.get("dip") 35478d11a35SAlexander V. Chernikov errno = params.get("ex", 0) 35578d11a35SAlexander V. Chernikov 35678d11a35SAlexander V. Chernikov # Sent ifp/IP to bind on 35778d11a35SAlexander V. Chernikov second_vnet = self.vnet_map["vnet2"] 35878d11a35SAlexander V. Chernikov second_vnet.pipe.send((dst_ip, dst_ifp)) 35978d11a35SAlexander V. Chernikov 36078d11a35SAlexander V. Chernikov # Wait for the child to become ready 36178d11a35SAlexander V. Chernikov ll_data = self.wait_object(second_vnet.pipe) 36278d11a35SAlexander V. Chernikov 36378d11a35SAlexander V. Chernikov if dst_ip is None: 36478d11a35SAlexander V. Chernikov # Pick LL address on dst_ifp vnet2's end 36578d11a35SAlexander V. Chernikov dst_ip, _ = ll_data[second_vnet.iface_alias_map[dst_ifp].name][0] 36678d11a35SAlexander V. Chernikov # Get local interface scope 36778d11a35SAlexander V. Chernikov os_ifname = self.vnet.iface_alias_map[dst_ifp].name 36878d11a35SAlexander V. Chernikov scopeid = socket.if_nametoindex(os_ifname) 36978d11a35SAlexander V. Chernikov target = (dst_ip, self.DEFAULT_PORT, 0, scopeid) 37078d11a35SAlexander V. Chernikov else: 37178d11a35SAlexander V. Chernikov target = (dst_ip, self.DEFAULT_PORT, 0, 0) 37278d11a35SAlexander V. Chernikov 37378d11a35SAlexander V. Chernikov # Bind 37478d11a35SAlexander V. Chernikov if src_ip is None: 37578d11a35SAlexander V. Chernikov ll_data = ToolsHelper.get_linklocals() 37678d11a35SAlexander V. Chernikov os_ifname = self.vnet.iface_alias_map[src_ifp].name 37778d11a35SAlexander V. Chernikov src_ip, _ = ll_data[os_ifname][0] 37878d11a35SAlexander V. Chernikov scopeid = socket.if_nametoindex(os_ifname) 37978d11a35SAlexander V. Chernikov src = (src_ip, self.DEFAULT_PORT, 0, scopeid) 38078d11a35SAlexander V. Chernikov else: 38178d11a35SAlexander V. Chernikov src = (src_ip, self.DEFAULT_PORT, 0, 0) 38278d11a35SAlexander V. Chernikov 38378d11a35SAlexander V. Chernikov s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 38478d11a35SAlexander V. Chernikov s.bind(src) 38578d11a35SAlexander V. Chernikov data = bytes("AAAA", "utf-8") 38678d11a35SAlexander V. Chernikov print("## TX packet {} -> {}".format(src, target)) 38778d11a35SAlexander V. Chernikov 38878d11a35SAlexander V. Chernikov try: 38978d11a35SAlexander V. Chernikov s.sendto(data, target) 39078d11a35SAlexander V. Chernikov except OSError as e: 39178d11a35SAlexander V. Chernikov if not errno: 39278d11a35SAlexander V. Chernikov raise 39378d11a35SAlexander V. Chernikov assert e.errno == errno 39478d11a35SAlexander V. Chernikov print("Correctly raised {}".format(e)) 39578d11a35SAlexander V. Chernikov return 39678d11a35SAlexander V. Chernikov 39778d11a35SAlexander V. Chernikov # Wait for the received object 39878d11a35SAlexander V. Chernikov rx_obj = self.wait_object(second_vnet.pipe) 39978d11a35SAlexander V. Chernikov assert rx_obj["dst_ip"] == dst_ip 40078d11a35SAlexander V. Chernikov assert rx_obj["src_ip"] == src_ip 40178d11a35SAlexander V. Chernikov # assert rx_obj["dst_iface_alias"] == "if2" 40278d11a35SAlexander V. Chernikov 40378d11a35SAlexander V. Chernikov 404ce414d02SAlexander V. Chernikovclass TestIP6OutputMulticast(BaseTestIP6Ouput): 405*f63825ffSAlexander V. Chernikov def vnet2_handler(self, vnet): 406*f63825ffSAlexander V. Chernikov group = self.wait_object(vnet.pipe) 407ce414d02SAlexander V. Chernikov os_ifname = vnet.iface_alias_map["if2"].name 408*f63825ffSAlexander V. Chernikov self._vnet2_handler(vnet, group, os_ifname) 409ce414d02SAlexander V. Chernikov 410ce414d02SAlexander V. Chernikov @pytest.mark.parametrize("group_scope", ["ff02", "ff05", "ff08", "ff0e"]) 411ce414d02SAlexander V. Chernikov @pytest.mark.require_user("root") 412ce414d02SAlexander V. Chernikov def test_output6_multicast(self, group_scope): 413ce414d02SAlexander V. Chernikov """Tests simple UDP output""" 414ce414d02SAlexander V. Chernikov second_vnet = self.vnet_map["vnet2"] 415ce414d02SAlexander V. Chernikov 416ce414d02SAlexander V. Chernikov group = "{}::3456".format(group_scope) 417ce414d02SAlexander V. Chernikov second_vnet.pipe.send(group) 418ce414d02SAlexander V. Chernikov 419ce414d02SAlexander V. Chernikov # Pick target on if2 vnet2's end 420ce414d02SAlexander V. Chernikov ip = group 421ce414d02SAlexander V. Chernikov os_ifname = self.vnet.iface_alias_map["if2"].name 422ce414d02SAlexander V. Chernikov ifindex = socket.if_nametoindex(os_ifname) 423ce414d02SAlexander V. Chernikov optval = struct.pack("I", ifindex) 424ce414d02SAlexander V. Chernikov 425ce414d02SAlexander V. Chernikov s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 426ce414d02SAlexander V. Chernikov s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, optval) 427ce414d02SAlexander V. Chernikov 428ce414d02SAlexander V. Chernikov data = bytes("AAAA", "utf-8") 429ce414d02SAlexander V. Chernikov 430ce414d02SAlexander V. Chernikov # Wait for the child to become ready 431ce414d02SAlexander V. Chernikov self.wait_object(second_vnet.pipe) 432ce414d02SAlexander V. Chernikov 433ce414d02SAlexander V. Chernikov print("## TX packet to {},{}".format(ip, self.DEFAULT_PORT)) 434ce414d02SAlexander V. Chernikov s.sendto(data, (ip, self.DEFAULT_PORT)) 435ce414d02SAlexander V. Chernikov 436ce414d02SAlexander V. Chernikov # Wait for the received object 437ce414d02SAlexander V. Chernikov rx_obj = self.wait_object(second_vnet.pipe) 438ce414d02SAlexander V. Chernikov assert rx_obj["dst_ip"] == ip 439ce414d02SAlexander V. Chernikov assert rx_obj["dst_iface_alias"] == "if2" 440ce414d02SAlexander V. Chernikov 441ce414d02SAlexander V. Chernikov 442ce414d02SAlexander V. Chernikovclass TestIP6OutputLoopback(SingleVnetTestTemplate): 443ce414d02SAlexander V. Chernikov IPV6_PREFIXES = ["2001:db8:a::1/64"] 444ce414d02SAlexander V. Chernikov DEFAULT_PORT = 45365 445ce414d02SAlexander V. Chernikov 446ce414d02SAlexander V. Chernikov @pytest.mark.parametrize( 447ce414d02SAlexander V. Chernikov "source_validation", 448ce414d02SAlexander V. Chernikov [ 449ce414d02SAlexander V. Chernikov pytest.param(0, id="no_sav"), 45050fa27e7SAlexander V. Chernikov pytest.param(1, id="sav"), 451ce414d02SAlexander V. Chernikov ], 452ce414d02SAlexander V. Chernikov ) 453ce414d02SAlexander V. Chernikov @pytest.mark.parametrize("scope", ["gu", "ll", "lo"]) 454ce414d02SAlexander V. Chernikov def test_output6_self_tcp(self, scope, source_validation): 455ce414d02SAlexander V. Chernikov """Tests IPv6 TCP connection to the local IPv6 address""" 456ce414d02SAlexander V. Chernikov 457ce414d02SAlexander V. Chernikov ToolsHelper.set_sysctl( 458ce414d02SAlexander V. Chernikov "net.inet6.ip6.source_address_validation", source_validation 459ce414d02SAlexander V. Chernikov ) 460ce414d02SAlexander V. Chernikov 461ce414d02SAlexander V. Chernikov if scope == "gu": 462ce414d02SAlexander V. Chernikov ip = "2001:db8:a::1" 463ce414d02SAlexander V. Chernikov addr_tuple = (ip, self.DEFAULT_PORT) 464ce414d02SAlexander V. Chernikov elif scope == "ll": 465ce414d02SAlexander V. Chernikov os_ifname = self.vnet.iface_alias_map["if1"].name 466ce414d02SAlexander V. Chernikov ifindex = socket.if_nametoindex(os_ifname) 467ce414d02SAlexander V. Chernikov ll_data = ToolsHelper.get_linklocals() 468ce414d02SAlexander V. Chernikov ip, _ = ll_data[os_ifname][0] 469ce414d02SAlexander V. Chernikov addr_tuple = (ip, self.DEFAULT_PORT, 0, ifindex) 470ce414d02SAlexander V. Chernikov elif scope == "lo": 471ce414d02SAlexander V. Chernikov ip = "::1" 472ce414d02SAlexander V. Chernikov ToolsHelper.get_output("route add -6 ::1/128 -iface lo0") 473ce414d02SAlexander V. Chernikov ifindex = socket.if_nametoindex("lo0") 474ce414d02SAlexander V. Chernikov addr_tuple = (ip, self.DEFAULT_PORT) 475ce414d02SAlexander V. Chernikov else: 476ce414d02SAlexander V. Chernikov assert 0 == 1 477ce414d02SAlexander V. Chernikov print("address: {}".format(addr_tuple)) 478ce414d02SAlexander V. Chernikov 479ce414d02SAlexander V. Chernikov start = time.perf_counter() 480ce414d02SAlexander V. Chernikov ss = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP) 481ce414d02SAlexander V. Chernikov ss.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVPKTINFO, 1) 482ce414d02SAlexander V. Chernikov ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) 483ce414d02SAlexander V. Chernikov ss.bind(addr_tuple) 484ce414d02SAlexander V. Chernikov ss.listen() 485ce414d02SAlexander V. Chernikov s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP) 486ce414d02SAlexander V. Chernikov s.settimeout(2.0) 487ce414d02SAlexander V. Chernikov s.connect(addr_tuple) 488ce414d02SAlexander V. Chernikov conn, from_addr = ss.accept() 489ce414d02SAlexander V. Chernikov duration = time.perf_counter() - start 490ce414d02SAlexander V. Chernikov 491ce414d02SAlexander V. Chernikov assert from_addr[0] == ip 492ce414d02SAlexander V. Chernikov assert duration < 1.0 493ce414d02SAlexander V. Chernikov 494ce414d02SAlexander V. Chernikov @pytest.mark.parametrize( 495ce414d02SAlexander V. Chernikov "source_validation", 496ce414d02SAlexander V. Chernikov [ 497ce414d02SAlexander V. Chernikov pytest.param(0, id="no_sav"), 49850fa27e7SAlexander V. Chernikov pytest.param(1, id="sav"), 499ce414d02SAlexander V. Chernikov ], 500ce414d02SAlexander V. Chernikov ) 501ce414d02SAlexander V. Chernikov @pytest.mark.parametrize("scope", ["gu", "ll", "lo"]) 502ce414d02SAlexander V. Chernikov def test_output6_self_udp(self, scope, source_validation): 503ce414d02SAlexander V. Chernikov """Tests IPv6 UDP connection to the local IPv6 address""" 504ce414d02SAlexander V. Chernikov 505ce414d02SAlexander V. Chernikov ToolsHelper.set_sysctl( 506ce414d02SAlexander V. Chernikov "net.inet6.ip6.source_address_validation", source_validation 507ce414d02SAlexander V. Chernikov ) 508ce414d02SAlexander V. Chernikov 509ce414d02SAlexander V. Chernikov if scope == "gu": 510ce414d02SAlexander V. Chernikov ip = "2001:db8:a::1" 511ce414d02SAlexander V. Chernikov addr_tuple = (ip, self.DEFAULT_PORT) 512ce414d02SAlexander V. Chernikov elif scope == "ll": 513ce414d02SAlexander V. Chernikov os_ifname = self.vnet.iface_alias_map["if1"].name 514ce414d02SAlexander V. Chernikov ifindex = socket.if_nametoindex(os_ifname) 515ce414d02SAlexander V. Chernikov ll_data = ToolsHelper.get_linklocals() 516ce414d02SAlexander V. Chernikov ip, _ = ll_data[os_ifname][0] 517ce414d02SAlexander V. Chernikov addr_tuple = (ip, self.DEFAULT_PORT, 0, ifindex) 518ce414d02SAlexander V. Chernikov elif scope == "lo": 519ce414d02SAlexander V. Chernikov ip = "::1" 520ce414d02SAlexander V. Chernikov ToolsHelper.get_output("route add -6 ::1/128 -iface lo0") 521ce414d02SAlexander V. Chernikov ifindex = socket.if_nametoindex("lo0") 522ce414d02SAlexander V. Chernikov addr_tuple = (ip, self.DEFAULT_PORT) 523ce414d02SAlexander V. Chernikov else: 524ce414d02SAlexander V. Chernikov assert 0 == 1 525ce414d02SAlexander V. Chernikov print("address: {}".format(addr_tuple)) 526ce414d02SAlexander V. Chernikov 527ce414d02SAlexander V. Chernikov start = time.perf_counter() 528ce414d02SAlexander V. Chernikov ss = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP) 529ce414d02SAlexander V. Chernikov ss.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVPKTINFO, 1) 530ce414d02SAlexander V. Chernikov ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) 531ce414d02SAlexander V. Chernikov ss.bind(addr_tuple) 532ce414d02SAlexander V. Chernikov ss.listen() 533ce414d02SAlexander V. Chernikov s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP) 534ce414d02SAlexander V. Chernikov s.settimeout(2.0) 535ce414d02SAlexander V. Chernikov s.connect(addr_tuple) 536ce414d02SAlexander V. Chernikov conn, from_addr = ss.accept() 537ce414d02SAlexander V. Chernikov duration = time.perf_counter() - start 538ce414d02SAlexander V. Chernikov 539ce414d02SAlexander V. Chernikov assert from_addr[0] == ip 540ce414d02SAlexander V. Chernikov assert duration < 1.0 541