xref: /linux/tools/testing/selftests/net/link_netns.py (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3
4import time
5
6from lib.py import ksft_run, ksft_exit, ksft_true
7from lib.py import ip
8from lib.py import NetNS, NetNSEnter
9from lib.py import RtnlFamily
10
11
12LINK_NETNSID = 100
13
14
15def test_event() -> None:
16    with NetNS() as ns1, NetNS() as ns2:
17        with NetNSEnter(str(ns2)):
18            rtnl = RtnlFamily()
19
20        rtnl.ntf_subscribe("rtnlgrp-link")
21
22        ip(f"netns set {ns2} {LINK_NETNSID}", ns=str(ns1))
23        ip(f"link add netns {ns1} link-netnsid {LINK_NETNSID} dummy1 type dummy")
24        ip(f"link add netns {ns1} dummy2 type dummy", ns=str(ns2))
25
26        ip("link del dummy1", ns=str(ns1))
27        ip("link del dummy2", ns=str(ns1))
28
29        time.sleep(1)
30        rtnl.check_ntf()
31        ksft_true(rtnl.async_msg_queue.empty(),
32                  "Received unexpected link notification")
33
34
35def validate_link_netns(netns, ifname, link_netnsid) -> bool:
36    link_info = ip(f"-d link show dev {ifname}", ns=netns, json=True)
37    if not link_info:
38        return False
39    return link_info[0].get("link_netnsid") == link_netnsid
40
41
42def test_link_net() -> None:
43    configs = [
44        # type, common args, type args, fallback to dev_net
45        ("ipvlan", "link dummy1", "", False),
46        ("macsec", "link dummy1", "", False),
47        ("macvlan", "link dummy1", "", False),
48        ("macvtap", "link dummy1", "", False),
49        ("vlan", "link dummy1", "id 100", False),
50        ("gre", "", "local 192.0.2.1", True),
51        ("vti", "", "local 192.0.2.1", True),
52        ("ipip", "", "local 192.0.2.1", True),
53        ("ip6gre", "", "local 2001:db8::1", True),
54        ("ip6tnl", "", "local 2001:db8::1", True),
55        ("vti6", "", "local 2001:db8::1", True),
56        ("sit", "", "local 192.0.2.1", True),
57        ("xfrm", "", "if_id 1", True),
58    ]
59
60    with NetNS() as ns1, NetNS() as ns2, NetNS() as ns3:
61        net1, net2, net3 = str(ns1), str(ns2), str(ns3)
62
63        # prepare link netnsid  and a dummy link needed by certain drivers
64        ip(f"netns set {net3} {LINK_NETNSID}", ns=str(net2))
65        ip("link add dummy1 type dummy", ns=net3)
66
67        cases = [
68            # source, "netns", "link-netns", expected link-netns
69            (net3, None, None, None, None),
70            (net3, net2, None, None, LINK_NETNSID),
71            (net2, None, net3, LINK_NETNSID, LINK_NETNSID),
72            (net1, net2, net3, LINK_NETNSID, LINK_NETNSID),
73        ]
74
75        for src_net, netns, link_netns, exp1, exp2 in cases:
76            tgt_net = netns or src_net
77            for typ, cargs, targs, fb_dev_net in configs:
78                cmd = "link add"
79                if netns:
80                    cmd += f" netns {netns}"
81                if link_netns:
82                    cmd += f" link-netns {link_netns}"
83                cmd += f" {cargs} foo type {typ} {targs}"
84                ip(cmd, ns=src_net)
85                if fb_dev_net:
86                    ksft_true(validate_link_netns(tgt_net, "foo", exp1),
87                              f"{typ} link_netns validation failed")
88                else:
89                    ksft_true(validate_link_netns(tgt_net, "foo", exp2),
90                              f"{typ} link_netns validation failed")
91                ip(f"link del foo", ns=tgt_net)
92
93
94def test_peer_net() -> None:
95    types = [
96        "vxcan",
97        "netkit",
98        "veth",
99    ]
100
101    with NetNS() as ns1, NetNS() as ns2, NetNS() as ns3, NetNS() as ns4:
102        net1, net2, net3, net4 = str(ns1), str(ns2), str(ns3), str(ns4)
103
104        ip(f"netns set {net3} {LINK_NETNSID}", ns=str(net2))
105
106        cases = [
107            # source, "netns", "link-netns", "peer netns", expected
108            (net1, None, None, None, None),
109            (net1, net2, None, None, None),
110            (net2, None, net3, None, LINK_NETNSID),
111            (net1, net2, net3, None, None),
112            (net2, None, None, net3, LINK_NETNSID),
113            (net1, net2, None, net3, LINK_NETNSID),
114            (net2, None, net2, net3, LINK_NETNSID),
115            (net1, net2, net4, net3, LINK_NETNSID),
116        ]
117
118        for src_net, netns, link_netns, peer_netns, exp in cases:
119            tgt_net = netns or src_net
120            for typ in types:
121                cmd = "link add"
122                if netns:
123                    cmd += f" netns {netns}"
124                if link_netns:
125                    cmd += f" link-netns {link_netns}"
126                cmd += f" foo type {typ}"
127                if peer_netns:
128                    cmd += f" peer netns {peer_netns}"
129                ip(cmd, ns=src_net)
130                ksft_true(validate_link_netns(tgt_net, "foo", exp),
131                          f"{typ} peer_netns validation failed")
132                ip(f"link del foo", ns=tgt_net)
133
134
135def main() -> None:
136    ksft_run([test_event, test_link_net, test_peer_net])
137    ksft_exit()
138
139
140if __name__ == "__main__":
141    main()
142