You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

network-interfaces-systemd.nix 9.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. { config, lib, utils, ... }:
  2. with utils;
  3. with lib;
  4. let
  5. cfg = config.networking;
  6. interfaces = attrValues cfg.interfaces;
  7. interfaceIps = i:
  8. i.ipv4.addresses
  9. ++ optionals cfg.enableIPv6 i.ipv6.addresses;
  10. dhcpStr = useDHCP: if useDHCP == true || useDHCP == null then "yes" else "no";
  11. slaves =
  12. concatLists (map (bond: bond.interfaces) (attrValues cfg.bonds))
  13. ++ concatLists (map (bridge: bridge.interfaces) (attrValues cfg.bridges))
  14. ++ map (sit: sit.dev) (attrValues cfg.sits)
  15. ++ map (vlan: vlan.interface) (attrValues cfg.vlans);
  16. in
  17. {
  18. config = mkIf cfg.useNetworkd {
  19. assertions = [ {
  20. assertion = cfg.defaultGatewayWindowSize == null;
  21. message = "networking.defaultGatewayWindowSize is not supported by networkd.";
  22. } {
  23. assertion = cfg.vswitches == {};
  24. message = "networking.vswichtes are not supported by networkd.";
  25. } {
  26. assertion = cfg.defaultGateway == null || cfg.defaultGateway.interface == null;
  27. message = "networking.defaultGateway.interface is not supported by networkd.";
  28. } {
  29. assertion = cfg.defaultGateway6 == null || cfg.defaultGateway6.interface == null;
  30. message = "networking.defaultGateway6.interface is not supported by networkd.";
  31. } {
  32. assertion = cfg.useDHCP == false;
  33. message = ''
  34. networking.useDHCP is not supported by networkd.
  35. Please use per interface configuration and set the global option to false.
  36. '';
  37. } ] ++ flip mapAttrsToList cfg.bridges (n: { rstp, ... }: {
  38. assertion = !rstp;
  39. message = "networking.bridges.${n}.rstp is not supported by networkd.";
  40. });
  41. networking.dhcpcd.enable = mkDefault false;
  42. systemd.services.network-local-commands = {
  43. after = [ "systemd-networkd.service" ];
  44. bindsTo = [ "systemd-networkd.service" ];
  45. };
  46. systemd.network =
  47. let
  48. domains = cfg.search ++ (optional (cfg.domain != null) cfg.domain);
  49. genericNetwork = override:
  50. let gateway = optional (cfg.defaultGateway != null) cfg.defaultGateway.address
  51. ++ optional (cfg.defaultGateway6 != null) cfg.defaultGateway6.address;
  52. in optionalAttrs (gateway != [ ]) {
  53. routes = override [
  54. {
  55. routeConfig = {
  56. Gateway = gateway;
  57. GatewayOnLink = false;
  58. };
  59. }
  60. ];
  61. } // optionalAttrs (domains != [ ]) {
  62. domains = override domains;
  63. };
  64. in mkMerge [ {
  65. enable = true;
  66. networks."99-main" = (genericNetwork mkDefault) // {
  67. # We keep the "broken" behaviour of applying this to all interfaces.
  68. # In general we want to get rid of this workaround but there hasn't
  69. # been any work on that.
  70. # See the following issues for details:
  71. # - https://github.com/NixOS/nixpkgs/issues/18962
  72. # - https://github.com/NixOS/nixpkgs/issues/61629
  73. matchConfig = mkDefault { Name = "*"; };
  74. };
  75. }
  76. (mkMerge (forEach interfaces (i: {
  77. netdevs = mkIf i.virtual ({
  78. "40-${i.name}" = {
  79. netdevConfig = {
  80. Name = i.name;
  81. Kind = i.virtualType;
  82. };
  83. "${i.virtualType}Config" = optionalAttrs (i.virtualOwner != null) {
  84. User = i.virtualOwner;
  85. };
  86. };
  87. });
  88. networks."40-${i.name}" = mkMerge [ (genericNetwork mkDefault) {
  89. name = mkDefault i.name;
  90. DHCP = mkForce (dhcpStr
  91. (if i.useDHCP != null then i.useDHCP else false));
  92. address = forEach (interfaceIps i)
  93. (ip: "${ip.address}/${toString ip.prefixLength}");
  94. networkConfig.IPv6PrivacyExtensions = "kernel";
  95. } ];
  96. })))
  97. (mkMerge (flip mapAttrsToList cfg.bridges (name: bridge: {
  98. netdevs."40-${name}" = {
  99. netdevConfig = {
  100. Name = name;
  101. Kind = "bridge";
  102. };
  103. };
  104. networks = listToAttrs (forEach bridge.interfaces (bi:
  105. nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) {
  106. DHCP = mkOverride 0 (dhcpStr false);
  107. networkConfig.Bridge = name;
  108. } ])));
  109. })))
  110. (mkMerge (flip mapAttrsToList cfg.bonds (name: bond: {
  111. netdevs."40-${name}" = {
  112. netdevConfig = {
  113. Name = name;
  114. Kind = "bond";
  115. };
  116. bondConfig = let
  117. # manual mapping as of 2017-02-03
  118. # man 5 systemd.netdev [BOND]
  119. # to https://www.kernel.org/doc/Documentation/networking/bonding.txt
  120. # driver options.
  121. driverOptionMapping = let
  122. trans = f: optName: { valTransform = f; optNames = [optName]; };
  123. simp = trans id;
  124. ms = trans (v: v + "ms");
  125. in {
  126. Mode = simp "mode";
  127. TransmitHashPolicy = simp "xmit_hash_policy";
  128. LACPTransmitRate = simp "lacp_rate";
  129. MIIMonitorSec = ms "miimon";
  130. UpDelaySec = ms "updelay";
  131. DownDelaySec = ms "downdelay";
  132. LearnPacketIntervalSec = simp "lp_interval";
  133. AdSelect = simp "ad_select";
  134. FailOverMACPolicy = simp "fail_over_mac";
  135. ARPValidate = simp "arp_validate";
  136. # apparently in ms for this value?! Upstream bug?
  137. ARPIntervalSec = simp "arp_interval";
  138. ARPIPTargets = simp "arp_ip_target";
  139. ARPAllTargets = simp "arp_all_targets";
  140. PrimaryReselectPolicy = simp "primary_reselect";
  141. ResendIGMP = simp "resend_igmp";
  142. PacketsPerSlave = simp "packets_per_slave";
  143. GratuitousARP = { valTransform = id;
  144. optNames = [ "num_grat_arp" "num_unsol_na" ]; };
  145. AllSlavesActive = simp "all_slaves_active";
  146. MinLinks = simp "min_links";
  147. };
  148. do = bond.driverOptions;
  149. assertNoUnknownOption = let
  150. knownOptions = flatten (mapAttrsToList (_: kOpts: kOpts.optNames)
  151. driverOptionMapping);
  152. # options that apparently don’t exist in the networkd config
  153. unknownOptions = [ "primary" ];
  154. assertTrace = bool: msg: if bool then true else builtins.trace msg false;
  155. in assert all (driverOpt: assertTrace
  156. (elem driverOpt (knownOptions ++ unknownOptions))
  157. "The bond.driverOption `${driverOpt}` cannot be mapped to the list of known networkd bond options. Please add it to the mapping above the assert or to `unknownOptions` should it not exist in networkd.")
  158. (mapAttrsToList (k: _: k) do); "";
  159. # get those driverOptions that have been set
  160. filterSystemdOptions = filterAttrs (sysDOpt: kOpts:
  161. any (kOpt: do ? ${kOpt}) kOpts.optNames);
  162. # build final set of systemd options to bond values
  163. buildOptionSet = mapAttrs (_: kOpts: with kOpts;
  164. # we simply take the first set kernel bond option
  165. # (one option has multiple names, which is silly)
  166. head (map (optN: valTransform (do.${optN}))
  167. # only map those that exist
  168. (filter (o: do ? ${o}) optNames)));
  169. in seq assertNoUnknownOption
  170. (buildOptionSet (filterSystemdOptions driverOptionMapping));
  171. };
  172. networks = listToAttrs (forEach bond.interfaces (bi:
  173. nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) {
  174. DHCP = mkOverride 0 (dhcpStr false);
  175. networkConfig.Bond = name;
  176. } ])));
  177. })))
  178. (mkMerge (flip mapAttrsToList cfg.macvlans (name: macvlan: {
  179. netdevs."40-${name}" = {
  180. netdevConfig = {
  181. Name = name;
  182. Kind = "macvlan";
  183. };
  184. macvlanConfig = optionalAttrs (macvlan.mode != null) { Mode = macvlan.mode; };
  185. };
  186. networks."40-${macvlan.interface}" = (mkMerge [ (genericNetwork (mkOverride 999)) {
  187. macvlan = [ name ];
  188. } ]);
  189. })))
  190. (mkMerge (flip mapAttrsToList cfg.sits (name: sit: {
  191. netdevs."40-${name}" = {
  192. netdevConfig = {
  193. Name = name;
  194. Kind = "sit";
  195. };
  196. tunnelConfig =
  197. (optionalAttrs (sit.remote != null) {
  198. Remote = sit.remote;
  199. }) // (optionalAttrs (sit.local != null) {
  200. Local = sit.local;
  201. }) // (optionalAttrs (sit.ttl != null) {
  202. TTL = sit.ttl;
  203. });
  204. };
  205. networks = mkIf (sit.dev != null) {
  206. "40-${sit.dev}" = (mkMerge [ (genericNetwork (mkOverride 999)) {
  207. tunnel = [ name ];
  208. } ]);
  209. };
  210. })))
  211. (mkMerge (flip mapAttrsToList cfg.vlans (name: vlan: {
  212. netdevs."40-${name}" = {
  213. netdevConfig = {
  214. Name = name;
  215. Kind = "vlan";
  216. };
  217. vlanConfig.Id = vlan.id;
  218. };
  219. networks."40-${vlan.interface}" = (mkMerge [ (genericNetwork (mkOverride 999)) {
  220. vlan = [ name ];
  221. } ]);
  222. })))
  223. ];
  224. # We need to prefill the slaved devices with networking options
  225. # This forces the network interface creator to initialize slaves.
  226. networking.interfaces = listToAttrs (map (i: nameValuePair i { }) slaves);
  227. };
  228. }