git.iko.soy/typeable/nixpkgs
on master help sign in
nixos/networkd: add `dhcpServerStaticLeaseConfig` option

Add `systemd.network.networks.*.dhcpServerStaticLeaseConfig` to allow for configuring static DHCP leases through the `[DHCPServerStaticLease]` section. See systemd.network(5) of systemd 249 for details. Also adds the NixOS test `systemd-networkd-dhcpserver-static-lease` to test the assignment of static leases.

commitce81231420ec3499ac4e8f31076cc9425dbf90f3 authorVincent Haupert <mail@vincent-haupert.de> dateTue 30 Nov 2021, 09:57 (5 years ago) parent73bd63b
3 files changed+126
nixos/modules/system/boot/networkd.nix+44 nixos/tests/all-tests.nix+1 nixos/tests/systemd-networkd-dhcpserver-static-leases.nix+81
nixos/modules/system/boot/networkd.nix+44
@@ -821,6 +821,16 @@ let
821821 (assertValueOneOf "OnLink" boolValues)
822822 ];
823823
824+ sectionDHCPServerStaticLease = checkUnitConfig "DHCPServerStaticLease" [
825+ (assertOnlyFields [
826+ "MACAddress"
827+ "Address"
828+ ])
829+ (assertHasField "MACAddress")
830+ (assertHasField "Address")
831+ (assertMacAddress "MACAddress")
832+ ];
833+
824834 };
825835 };
826836
@@ -1161,6 +1171,25 @@ let
11611171 };
11621172 };
11631173
1174+ dhcpServerStaticLeaseOptions = {
1175+ options = {
1176+ dhcpServerStaticLeaseConfig = mkOption {
1177+ default = {};
1178+ example = { MACAddress = "65:43:4a:5b:d8:5f"; Address = "192.168.1.42"; };
1179+ type = types.addCheck (types.attrsOf unitOption) check.network.sectionDHCPServerStaticLease;
1180+ description = ''
1181+ Each attribute in this set specifies an option in the
1182+ <literal>[DHCPServerStaticLease]</literal> section of the unit. See
1183+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
1184+ <manvolnum>5</manvolnum></citerefentry> for details.
1185+
1186+ Make sure to configure the corresponding client interface to use
1187+ <literal>ClientIdentifier=mac</literal>.
1188+ '';
1189+ };
1190+ };
1191+ };
1192+
11641193 networkOptions = commonNetworkOptions // {
11651194
11661195 linkConfig = mkOption {
@@ -1273,6 +1302,17 @@ let
12731302 '';
12741303 };
12751304
1305+ dhcpServerStaticLeases = mkOption {
1306+ default = [];
1307+ example = [ { MACAddress = "65:43:4a:5b:d8:5f"; Address = "192.168.1.42"; } ];
1308+ type = with types; listOf (submodule dhcpServerStaticLeaseOptions);
1309+ description = ''
1310+ A list of DHCPServerStaticLease sections to be added to the unit. See
1311+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
1312+ <manvolnum>5</manvolnum></citerefentry> for details.
1313+ '';
1314+ };
1315+
12761316 ipv6Prefixes = mkOption {
12771317 default = [];
12781318 example = [ { AddressAutoconfiguration = true; OnLink = true; } ];
@@ -1644,6 +1684,10 @@ let
16441684 [IPv6Prefix]
16451685 ${attrsToSection x.ipv6PrefixConfig}
16461686 '')
1687+ + flip concatMapStrings def.dhcpServerStaticLeases (x: ''
1688+ [DHCPServerStaticLease]
1689+ ${attrsToSection x.dhcpServerStaticLeaseConfig}
1690+ '')
16471691 + def.extraConfig;
16481692 };
16491693
nixos/tests/all-tests.nix+1
@@ -449,6 +449,7 @@ in
449449 systemd-journal = handleTest ./systemd-journal.nix {};
450450 systemd-networkd = handleTest ./systemd-networkd.nix {};
451451 systemd-networkd-dhcpserver = handleTest ./systemd-networkd-dhcpserver.nix {};
452+ systemd-networkd-dhcpserver-static-leases = handleTest ./systemd-networkd-dhcpserver-static-leases.nix {};
452453 systemd-networkd-ipv6-prefix-delegation = handleTest ./systemd-networkd-ipv6-prefix-delegation.nix {};
453454 systemd-networkd-vrf = handleTest ./systemd-networkd-vrf.nix {};
454455 systemd-nspawn = handleTest ./systemd-nspawn.nix {};
nixos/tests/systemd-networkd-dhcpserver-static-leases.nixnew file+81
@@ -0,0 +1,81 @@
1+# In contrast to systemd-networkd-dhcpserver, this test configures
2+# the router with a static DHCP lease for the client's MAC address.
3+import ./make-test-python.nix ({ lib, ... }: {
4+ name = "systemd-networkd-dhcpserver-static-leases";
5+ meta = with lib.maintainers; {
6+ maintainers = [ veehaitch tomfitzhenry ];
7+ };
8+ nodes = {
9+ router = {
10+ virtualisation.vlans = [ 1 ];
11+ systemd.services.systemd-networkd.environment.SYSTEMD_LOG_LEVEL = "debug";
12+ networking = {
13+ useNetworkd = true;
14+ useDHCP = false;
15+ firewall.enable = false;
16+ };
17+ systemd.network = {
18+ networks = {
19+ # systemd-networkd will load the first network unit file
20+ # that matches, ordered lexiographically by filename.
21+ # /etc/systemd/network/{40-eth1,99-main}.network already
22+ # exists. This network unit must be loaded for the test,
23+ # however, hence why this network is named such.
24+ "01-eth1" = {
25+ name = "eth1";
26+ networkConfig = {
27+ DHCPServer = true;
28+ Address = "10.0.0.1/24";
29+ };
30+ dhcpServerStaticLeases = [{
31+ dhcpServerStaticLeaseConfig = {
32+ MACAddress = "02:de:ad:be:ef:01";
33+ Address = "10.0.0.10";
34+ };
35+ }];
36+ };
37+ };
38+ };
39+ };
40+
41+ client = {
42+ virtualisation.vlans = [ 1 ];
43+ systemd.services.systemd-networkd.environment.SYSTEMD_LOG_LEVEL = "debug";
44+ networking = {
45+ useNetworkd = true;
46+ useDHCP = false;
47+ firewall.enable = false;
48+ interfaces.eth1 = {
49+ useDHCP = true;
50+ macAddress = "02:de:ad:be:ef:01";
51+ };
52+ };
53+
54+ # This setting is important to have the router assign the
55+ # configured lease based on the client's MAC address. Also see:
56+ # https://github.com/systemd/systemd/issues/21368#issuecomment-982193546
57+ systemd.network.networks."40-eth1".dhcpV4Config.ClientIdentifier = "mac";
58+ };
59+ };
60+ testScript = ''
61+ start_all()
62+
63+ with subtest("check router network configuration"):
64+ router.wait_for_unit("systemd-networkd-wait-online.service")
65+ eth1_status = router.succeed("networkctl status eth1")
66+ assert "Network File: /etc/systemd/network/01-eth1.network" in eth1_status, \
67+ "The router interface eth1 is not using the expected network file"
68+ assert "10.0.0.1" in eth1_status, "Did not find expected router IPv4"
69+
70+ with subtest("check client network configuration"):
71+ client.wait_for_unit("systemd-networkd-wait-online.service")
72+ eth1_status = client.succeed("networkctl status eth1")
73+ assert "Network File: /etc/systemd/network/40-eth1.network" in eth1_status, \
74+ "The client interface eth1 is not using the expected network file"
75+ assert "10.0.0.10" in eth1_status, "Did not find expected client IPv4"
76+
77+ with subtest("router and client can reach each other"):
78+ client.wait_until_succeeds("ping -c 5 10.0.0.1")
79+ router.wait_until_succeeds("ping -c 5 10.0.0.10")
80+ '';
81+})