From 8b4cd01f9081b08b8ad8ca5d7b6938ae4c460cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Hanzl=C3=ADk?= Date: Mon, 22 Jul 2024 23:51:18 +0200 Subject: [PATCH] nixos/networkd: allow specifying FirewallMark mask --- nixos/lib/systemd-lib.nix | 23 +++++++++++++++++++++++ nixos/modules/system/boot/networkd.nix | 3 +-- nixos/tests/systemd-networkd.nix | 6 ++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix index f75a52a95a09..fedd85f09b80 100644 --- a/nixos/lib/systemd-lib.nix +++ b/nixos/lib/systemd-lib.nix @@ -12,6 +12,7 @@ let concatStringsSep const elem + elemAt filter filterAttrs flatten @@ -21,11 +22,14 @@ let isFloat isList isPath + isString length makeBinPath makeSearchPathOutput mapAttrs mapAttrsToList + mapNullable + match mkAfter mkIf optional @@ -101,6 +105,8 @@ in rec { optional (attr ? ${name} && ! isByteFormat attr.${name}) "Systemd ${group} field `${name}' must be in byte format [0-9]+[KMGT]."; + toIntBaseDetected = value: assert (match "[0-9]+|0x[0-9a-fA-F]+" value) != null; (builtins.fromTOML "v=${value}").v; + hexChars = stringToCharacters "0123456789abcdefABCDEF"; isMacAddress = s: stringLength s == 17 @@ -156,6 +162,23 @@ in rec { optional (attr ? ${name} && !(((isInt attr.${name} || isFloat attr.${name}) && min <= attr.${name} && max >= attr.${name}) || elem attr.${name} values)) "Systemd ${group} field `${name}' is not a value in range [${toString min},${toString max}], or one of ${toString values}"; + assertRangeWithOptionalMask = name: min: max: group: attr: + if (attr ? ${name}) then + if isInt attr.${name} then + assertRange name min max group attr + else if isString attr.${name} then + let + fields = match "([0-9]+|0x[0-9a-fA-F]+)(/([0-9]+|0x[0-9a-fA-F]+))?" attr.${name}; + in if fields == null then ["Systemd ${group} field `${name}' must either be an integer or two integers separated by a slash (/)."] + else let + value = toIntBaseDetected (elemAt fields 0); + mask = mapNullable toIntBaseDetected (elemAt fields 2); + in + optional (!(min <= value && max >= value)) "Systemd ${group} field `${name}' has main value outside the range [${toString min},${toString max}]." + ++ optional (mask != null && !(min <= mask && max >= mask)) "Systemd ${group} field `${name}' has mask outside the range [${toString min},${toString max}]." + else ["Systemd ${group} field `${name}' must either be an integer or a string."] + else []; + assertMinimum = name: min: group: attr: optional (attr ? ${name} && attr.${name} < min) "Systemd ${group} field `${name}' must be greater than or equal to ${toString min}"; diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index 2ee9c721480d..44210c8a82c6 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -778,8 +778,7 @@ let ]) (assertInt "TypeOfService") (assertRange "TypeOfService" 0 255) - (assertInt "FirewallMark") - (assertRange "FirewallMark" 1 4294967295) + (assertRangeWithOptionalMask "FirewallMark" 1 4294967295) (assertInt "Priority") (assertPortOrPortRange "SourcePort") (assertPortOrPortRange "DestinationPort") diff --git a/nixos/tests/systemd-networkd.nix b/nixos/tests/systemd-networkd.nix index 44ad713cd6df..a595fb9cba4a 100644 --- a/nixos/tests/systemd-networkd.nix +++ b/nixos/tests/systemd-networkd.nix @@ -57,6 +57,8 @@ let generateNodeConf = { lib, pkgs, config, privk, pubk, peerId, nodeId, ...}: { { Table = 30; From = "192.168.1.1"; To = "192.168.1.2"; SourcePort = 666 ; DestinationPort = 667; } { Table = 40; IPProtocol = "tcp"; InvertRule = true; } { Table = 50; IncomingInterface = "eth1"; Family = "ipv4"; } + { Table = 60; FirewallMark = 4; } + { Table = 70; FirewallMark = "16/0x1f"; } ]; }; }; @@ -119,5 +121,9 @@ testScript = '' ) # IPProtocol + InvertRule node1.succeed("sudo ip rule | grep 'not from all ipproto tcp lookup 40'") + # FirewallMark without a mask + node1.succeed("sudo ip rule | grep 'from all fwmark 0x4 lookup 60'") + # FirewallMark with a mask + node1.succeed("sudo ip rule | grep 'from all fwmark 0x10/0x1f lookup 70'") ''; })