nixos/networkd: configure /etc/systemd/networkd.conf

The networkd.conf file controls a variety of interesting settings
which don't seem to be configurable at the moment, including
adding names to route tables (for networkd only, although this commit
also exports them into iproute2 for convenience's sake), and
the speed metering functionality built into networkd.

Importantly, however, this also allows disabling the systemd
functionality where it likes to delete all the routes and routing rules
that haven't been configured through networkd whenever something causes
it to perform a reconfiguration.
This commit is contained in:
Luke Granger-Brown 2022-03-13 04:30:49 +00:00
parent 12769bc7e1
commit f47caf769b
2 changed files with 145 additions and 1 deletions

View File

@ -10,6 +10,36 @@ let
check = { check = {
global = {
sectionNetwork = checkUnitConfig "Network" [
(assertOnlyFields [
"SpeedMeter"
"SpeedMeterIntervalSec"
"ManageForeignRoutingPolicyRules"
"ManageForeignRoutes"
"RouteTable"
])
(assertValueOneOf "SpeedMeter" boolValues)
(assertInt "SpeedMeterIntervalSec")
(assertValueOneOf "ManageForeignRoutingPolicyRules" boolValues)
(assertValueOneOf "ManageForeignRoutes" boolValues)
];
sectionDHCPv4 = checkUnitConfig "DHCPv4" [
(assertOnlyFields [
"DUIDType"
"DUIDRawData"
])
];
sectionDHCPv6 = checkUnitConfig "DHCPv6" [
(assertOnlyFields [
"DUIDType"
"DUIDRawData"
])
];
};
link = { link = {
sectionLink = checkUnitConfig "Link" [ sectionLink = checkUnitConfig "Link" [
@ -867,6 +897,44 @@ let
}; };
}; };
networkdOptions = {
networkConfig = mkOption {
default = {};
example = { SpeedMeter = true; ManageForeignRoutingPolicyRules = false; };
type = types.addCheck (types.attrsOf unitOption) check.global.sectionNetwork;
description = ''
Each attribute in this set specifies an option in the
<literal>[Network]</literal> section of the networkd config.
See <citerefentry><refentrytitle>networkd.conf</refentrytitle>
<manvolnum>5</manvolnum></citerefentry> for details.
'';
};
dhcpV4Config = mkOption {
default = {};
example = { DUIDType = "vendor"; };
type = types.addCheck (types.attrsOf unitOption) check.global.sectionDHCPv4;
description = ''
Each attribute in this set specifies an option in the
<literal>[DHCPv4]</literal> section of the networkd config.
See <citerefentry><refentrytitle>networkd.conf</refentrytitle>
<manvolnum>5</manvolnum></citerefentry> for details.
'';
};
dhcpV6Config = mkOption {
default = {};
example = { DUIDType = "vendor"; };
type = types.addCheck (types.attrsOf unitOption) check.global.sectionDHCPv6;
description = ''
Each attribute in this set specifies an option in the
<literal>[DHCPv6]</literal> section of the networkd config.
See <citerefentry><refentrytitle>networkd.conf</refentrytitle>
<manvolnum>5</manvolnum></citerefentry> for details.
'';
};
};
linkOptions = commonNetworkOptions // { linkOptions = commonNetworkOptions // {
# overwrite enable option from above # overwrite enable option from above
enable = mkOption { enable = mkOption {
@ -1515,6 +1583,39 @@ let
}; };
}; };
networkdConfig = { config, ... }: {
options = {
routeTables = mkOption {
default = {};
example = { foo = 27; };
type = with types; attrsOf int;
description = ''
Defines route table names as an attrset of name to number.
See <citerefentry><refentrytitle>networkd.conf</refentrytitle>
<manvolnum>5</manvolnum></citerefentry> for details.
'';
};
addRouteTablesToIPRoute2 = mkOption {
default = true;
example = false;
type = types.bool;
description = ''
If true and routeTables are set, then the specified route tables
will also be installed into /etc/iproute2/rt_tables.
'';
};
};
config = {
networkConfig = optionalAttrs (config.routeTables != { }) {
RouteTable = mapAttrsToList
(name: number: "${name}:${toString number}")
config.routeTables;
};
};
};
commonMatchText = def: optionalString (def.matchConfig != { }) '' commonMatchText = def: optionalString (def.matchConfig != { }) ''
[Match] [Match]
${attrsToSection def.matchConfig} ${attrsToSection def.matchConfig}
@ -1596,6 +1697,20 @@ let
+ def.extraConfig; + def.extraConfig;
}; };
renderConfig = def:
{ text = ''
[Network]
${attrsToSection def.networkConfig}
''
+ optionalString (def.dhcpV4Config != { }) ''
[DHCPv4]
${attrsToSection def.dhcpV4Config}
''
+ optionalString (def.dhcpV6Config != { }) ''
[DHCPv6]
${attrsToSection def.dhcpV6Config}
''; };
networkToUnit = name: def: networkToUnit = name: def:
{ inherit (def) enable; { inherit (def) enable;
text = commonMatchText def text = commonMatchText def
@ -1728,6 +1843,12 @@ in
description = "Definition of systemd networks."; description = "Definition of systemd networks.";
}; };
systemd.network.config = mkOption {
default = {};
type = with types; submodule [ { options = networkdOptions; } networkdConfig ];
description = "Definition of global systemd network config.";
};
systemd.network.units = mkOption { systemd.network.units = mkOption {
description = "Definition of networkd units."; description = "Definition of networkd units.";
default = {}; default = {};
@ -1772,7 +1893,9 @@ in
systemd.services.systemd-networkd = { systemd.services.systemd-networkd = {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
aliases = [ "dbus-org.freedesktop.network1.service" ]; aliases = [ "dbus-org.freedesktop.network1.service" ];
restartTriggers = map (x: x.source) (attrValues unitFiles); restartTriggers = map (x: x.source) (attrValues unitFiles) ++ [
config.environment.etc."systemd/networkd.conf".source
];
}; };
systemd.services.systemd-networkd-wait-online = { systemd.services.systemd-networkd-wait-online = {
@ -1791,6 +1914,17 @@ in
}; };
}; };
environment.etc."systemd/networkd.conf" = renderConfig cfg.config;
networking.iproute2 = mkIf (cfg.config.addRouteTablesToIPRoute2 && cfg.config.routeTables != { }) {
enable = mkDefault true;
rttablesExtraConfig = ''
# Extra tables defined in NixOS systemd.networkd.config.routeTables.
${concatStringsSep "\n" (mapAttrsToList (name: number: "${toString number} ${name}") cfg.config.routeTables)}
'';
};
services.resolved.enable = mkDefault true; services.resolved.enable = mkDefault true;
}) })
]; ];

View File

@ -8,6 +8,9 @@ let generateNodeConf = { lib, pkgs, config, privk, pubk, peerId, nodeId, ...}: {
environment.systemPackages = with pkgs; [ wireguard-tools ]; environment.systemPackages = with pkgs; [ wireguard-tools ];
systemd.network = { systemd.network = {
enable = true; enable = true;
config = {
routeTables.custom = 23;
};
netdevs = { netdevs = {
"90-wg0" = { "90-wg0" = {
netdevConfig = { Kind = "wireguard"; Name = "wg0"; }; netdevConfig = { Kind = "wireguard"; Name = "wg0"; };
@ -39,6 +42,7 @@ let generateNodeConf = { lib, pkgs, config, privk, pubk, peerId, nodeId, ...}: {
address = [ "10.0.0.${nodeId}/32" ]; address = [ "10.0.0.${nodeId}/32" ];
routes = [ routes = [
{ routeConfig = { Gateway = "10.0.0.${nodeId}"; Destination = "10.0.0.0/24"; }; } { routeConfig = { Gateway = "10.0.0.${nodeId}"; Destination = "10.0.0.0/24"; }; }
{ routeConfig = { Gateway = "10.0.0.${nodeId}"; Destination = "10.0.0.0/24"; Table = "custom"; }; }
]; ];
}; };
"30-eth1" = { "30-eth1" = {
@ -87,6 +91,12 @@ testScript = ''
node1.wait_for_unit("systemd-networkd-wait-online.service") node1.wait_for_unit("systemd-networkd-wait-online.service")
node2.wait_for_unit("systemd-networkd-wait-online.service") node2.wait_for_unit("systemd-networkd-wait-online.service")
# ================================
# Networkd Config
# ================================
node1.succeed("grep RouteTable=custom:23 /etc/systemd/networkd.conf")
node1.succeed("sudo ip route show table custom | grep '10.0.0.0/24 via 10.0.0.1 dev wg0 proto static'")
# ================================ # ================================
# Wireguard # Wireguard
# ================================ # ================================