From 045132a9b096a22cb6f84210fcd5223b9a770d62 Mon Sep 17 00:00:00 2001 From: "William A. Kennington III" Date: Thu, 20 Nov 2014 01:41:05 -0800 Subject: systemd-network: Add assertions for user clarity --- nixos/modules/system/boot/systemd-unit-options.nix | 215 ++++++++++++++++++--- 1 file changed, 192 insertions(+), 23 deletions(-) (limited to 'nixos/modules/system/boot/systemd-unit-options.nix') diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix index 4aceaad9e9f..52ad71a66c7 100644 --- a/nixos/modules/system/boot/systemd-unit-options.nix +++ b/nixos/modules/system/boot/systemd-unit-options.nix @@ -4,15 +4,184 @@ with lib; let - checkService = v: - let assertValueOneOf = name: values: attr: - let val = attr.${name}; - in optional (attr ? ${name} && !elem val values) "Systemd service field `${name}' cannot have value `${val}'."; - checkType = assertValueOneOf "Type" ["simple" "forking" "oneshot" "dbus" "notify" "idle"]; - checkRestart = assertValueOneOf "Restart" ["no" "on-success" "on-failure" "on-abort" "always"]; - errors = concatMap (c: c v) [checkType checkRestart]; - in if errors == [] then true - else builtins.trace (concatStringsSep "\n" errors) false; + boolValues = [true false "yes" "no"]; + + assertValueOneOf = name: values: group: attr: + optional (attr ? ${name} && !elem attr.${name} values) + "Systemd ${group} field `${name}' cannot have value `${attr.${name}}'."; + + assertHasField = name: group: attr: + optional (!(attr ? ${name})) + "Systemd ${group} field `${name}' must exist."; + + assertOnlyFields = fields: group: attr: + let badFields = filter (name: ! elem name fields) (attrNames attr); in + optional (badFields != [ ]) + "Systemd ${group} has extra fields [${concatStringsSep " " badFields}]."; + + assertRange = name: min: max: group: attr: + optional (attr ? ${name} && !(min <= attr.${name} && max >= attr.${name})) + "Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]"; + + digits = map toString (range 0 9); + + isByteFormat = s: + let + l = reverseList (stringToCharacters s); + suffix = head l; + nums = tail l; + in elem suffix (["K" "M" "G" "T"] ++ digits) + && all (num: elem num digits) nums; + + assertByteFormat = name: group: attr: + optional (attr ? ${name} && ! isByteFormat attr.${name}) + "Systemd ${group} field `${name}' must be in byte format [0-9]+[KMGT]."; + + hexChars = stringToCharacters "0123456789abcdefABCDEF"; + + isMacAddress = s: stringLength s == 17 + && flip all (splitString ":" s) (bytes: + all (byte: elem byte hexChars) (stringToCharacters bytes) + ); + + assertMacAddress = name: group: attr: + optional (attr ? ${name} && ! isMacAddress attr.${name}) + "Systemd ${group} field `${name}' must be a valid mac address."; + + checkUnitConfig = group: checks: v: + let errors = concatMap (c: c group v) checks; in + if errors == [] then true + else builtins.trace (concatStringsSep "\n" errors) false; + + checkService = checkUnitConfig "Service" [ + (assertValueOneOf "Type" [ + "simple" "forking" "oneshot" "dbus" "notify" "idle" + ]) + (assertValueOneOf "Restart" [ + "no" "on-success" "on-failure" "on-abort" "always" + ]) + ]; + + checkLink = checkUnitConfig "Link" [ + (assertOnlyFields [ + "Description" "Alias" "MACAddressPolicy" "MACAddress" "NamePolicy" "Name" + "MTUBytes" "BitsPerSecond" "Duplex" "WakeOnLan" + ]) + (assertValueOneOf "MACAddressPolicy" ["persistent" "random"]) + (assertMacAddress "MACAddress") + (assertValueOneOf "NamePolicy" [ + "kernel" "database" "onboard" "slot" "path" "mac" + ]) + (assertByteFormat "MTUBytes") + (assertByteFormat "BitsPerSecond") + (assertValueOneOf "Duplex" ["half" "full"]) + (assertValueOneOf "WakeOnLan" ["phy" "magic" "off"]) + ]; + + checkNetdev = checkUnitConfig "Netdev" [ + (assertOnlyFields [ + "Description" "Name" "Kind" "MTUBytes" "MACAddress" + ]) + (assertHasField "Name") + (assertHasField "Kind") + (assertValueOneOf "Kind" [ + "bridge" "bond" "vlan" "macvlan" "vxlan" "ipip" + "gre" "sit" "vti" "veth" "tun" "tap" "dummy" + ]) + (assertByteFormat "MTUBytes") + (assertMacAddress "MACAddress") + ]; + + checkVlan = checkUnitConfig "VLAN" [ + (assertOnlyFields ["Id"]) + (assertRange "Id" 0 4094) + ]; + + checkMacvlan = checkUnitConfig "MACVLAN" [ + (assertOnlyFields ["Mode"]) + (assertValueOneOf "Mode" ["private" "vepa" "bridge" "passthru"]) + ]; + + checkVxlan = checkUnitConfig "VXLAN" [ + (assertOnlyFields ["Id" "Group" "TOS" "TTL" "MacLearning"]) + (assertRange "TTL" 0 255) + (assertValueOneOf "MacLearning" boolValues) + ]; + + checkTunnel = checkUnitConfig "Tunnel" [ + (assertOnlyFields ["Local" "Remote" "TOS" "TTL" "DiscoverPathMTU"]) + (assertRange "TTL" 0 255) + (assertValueOneOf "DiscoverPathMTU" boolValues) + ]; + + checkPeer = checkUnitConfig "Peer" [ + (assertOnlyFields ["Name" "MACAddress"]) + (assertMacAddress "MACAddress") + ]; + + tunTapChecks = [ + (assertOnlyFields ["OneQueue" "MultiQueue" "PacketInfo" "User" "Group"]) + (assertValueOneOf "OneQueue" boolValues) + (assertValueOneOf "MultiQueue" boolValues) + (assertValueOneOf "PacketInfo" boolValues) + ]; + + checkTun = checkUnitConfig "Tun" tunTapChecks; + + checkTap = checkUnitConfig "Tap" tunTapChecks; + + checkBond = checkUnitConfig "Bond" [ + (assertOnlyFields [ + "Mode" "TransmitHashPolicy" "LACPTransmitRate" "MIIMonitorSec" + "UpDelaySec" "DownDelaySec" + ]) + (assertValueOneOf "Mode" [ + "balance-rr" "active-backup" "balance-xor" + "broadcast" "802.3ad" "balance-tlb" "balance-alb" + ]) + (assertValueOneOf "TransmitHashPolicy" [ + "layer2" "layer3+4" "layer2+3" "encap2+3" "802.3ad" "encap3+4" + ]) + (assertValueOneOf "LACPTransmitRate" ["slow" "fast"]) + ]; + + checkNetwork = checkUnitConfig "Network" [ + (assertOnlyFields [ + "Description" "DHCP" "DHCPServer" "IPv4LL" "IPv4LLRoute" + "LLMNR" "Domains" "Bridge" "Bond" + ]) + (assertValueOneOf "DHCP" ["both" "none" "v4" "v6"]) + (assertValueOneOf "DHCPServer" boolValues) + (assertValueOneOf "IPv4LL" boolValues) + (assertValueOneOf "IPv4LLRoute" boolValues) + (assertValueOneOf "LLMNR" boolValues) + ]; + + checkAddress = checkUnitConfig "Address" [ + (assertOnlyFields ["Address" "Peer" "Broadcast" "Label"]) + (assertHasField "Address") + ]; + + checkRoute = checkUnitConfig "Route" [ + (assertOnlyFields ["Gateway" "Destination" "Metric"]) + (assertHasField "Gateway") + ]; + + checkDhcp = checkUnitConfig "DHCP" [ + (assertOnlyFields [ + "UseDNS" "UseMTU" "SendHostname" "UseHostname" "UseDomains" "UseRoutes" + "CriticalConnections" "VendorClassIdentifier" "RequestBroadcast" + "RouteMetric" + ]) + (assertValueOneOf "UseDNS" boolValues) + (assertValueOneOf "UseMTU" boolValues) + (assertValueOneOf "SendHostname" boolValues) + (assertValueOneOf "UseHostname" boolValues) + (assertValueOneOf "UseDomains" boolValues) + (assertValueOneOf "UseRoutes" boolValues) + (assertValueOneOf "CriticalConnections" boolValues) + (assertValueOneOf "RequestBroadcast" boolValues) + ]; unitOption = mkOptionType { name = "systemd option"; @@ -482,7 +651,7 @@ in rec { linkConfig = mkOption { default = {}; example = { MACAddress = "00:ff:ee:aa:cc:dd"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkLink; description = '' Each attribute in this set specifies an option in the [Link] section of the unit. See @@ -498,7 +667,7 @@ in rec { netdevConfig = mkOption { default = {}; example = { Name = "mybridge"; Kind = "bridge"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkNetdev; description = '' Each attribute in this set specifies an option in the [Netdev] section of the unit. See @@ -510,7 +679,7 @@ in rec { vlanConfig = mkOption { default = {}; example = { Id = "4"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkVlan; description = '' Each attribute in this set specifies an option in the [VLAN] section of the unit. See @@ -522,7 +691,7 @@ in rec { macvlanConfig = mkOption { default = {}; example = { Mode = "private"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkMacvlan; description = '' Each attribute in this set specifies an option in the [MACVLAN] section of the unit. See @@ -534,7 +703,7 @@ in rec { vxlanConfig = mkOption { default = {}; example = { Id = "4"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkVxlan; description = '' Each attribute in this set specifies an option in the [VXLAN] section of the unit. See @@ -546,7 +715,7 @@ in rec { tunnelConfig = mkOption { default = {}; example = { Remote = "192.168.1.1"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkTunnel; description = '' Each attribute in this set specifies an option in the [Tunnel] section of the unit. See @@ -558,7 +727,7 @@ in rec { peerConfig = mkOption { default = {}; example = { Name = "veth2"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkPeer; description = '' Each attribute in this set specifies an option in the [Peer] section of the unit. See @@ -570,7 +739,7 @@ in rec { tunConfig = mkOption { default = {}; example = { User = "openvpn"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkTun; description = '' Each attribute in this set specifies an option in the [Tun] section of the unit. See @@ -582,7 +751,7 @@ in rec { tapConfig = mkOption { default = {}; example = { User = "openvpn"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkTap; description = '' Each attribute in this set specifies an option in the [Tap] section of the unit. See @@ -594,7 +763,7 @@ in rec { bondConfig = mkOption { default = {}; example = { Mode = "802.3ad"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkBond; description = '' Each attribute in this set specifies an option in the [Bond] section of the unit. See @@ -610,7 +779,7 @@ in rec { addressConfig = mkOption { default = {}; example = { Address = "192.168.0.100/24"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkAddress; description = '' Each attribute in this set specifies an option in the [Address] section of the unit. See @@ -626,7 +795,7 @@ in rec { routeConfig = mkOption { default = {}; example = { Gateway = "192.168.0.1"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkRoute; description = '' Each attribute in this set specifies an option in the [Route] section of the unit. See @@ -642,7 +811,7 @@ in rec { networkConfig = mkOption { default = {}; example = { Description = "My Network"; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkNetwork; description = '' Each attribute in this set specifies an option in the [Network] section of the unit. See @@ -654,7 +823,7 @@ in rec { dhcpConfig = mkOption { default = {}; example = { UseDNS = true; UseRoutes = true; }; - type = types.attrsOf unitOption; + type = types.addCheck (types.attrsOf unitOption) checkDhcp; description = '' Each attribute in this set specifies an option in the [DHCP] section of the unit. See -- cgit 1.4.1