python312Packages.millheater: 0.11.8 -> 0.12.0
[NixPkgs.git] / nixos / tests / systemd-networkd-vrf.nix
bloba7875bb177faf91aa452d648744c77ea3bb5bd68
1 import ./make-test-python.nix ({ pkgs, lib, ... }: let
2   inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
4   mkNode = vlan: id: {
5     virtualisation.vlans = [ vlan ];
6     networking = {
7       useDHCP = false;
8       useNetworkd = true;
9     };
11     systemd.network = {
12       enable = true;
14       networks."10-eth${toString vlan}" = {
15         matchConfig.Name = "eth${toString vlan}";
16         linkConfig.RequiredForOnline = "no";
17         networkConfig = {
18           Address = "192.168.${toString vlan}.${toString id}/24";
19           IPv4Forwarding = "yes";
20           IPv6Forwarding = "yes";
21         };
22       };
23     };
24   };
25 in {
26   name = "systemd-networkd-vrf";
27   meta.maintainers = with lib.maintainers; [ ma27 ];
29   nodes = {
30     client = { pkgs, ... }: {
31       virtualisation.vlans = [ 1 2 ];
33       networking = {
34         useDHCP = false;
35         useNetworkd = true;
36         firewall.checkReversePath = "loose";
37       };
39       systemd.network = {
40         enable = true;
42         netdevs."10-vrf1" = {
43           netdevConfig = {
44             Kind = "vrf";
45             Name = "vrf1";
46             MTUBytes = "1300";
47           };
48           vrfConfig.Table = 23;
49         };
50         netdevs."10-vrf2" = {
51           netdevConfig = {
52             Kind = "vrf";
53             Name = "vrf2";
54             MTUBytes = "1300";
55           };
56           vrfConfig.Table = 42;
57         };
59         networks."10-vrf1" = {
60           matchConfig.Name = "vrf1";
61           networkConfig.IPv4Forwarding = "yes";
62           networkConfig.IPv6Forwarding = "yes";
63           routes = [
64             { Destination = "192.168.1.2"; Metric = 100; }
65           ];
66         };
67         networks."10-vrf2" = {
68           matchConfig.Name = "vrf2";
69           networkConfig.IPv4Forwarding = "yes";
70           networkConfig.IPv6Forwarding = "yes";
71           routes = [
72             { Destination = "192.168.2.3"; Metric = 100; }
73           ];
74         };
76         networks."10-eth1" = {
77           matchConfig.Name = "eth1";
78           linkConfig.RequiredForOnline = "no";
79           networkConfig = {
80             VRF = "vrf1";
81             Address = "192.168.1.1/24";
82             IPv4Forwarding = "yes";
83             IPv6Forwarding = "yes";
84           };
85         };
86         networks."10-eth2" = {
87           matchConfig.Name = "eth2";
88           linkConfig.RequiredForOnline = "no";
89           networkConfig = {
90             VRF = "vrf2";
91             Address = "192.168.2.1/24";
92             IPv4Forwarding = "yes";
93             IPv6Forwarding = "yes";
94           };
95         };
96       };
97     };
99     node1 = lib.mkMerge [
100       (mkNode 1 2)
101       {
102         services.openssh.enable = true;
103         users.users.root.openssh.authorizedKeys.keys = [ snakeOilPublicKey ];
104       }
105     ];
107     node2 = mkNode 2 3;
108     node3 = mkNode 2 4;
109   };
111   testScript = ''
112     import json
114     def compare(raw_json, to_compare):
115         data = json.loads(raw_json)
116         assert len(raw_json) >= len(to_compare)
117         for i, row in enumerate(to_compare):
118             actual = data[i]
119             assert len(row.keys()) > 0
120             for key, value in row.items():
121                 assert value == actual[key], f"""
122                   In entry {i}, value {key}: got: {actual[key]}, expected {value}
123                 """
126     start_all()
128     client.wait_for_unit("network.target")
129     node1.wait_for_unit("network.target")
130     node2.wait_for_unit("network.target")
131     node3.wait_for_unit("network.target")
133     # Check that networkd properly configures the main routing table
134     # and the routing tables for the VRF.
135     with subtest("check vrf routing tables"):
136         compare(
137             client.succeed("ip --json -4 route list"),
138             [
139                 {"dst": "192.168.1.2", "dev": "vrf1", "metric": 100},
140                 {"dst": "192.168.2.3", "dev": "vrf2", "metric": 100}
141             ]
142         )
143         compare(
144             client.succeed("ip --json -4 route list table 23"),
145             [
146                 {"dst": "192.168.1.0/24", "dev": "eth1", "prefsrc": "192.168.1.1"},
147                 {"type": "local", "dst": "192.168.1.1", "dev": "eth1", "prefsrc": "192.168.1.1"},
148                 {"type": "broadcast", "dev": "eth1", "prefsrc": "192.168.1.1", "dst": "192.168.1.255"}
149             ]
150         )
151         compare(
152             client.succeed("ip --json -4 route list table 42"),
153             [
154                 {"dst": "192.168.2.0/24", "dev": "eth2", "prefsrc": "192.168.2.1"},
155                 {"type": "local", "dst": "192.168.2.1", "dev": "eth2", "prefsrc": "192.168.2.1"},
156                 {"type": "broadcast", "dev": "eth2", "prefsrc": "192.168.2.1", "dst": "192.168.2.255"}
157             ]
158         )
160     # Ensure that other nodes are reachable via ICMP through the VRF.
161     with subtest("icmp through vrf works"):
162         client.succeed("ping -c5 192.168.1.2")
163         client.succeed("ping -c5 192.168.2.3")
165     # Test whether TCP through a VRF IP is possible.
166     with subtest("tcp traffic through vrf works"):
167         node1.wait_for_open_port(22)
168         client.succeed(
169             "cat ${snakeOilPrivateKey} > privkey.snakeoil"
170         )
171         client.succeed("chmod 600 privkey.snakeoil")
172         client.succeed(
173             "ulimit -l 2048; ip vrf exec vrf1 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil root@192.168.1.2 true"
174         )
176     # Only configured routes through the VRF from the main routing table should
177     # work. Additional IPs are only reachable when binding to the vrf interface.
178     with subtest("only routes from main routing table work by default"):
179         client.fail("ping -c5 192.168.2.4")
180         client.succeed("ping -I vrf2 -c5 192.168.2.4")
182     client.shutdown()
183     node1.shutdown()
184     node2.shutdown()
185     node3.shutdown()
186   '';