xref: /freebsd/tests/sys/netinet6/test_ip6_output.py (revision f63825ff21a3bee2630ea8b0ed27a4583cc4242b)
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