at 23.11-beta 28 kB view raw
1{ config, lib, pkgs, utils, ... }: 2 3with utils; 4with lib; 5 6let 7 8 cfg = config.networking; 9 interfaces = attrValues cfg.interfaces; 10 11 slaves = concatMap (i: i.interfaces) (attrValues cfg.bonds) 12 ++ concatMap (i: i.interfaces) (attrValues cfg.bridges) 13 ++ concatMap (i: attrNames (filterAttrs (_: config: config.type != "internal") i.interfaces)) (attrValues cfg.vswitches) 14 ++ concatMap (i: [i.interface]) (attrValues cfg.macvlans) 15 ++ concatMap (i: [i.interface]) (attrValues cfg.vlans); 16 17 # We must escape interfaces due to the systemd interpretation 18 subsystemDevice = interface: 19 "sys-subsystem-net-devices-${escapeSystemdPath interface}.device"; 20 21 interfaceIps = i: 22 i.ipv4.addresses 23 ++ optionals cfg.enableIPv6 i.ipv6.addresses; 24 25 destroyBond = i: '' 26 while true; do 27 UPDATED=1 28 SLAVES=$(ip link | grep 'master ${i}' | awk -F: '{print $2}') 29 for I in $SLAVES; do 30 UPDATED=0 31 ip link set dev "$I" nomaster 32 done 33 [ "$UPDATED" -eq "1" ] && break 34 done 35 ip link set dev "${i}" down 2>/dev/null || true 36 ip link del dev "${i}" 2>/dev/null || true 37 ''; 38 39 # warn that these attributes are deprecated (2017-2-2) 40 # Should be removed in the release after next 41 bondDeprecation = rec { 42 deprecated = [ "lacp_rate" "miimon" "mode" "xmit_hash_policy" ]; 43 filterDeprecated = bond: (filterAttrs (attrName: attr: 44 elem attrName deprecated && attr != null) bond); 45 }; 46 47 bondWarnings = 48 let oneBondWarnings = bondName: bond: 49 mapAttrsToList (bondText bondName) (bondDeprecation.filterDeprecated bond); 50 bondText = bondName: optName: _: 51 "${bondName}.${optName} is deprecated, use ${bondName}.driverOptions"; 52 in { 53 warnings = flatten (mapAttrsToList oneBondWarnings cfg.bonds); 54 }; 55 56 normalConfig = { 57 systemd.network.links = let 58 createNetworkLink = i: nameValuePair "40-${i.name}" { 59 matchConfig.OriginalName = i.name; 60 linkConfig = optionalAttrs (i.macAddress != null) { 61 MACAddress = i.macAddress; 62 } // optionalAttrs (i.mtu != null) { 63 MTUBytes = toString i.mtu; 64 }; 65 }; 66 in listToAttrs (map createNetworkLink interfaces); 67 systemd.services = 68 let 69 70 deviceDependency = dev: 71 # Use systemd service if we manage device creation, else 72 # trust udev when not in a container 73 if (hasAttr dev (filterAttrs (k: v: v.virtual) cfg.interfaces)) || 74 (hasAttr dev cfg.bridges) || 75 (hasAttr dev cfg.bonds) || 76 (hasAttr dev cfg.macvlans) || 77 (hasAttr dev cfg.sits) || 78 (hasAttr dev cfg.vlans) || 79 (hasAttr dev cfg.vswitches) 80 then [ "${dev}-netdev.service" ] 81 else optional (dev != null && dev != "lo" && !config.boot.isContainer) (subsystemDevice dev); 82 83 hasDefaultGatewaySet = (cfg.defaultGateway != null && cfg.defaultGateway.address != "") 84 || (cfg.enableIPv6 && cfg.defaultGateway6 != null && cfg.defaultGateway6.address != ""); 85 86 needNetworkSetup = cfg.resolvconf.enable || cfg.defaultGateway != null || cfg.defaultGateway6 != null; 87 88 networkLocalCommands = lib.mkIf needNetworkSetup { 89 after = [ "network-setup.service" ]; 90 bindsTo = [ "network-setup.service" ]; 91 }; 92 93 networkSetup = lib.mkIf needNetworkSetup 94 { description = "Networking Setup"; 95 96 after = [ "network-pre.target" "systemd-udevd.service" "systemd-sysctl.service" ]; 97 before = [ "network.target" "shutdown.target" ]; 98 wants = [ "network.target" ]; 99 # exclude bridges from the partOf relationship to fix container networking bug #47210 100 partOf = map (i: "network-addresses-${i.name}.service") (filter (i: !(hasAttr i.name cfg.bridges)) interfaces); 101 conflicts = [ "shutdown.target" ]; 102 wantedBy = [ "multi-user.target" ] ++ optional hasDefaultGatewaySet "network-online.target"; 103 104 unitConfig.ConditionCapability = "CAP_NET_ADMIN"; 105 106 path = [ pkgs.iproute2 ]; 107 108 serviceConfig = { 109 Type = "oneshot"; 110 RemainAfterExit = true; 111 }; 112 113 unitConfig.DefaultDependencies = false; 114 115 script = 116 '' 117 ${optionalString config.networking.resolvconf.enable '' 118 # Set the static DNS configuration, if given. 119 ${pkgs.openresolv}/sbin/resolvconf -m 1 -a static <<EOF 120 ${optionalString (cfg.nameservers != [] && cfg.domain != null) '' 121 domain ${cfg.domain} 122 ''} 123 ${optionalString (cfg.search != []) ("search " + concatStringsSep " " cfg.search)} 124 ${flip concatMapStrings cfg.nameservers (ns: '' 125 nameserver ${ns} 126 '')} 127 EOF 128 ''} 129 130 # Set the default gateway. 131 ${optionalString (cfg.defaultGateway != null && cfg.defaultGateway.address != "") '' 132 ${optionalString (cfg.defaultGateway.interface != null) '' 133 ip route replace ${cfg.defaultGateway.address} dev ${cfg.defaultGateway.interface} ${optionalString (cfg.defaultGateway.metric != null) 134 "metric ${toString cfg.defaultGateway.metric}" 135 } proto static 136 ''} 137 ip route replace default ${optionalString (cfg.defaultGateway.metric != null) 138 "metric ${toString cfg.defaultGateway.metric}" 139 } via "${cfg.defaultGateway.address}" ${ 140 optionalString (cfg.defaultGatewayWindowSize != null) 141 "window ${toString cfg.defaultGatewayWindowSize}"} ${ 142 optionalString (cfg.defaultGateway.interface != null) 143 "dev ${cfg.defaultGateway.interface}"} proto static 144 ''} 145 ${optionalString (cfg.defaultGateway6 != null && cfg.defaultGateway6.address != "") '' 146 ${optionalString (cfg.defaultGateway6.interface != null) '' 147 ip -6 route replace ${cfg.defaultGateway6.address} dev ${cfg.defaultGateway6.interface} ${optionalString (cfg.defaultGateway6.metric != null) 148 "metric ${toString cfg.defaultGateway6.metric}" 149 } proto static 150 ''} 151 ip -6 route replace default ${optionalString (cfg.defaultGateway6.metric != null) 152 "metric ${toString cfg.defaultGateway6.metric}" 153 } via "${cfg.defaultGateway6.address}" ${ 154 optionalString (cfg.defaultGatewayWindowSize != null) 155 "window ${toString cfg.defaultGatewayWindowSize}"} ${ 156 optionalString (cfg.defaultGateway6.interface != null) 157 "dev ${cfg.defaultGateway6.interface}"} proto static 158 ''} 159 ''; 160 }; 161 162 # For each interface <foo>, create a job ‘network-addresses-<foo>.service" 163 # that performs static address configuration. It has a "wants" 164 # dependency on ‘<foo>.service’, which is supposed to create 165 # the interface and need not exist (i.e. for hardware 166 # interfaces). It has a binds-to dependency on the actual 167 # network device, so it only gets started after the interface 168 # has appeared, and it's stopped when the interface 169 # disappears. 170 configureAddrs = i: 171 let 172 ips = interfaceIps i; 173 in 174 nameValuePair "network-addresses-${i.name}" 175 { description = "Address configuration of ${i.name}"; 176 wantedBy = [ 177 "network-setup.service" 178 "network.target" 179 ]; 180 # order before network-setup because the routes that are configured 181 # there may need ip addresses configured 182 before = [ "network-setup.service" ]; 183 bindsTo = deviceDependency i.name; 184 after = [ "network-pre.target" ] ++ (deviceDependency i.name); 185 serviceConfig.Type = "oneshot"; 186 serviceConfig.RemainAfterExit = true; 187 # Restart rather than stop+start this unit to prevent the 188 # network from dying during switch-to-configuration. 189 stopIfChanged = false; 190 path = [ pkgs.iproute2 ]; 191 script = 192 '' 193 state="/run/nixos/network/addresses/${i.name}" 194 mkdir -p $(dirname "$state") 195 196 ip link set dev "${i.name}" up 197 198 ${flip concatMapStrings ips (ip: 199 let 200 cidr = "${ip.address}/${toString ip.prefixLength}"; 201 in 202 '' 203 echo "${cidr}" >> $state 204 echo -n "adding address ${cidr}... " 205 if out=$(ip addr add "${cidr}" dev "${i.name}" 2>&1); then 206 echo "done" 207 elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then 208 echo "'ip addr add "${cidr}" dev "${i.name}"' failed: $out" 209 exit 1 210 fi 211 '' 212 )} 213 214 state="/run/nixos/network/routes/${i.name}" 215 mkdir -p $(dirname "$state") 216 217 ${flip concatMapStrings (i.ipv4.routes ++ i.ipv6.routes) (route: 218 let 219 cidr = "${route.address}/${toString route.prefixLength}"; 220 via = optionalString (route.via != null) ''via "${route.via}"''; 221 options = concatStrings (mapAttrsToList (name: val: "${name} ${val} ") route.options); 222 type = toString route.type; 223 in 224 '' 225 echo "${cidr}" >> $state 226 echo -n "adding route ${cidr}... " 227 if out=$(ip route add ${type} "${cidr}" ${options} ${via} dev "${i.name}" proto static 2>&1); then 228 echo "done" 229 elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then 230 echo "'ip route add ${type} "${cidr}" ${options} ${via} dev "${i.name}"' failed: $out" 231 exit 1 232 fi 233 '' 234 )} 235 ''; 236 preStop = '' 237 state="/run/nixos/network/routes/${i.name}" 238 if [ -e "$state" ]; then 239 while read cidr; do 240 echo -n "deleting route $cidr... " 241 ip route del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed" 242 done < "$state" 243 rm -f "$state" 244 fi 245 246 state="/run/nixos/network/addresses/${i.name}" 247 if [ -e "$state" ]; then 248 while read cidr; do 249 echo -n "deleting address $cidr... " 250 ip addr del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed" 251 done < "$state" 252 rm -f "$state" 253 fi 254 ''; 255 }; 256 257 createTunDevice = i: nameValuePair "${i.name}-netdev" 258 { description = "Virtual Network Interface ${i.name}"; 259 bindsTo = optional (!config.boot.isContainer) "dev-net-tun.device"; 260 after = optional (!config.boot.isContainer) "dev-net-tun.device" ++ [ "network-pre.target" ]; 261 wantedBy = [ "network-setup.service" (subsystemDevice i.name) ]; 262 partOf = [ "network-setup.service" ]; 263 before = [ "network-setup.service" ]; 264 path = [ pkgs.iproute2 ]; 265 serviceConfig = { 266 Type = "oneshot"; 267 RemainAfterExit = true; 268 }; 269 script = '' 270 ip tuntap add dev "${i.name}" mode "${i.virtualType}" user "${i.virtualOwner}" 271 ''; 272 postStop = '' 273 ip link del dev ${i.name} || true 274 ''; 275 }; 276 277 createBridgeDevice = n: v: nameValuePair "${n}-netdev" 278 (let 279 deps = concatLists (map deviceDependency v.interfaces); 280 in 281 { description = "Bridge Interface ${n}"; 282 wantedBy = [ "network-setup.service" (subsystemDevice n) ]; 283 bindsTo = deps ++ optional v.rstp "mstpd.service"; 284 partOf = [ "network-setup.service" ] ++ optional v.rstp "mstpd.service"; 285 after = [ "network-pre.target" ] ++ deps ++ optional v.rstp "mstpd.service" 286 ++ map (i: "network-addresses-${i}.service") v.interfaces; 287 before = [ "network-setup.service" ]; 288 serviceConfig.Type = "oneshot"; 289 serviceConfig.RemainAfterExit = true; 290 path = [ pkgs.iproute2 ]; 291 script = '' 292 # Remove Dead Interfaces 293 echo "Removing old bridge ${n}..." 294 ip link show dev "${n}" >/dev/null 2>&1 && ip link del dev "${n}" 295 296 echo "Adding bridge ${n}..." 297 ip link add name "${n}" type bridge 298 299 # Enslave child interfaces 300 ${flip concatMapStrings v.interfaces (i: '' 301 ip link set dev "${i}" master "${n}" 302 ip link set dev "${i}" up 303 '')} 304 # Save list of enslaved interfaces 305 echo "${flip concatMapStrings v.interfaces (i: '' 306 ${i} 307 '')}" > /run/${n}.interfaces 308 309 ${optionalString config.virtualisation.libvirtd.enable '' 310 # Enslave dynamically added interfaces which may be lost on nixos-rebuild 311 # 312 # if `libvirtd.service` is not running, do not use `virsh` which would try activate it via 'libvirtd.socket' and thus start it out-of-order. 313 # `libvirtd.service` will set up bridge interfaces when it will start normally. 314 # 315 if /run/current-system/systemd/bin/systemctl --quiet is-active 'libvirtd.service'; then 316 for uri in qemu:///system lxc:///; do 317 for dom in $(${pkgs.libvirt}/bin/virsh -c $uri list --name); do 318 ${pkgs.libvirt}/bin/virsh -c $uri dumpxml "$dom" | \ 319 ${pkgs.xmlstarlet}/bin/xmlstarlet sel -t -m "//domain/devices/interface[@type='bridge'][source/@bridge='${n}'][target/@dev]" -v "concat('ip link set dev ',target/@dev,' master ',source/@bridge,';')" | \ 320 ${pkgs.bash}/bin/bash 321 done 322 done 323 fi 324 ''} 325 326 # Enable stp on the interface 327 ${optionalString v.rstp '' 328 echo 2 >/sys/class/net/${n}/bridge/stp_state 329 ''} 330 331 ip link set dev "${n}" up 332 ''; 333 postStop = '' 334 ip link set dev "${n}" down || true 335 ip link del dev "${n}" || true 336 rm -f /run/${n}.interfaces 337 ''; 338 reload = '' 339 # Un-enslave child interfaces (old list of interfaces) 340 for interface in `cat /run/${n}.interfaces`; do 341 ip link set dev "$interface" nomaster up 342 done 343 344 # Enslave child interfaces (new list of interfaces) 345 ${flip concatMapStrings v.interfaces (i: '' 346 ip link set dev "${i}" master "${n}" 347 ip link set dev "${i}" up 348 '')} 349 # Save list of enslaved interfaces 350 echo "${flip concatMapStrings v.interfaces (i: '' 351 ${i} 352 '')}" > /run/${n}.interfaces 353 354 # (Un-)set stp on the bridge 355 echo ${if v.rstp then "2" else "0"} > /sys/class/net/${n}/bridge/stp_state 356 ''; 357 reloadIfChanged = true; 358 }); 359 360 createVswitchDevice = n: v: nameValuePair "${n}-netdev" 361 (let 362 deps = concatLists (map deviceDependency (attrNames (filterAttrs (_: config: config.type != "internal") v.interfaces))); 363 internalConfigs = map (i: "network-addresses-${i}.service") (attrNames (filterAttrs (_: config: config.type == "internal") v.interfaces)); 364 ofRules = pkgs.writeText "vswitch-${n}-openFlowRules" v.openFlowRules; 365 in 366 { description = "Open vSwitch Interface ${n}"; 367 wantedBy = [ "network-setup.service" (subsystemDevice n) ] ++ internalConfigs; 368 # before = [ "network-setup.service" ]; 369 # should work without internalConfigs dependencies because address/link configuration depends 370 # on the device, which is created by ovs-vswitchd with type=internal, but it does not... 371 before = [ "network-setup.service" ] ++ internalConfigs; 372 partOf = [ "network-setup.service" ]; # shutdown the bridge when network is shutdown 373 bindsTo = [ "ovs-vswitchd.service" ]; # requires ovs-vswitchd to be alive at all times 374 after = [ "network-pre.target" "ovs-vswitchd.service" ] ++ deps; # start switch after physical interfaces and vswitch daemon 375 wants = deps; # if one or more interface fails, the switch should continue to run 376 serviceConfig.Type = "oneshot"; 377 serviceConfig.RemainAfterExit = true; 378 path = [ pkgs.iproute2 config.virtualisation.vswitch.package ]; 379 preStart = '' 380 echo "Resetting Open vSwitch ${n}..." 381 ovs-vsctl --if-exists del-br ${n} -- add-br ${n} \ 382 -- set bridge ${n} protocols=${concatStringsSep "," v.supportedOpenFlowVersions} 383 ''; 384 script = '' 385 echo "Configuring Open vSwitch ${n}..." 386 ovs-vsctl ${concatStrings (mapAttrsToList (name: config: " -- add-port ${n} ${name}" + optionalString (config.vlan != null) " tag=${toString config.vlan}") v.interfaces)} \ 387 ${concatStrings (mapAttrsToList (name: config: optionalString (config.type != null) " -- set interface ${name} type=${config.type}") v.interfaces)} \ 388 ${concatMapStrings (x: " -- set-controller ${n} " + x) v.controllers} \ 389 ${concatMapStrings (x: " -- " + x) (splitString "\n" v.extraOvsctlCmds)} 390 391 392 echo "Adding OpenFlow rules for Open vSwitch ${n}..." 393 ovs-ofctl --protocols=${v.openFlowVersion} add-flows ${n} ${ofRules} 394 ''; 395 postStop = '' 396 echo "Cleaning Open vSwitch ${n}" 397 echo "Shutting down internal ${n} interface" 398 ip link set dev ${n} down || true 399 echo "Deleting flows for ${n}" 400 ovs-ofctl --protocols=${v.openFlowVersion} del-flows ${n} || true 401 echo "Deleting Open vSwitch ${n}" 402 ovs-vsctl --if-exists del-br ${n} || true 403 ''; 404 }); 405 406 createBondDevice = n: v: nameValuePair "${n}-netdev" 407 (let 408 deps = concatLists (map deviceDependency v.interfaces); 409 in 410 { description = "Bond Interface ${n}"; 411 wantedBy = [ "network-setup.service" (subsystemDevice n) ]; 412 bindsTo = deps; 413 partOf = [ "network-setup.service" ]; 414 after = [ "network-pre.target" ] ++ deps 415 ++ map (i: "network-addresses-${i}.service") v.interfaces; 416 before = [ "network-setup.service" ]; 417 serviceConfig.Type = "oneshot"; 418 serviceConfig.RemainAfterExit = true; 419 path = [ pkgs.iproute2 pkgs.gawk ]; 420 script = '' 421 echo "Destroying old bond ${n}..." 422 ${destroyBond n} 423 424 echo "Creating new bond ${n}..." 425 ip link add name "${n}" type bond \ 426 ${let opts = (mapAttrs (const toString) 427 (bondDeprecation.filterDeprecated v)) 428 // v.driverOptions; 429 in concatStringsSep "\n" 430 (mapAttrsToList (set: val: " ${set} ${val} \\") opts)} 431 432 # !!! There must be a better way to wait for the interface 433 while [ ! -d "/sys/class/net/${n}" ]; do sleep 0.1; done; 434 435 # Bring up the bond and enslave the specified interfaces 436 ip link set dev "${n}" up 437 ${flip concatMapStrings v.interfaces (i: '' 438 ip link set dev "${i}" down 439 ip link set dev "${i}" master "${n}" 440 '')} 441 ''; 442 postStop = destroyBond n; 443 }); 444 445 createMacvlanDevice = n: v: nameValuePair "${n}-netdev" 446 (let 447 deps = deviceDependency v.interface; 448 in 449 { description = "Vlan Interface ${n}"; 450 wantedBy = [ "network-setup.service" (subsystemDevice n) ]; 451 bindsTo = deps; 452 partOf = [ "network-setup.service" ]; 453 after = [ "network-pre.target" ] ++ deps; 454 before = [ "network-setup.service" ]; 455 serviceConfig.Type = "oneshot"; 456 serviceConfig.RemainAfterExit = true; 457 path = [ pkgs.iproute2 ]; 458 script = '' 459 # Remove Dead Interfaces 460 ip link show dev "${n}" >/dev/null 2>&1 && ip link delete dev "${n}" 461 ip link add link "${v.interface}" name "${n}" type macvlan \ 462 ${optionalString (v.mode != null) "mode ${v.mode}"} 463 ip link set dev "${n}" up 464 ''; 465 postStop = '' 466 ip link delete dev "${n}" || true 467 ''; 468 }); 469 470 createFouEncapsulation = n: v: nameValuePair "${n}-fou-encap" 471 (let 472 # if we have a device to bind to we can wait for its addresses to be 473 # configured, otherwise external sequencing is required. 474 deps = optionals (v.local != null && v.local.dev != null) 475 (deviceDependency v.local.dev ++ [ "network-addresses-${v.local.dev}.service" ]); 476 fouSpec = "port ${toString v.port} ${ 477 if v.protocol != null then "ipproto ${toString v.protocol}" else "gue" 478 } ${ 479 optionalString (v.local != null) "local ${escapeShellArg v.local.address} ${ 480 optionalString (v.local.dev != null) "dev ${escapeShellArg v.local.dev}" 481 }" 482 }"; 483 in 484 { description = "FOU endpoint ${n}"; 485 wantedBy = [ "network-setup.service" (subsystemDevice n) ]; 486 bindsTo = deps; 487 partOf = [ "network-setup.service" ]; 488 after = [ "network-pre.target" ] ++ deps; 489 before = [ "network-setup.service" ]; 490 serviceConfig.Type = "oneshot"; 491 serviceConfig.RemainAfterExit = true; 492 path = [ pkgs.iproute2 ]; 493 script = '' 494 # always remove previous incarnation since show can't filter 495 ip fou del ${fouSpec} >/dev/null 2>&1 || true 496 ip fou add ${fouSpec} 497 ''; 498 postStop = '' 499 ip fou del ${fouSpec} || true 500 ''; 501 }); 502 503 createSitDevice = n: v: nameValuePair "${n}-netdev" 504 (let 505 deps = deviceDependency v.dev; 506 in 507 { description = "6-to-4 Tunnel Interface ${n}"; 508 wantedBy = [ "network-setup.service" (subsystemDevice n) ]; 509 bindsTo = deps; 510 partOf = [ "network-setup.service" ]; 511 after = [ "network-pre.target" ] ++ deps; 512 before = [ "network-setup.service" ]; 513 serviceConfig.Type = "oneshot"; 514 serviceConfig.RemainAfterExit = true; 515 path = [ pkgs.iproute2 ]; 516 script = '' 517 # Remove Dead Interfaces 518 ip link show dev "${n}" >/dev/null 2>&1 && ip link delete dev "${n}" 519 ip link add name "${n}" type sit \ 520 ${optionalString (v.remote != null) "remote \"${v.remote}\""} \ 521 ${optionalString (v.local != null) "local \"${v.local}\""} \ 522 ${optionalString (v.ttl != null) "ttl ${toString v.ttl}"} \ 523 ${optionalString (v.dev != null) "dev \"${v.dev}\""} \ 524 ${optionalString (v.encapsulation != null) 525 "encap ${v.encapsulation.type} encap-dport ${toString v.encapsulation.port} ${ 526 optionalString (v.encapsulation.sourcePort != null) 527 "encap-sport ${toString v.encapsulation.sourcePort}" 528 }"} 529 ip link set dev "${n}" up 530 ''; 531 postStop = '' 532 ip link delete dev "${n}" || true 533 ''; 534 }); 535 536 createGreDevice = n: v: nameValuePair "${n}-netdev" 537 (let 538 deps = deviceDependency v.dev; 539 ttlarg = if lib.hasPrefix "ip6" v.type then "hoplimit" else "ttl"; 540 in 541 { description = "GRE Tunnel Interface ${n}"; 542 wantedBy = [ "network-setup.service" (subsystemDevice n) ]; 543 bindsTo = deps; 544 partOf = [ "network-setup.service" ]; 545 after = [ "network-pre.target" ] ++ deps; 546 before = [ "network-setup.service" ]; 547 serviceConfig.Type = "oneshot"; 548 serviceConfig.RemainAfterExit = true; 549 path = [ pkgs.iproute2 ]; 550 script = '' 551 # Remove Dead Interfaces 552 ip link show dev "${n}" >/dev/null 2>&1 && ip link delete dev "${n}" 553 ip link add name "${n}" type ${v.type} \ 554 ${optionalString (v.remote != null) "remote \"${v.remote}\""} \ 555 ${optionalString (v.local != null) "local \"${v.local}\""} \ 556 ${optionalString (v.ttl != null) "${ttlarg} ${toString v.ttl}"} \ 557 ${optionalString (v.dev != null) "dev \"${v.dev}\""} 558 ip link set dev "${n}" up 559 ''; 560 postStop = '' 561 ip link delete dev "${n}" || true 562 ''; 563 }); 564 565 createVlanDevice = n: v: nameValuePair "${n}-netdev" 566 (let 567 deps = deviceDependency v.interface; 568 in 569 { description = "Vlan Interface ${n}"; 570 wantedBy = [ "network-setup.service" (subsystemDevice n) ]; 571 bindsTo = deps; 572 partOf = [ "network-setup.service" ]; 573 after = [ "network-pre.target" ] ++ deps; 574 before = [ "network-setup.service" ]; 575 serviceConfig.Type = "oneshot"; 576 serviceConfig.RemainAfterExit = true; 577 path = [ pkgs.iproute2 ]; 578 script = '' 579 # Remove Dead Interfaces 580 ip link show dev "${n}" >/dev/null 2>&1 && ip link delete dev "${n}" 581 ip link add link "${v.interface}" name "${n}" type vlan id "${toString v.id}" 582 583 # We try to bring up the logical VLAN interface. If the master 584 # interface the logical interface is dependent upon is not up yet we will 585 # fail to immediately bring up the logical interface. The resulting logical 586 # interface will brought up later when the master interface is up. 587 ip link set dev "${n}" up || true 588 ''; 589 postStop = '' 590 ip link delete dev "${n}" || true 591 ''; 592 }); 593 594 in listToAttrs ( 595 map configureAddrs interfaces ++ 596 map createTunDevice (filter (i: i.virtual) interfaces)) 597 // mapAttrs' createBridgeDevice cfg.bridges 598 // mapAttrs' createVswitchDevice cfg.vswitches 599 // mapAttrs' createBondDevice cfg.bonds 600 // mapAttrs' createMacvlanDevice cfg.macvlans 601 // mapAttrs' createFouEncapsulation cfg.fooOverUDP 602 // mapAttrs' createSitDevice cfg.sits 603 // mapAttrs' createGreDevice cfg.greTunnels 604 // mapAttrs' createVlanDevice cfg.vlans 605 // { 606 network-setup = networkSetup; 607 network-local-commands = networkLocalCommands; 608 }; 609 610 services.udev.extraRules = 611 '' 612 KERNEL=="tun", TAG+="systemd" 613 ''; 614 615 616 }; 617 618in 619 620{ 621 config = mkMerge [ 622 bondWarnings 623 (mkIf (!cfg.useNetworkd) normalConfig) 624 { # Ensure slave interfaces are brought up 625 networking.interfaces = genAttrs slaves (i: {}); 626 } 627 ]; 628}