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