diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index 9dba6d1bd0a2..28bbc27a2693 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -210,12 +210,13 @@ let
${flip concatMapStrings (i.ipv4Routes ++ i.ipv6Routes) (route:
let
cidr = "${route.address}/${toString route.prefixLength}";
- nextHop = optionalString (route.nextHop != null) ''via "${route.nextHop}"'';
+ via = optionalString (route.via != null) ''via "${route.via}"'';
+ options = concatStrings (mapAttrsToList (name: val: "${name} ${val} ") route.options);
in
''
echo "${cidr}" >> $state
echo -n "adding route ${cidr}... "
- if out=$(ip route add "${cidr}" ${route.options} ${nextHop} dev "${i.name}" 2>&1); then
+ if out=$(ip route add "${cidr}" ${options} ${via} dev "${i.name}" 2>&1); then
echo "done"
elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
echo "failed"
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index d437a829be5f..6f8ee147649d 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -131,16 +131,16 @@ let
'';
};
- nextHop = mkOption {
+ via = mkOption {
type = types.nullOr types.str;
default = null;
description = "IPv${toString v} address of the next hop.";
};
options = mkOption {
- type = types.str;
- default = "";
- example = "mtu 1492 window 524288";
+ type = types.attrsOf types.str;
+ default = { };
+ example = { mtu = "1492"; window = "524288"; };
description = ''
Other route options. See the symbol OPTION
in the ip-route(8) manual page for the details.
@@ -237,7 +237,7 @@ let
default = [];
example = [
{ address = "10.0.0.0"; prefixLength = 16; }
- { address = "192.168.2.0"; prefixLength = 24; nextHop = "192.168.1.1"; }
+ { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; }
];
type = with types; listOf (submodule (routeOpts 4));
description = ''
@@ -249,7 +249,7 @@ let
default = [];
example = [
{ address = "fdfd:b3f0::"; prefixLength = 48; }
- { address = "2001:1470:fffd:2098::"; prefixLength = 64; nextHop = "fdfd:b3f0::1"; }
+ { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; }
];
type = with types; listOf (submodule (routeOpts 6));
description = ''
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index fa3dc0538729..e401004ab322 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -533,6 +533,69 @@ let
$client->succeed("! ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'");
'';
};
+ routes = {
+ name = "routes";
+ machine = {
+ networking.useDHCP = false;
+ networking.interfaces."eth0" = {
+ ip4 = [ { address = "192.168.1.2"; prefixLength = 24; } ];
+ ip6 = [ { address = "2001:1470:fffd:2097::"; prefixLength = 64; } ];
+ ipv6Routes = [
+ { address = "fdfd:b3f0::"; prefixLength = 48; }
+ { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; }
+ ];
+ ipv4Routes = [
+ { address = "10.0.0.0"; prefixLength = 16; options = { mtu = "1500"; }; }
+ { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; }
+ ];
+ };
+ virtualisation.vlans = [ ];
+ };
+
+ testScript = ''
+ my $targetIPv4Table = <<'END';
+ 10.0.0.0/16 scope link mtu 1500
+ 192.168.1.0/24 proto kernel scope link src 192.168.1.2
+ 192.168.2.0/24 via 192.168.1.1
+ END
+
+ my $targetIPv6Table = <<'END';
+ 2001:1470:fffd:2097::/64 proto kernel metric 256 pref medium
+ 2001:1470:fffd:2098::/64 via fdfd:b3f0::1 metric 1024 pref medium
+ fdfd:b3f0::/48 metric 1024 pref medium
+ END
+
+ $machine->start;
+ $machine->waitForUnit("network.target");
+
+ # test routing tables
+ my $ipv4Table = $machine->succeed("ip -4 route list dev eth0 | head -n3");
+ my $ipv6Table = $machine->succeed("ip -6 route list dev eth0 | head -n3");
+ "$ipv4Table" eq "$targetIPv4Table" or die(
+ "The IPv4 routing table does not match the expected one:\n",
+ "Result:\n", "$ipv4Table\n",
+ "Expected:\n", "$targetIPv4Table\n"
+ );
+ "$ipv6Table" eq "$targetIPv6Table" or die(
+ "The IPv6 routing table does not match the expected one:\n",
+ "Result:\n", "$ipv6Table\n",
+ "Expected:\n", "$targetIPv6Table\n"
+ );
+
+ # test clean-up of the tables
+ $machine->succeed("systemctl stop network-addresses-eth0");
+ my $ipv4Residue = $machine->succeed("ip -4 route list dev eth0 | head -n-3");
+ my $ipv6Residue = $machine->succeed("ip -6 route list dev eth0 | head -n-3");
+ $ipv4Residue eq "" or die(
+ "The IPv4 routing table has not been properly cleaned:\n",
+ "$ipv4Residue\n"
+ );
+ $ipv6Residue eq "" or die(
+ "The IPv6 routing table has not been properly cleaned:\n",
+ "$ipv6Residue\n"
+ );
+ '';
+ };
};
in mapAttrs (const (attrs: makeTest (attrs // {