at 22.05-pre 51 kB view raw
1{ config, options, lib, pkgs, utils, ... }: 2 3with lib; 4with utils; 5 6let 7 8 cfg = config.networking; 9 interfaces = attrValues cfg.interfaces; 10 hasVirtuals = any (i: i.virtual) interfaces; 11 hasSits = cfg.sits != { }; 12 hasBonds = cfg.bonds != { }; 13 hasFous = cfg.fooOverUDP != { } 14 || filterAttrs (_: s: s.encapsulation != null) cfg.sits != { }; 15 16 slaves = concatMap (i: i.interfaces) (attrValues cfg.bonds) 17 ++ concatMap (i: i.interfaces) (attrValues cfg.bridges) 18 ++ concatMap (i: attrNames (filterAttrs (name: config: ! (config.type == "internal" || hasAttr name cfg.interfaces)) i.interfaces)) (attrValues cfg.vswitches); 19 20 slaveIfs = map (i: cfg.interfaces.${i}) (filter (i: cfg.interfaces ? ${i}) slaves); 21 22 rstpBridges = flip filterAttrs cfg.bridges (_: { rstp, ... }: rstp); 23 24 needsMstpd = rstpBridges != { }; 25 26 bridgeStp = optional needsMstpd (pkgs.writeTextFile { 27 name = "bridge-stp"; 28 executable = true; 29 destination = "/bin/bridge-stp"; 30 text = '' 31 #!${pkgs.runtimeShell} -e 32 export PATH="${pkgs.mstpd}/bin" 33 34 BRIDGES=(${concatStringsSep " " (attrNames rstpBridges)}) 35 for BRIDGE in $BRIDGES; do 36 if [ "$BRIDGE" = "$1" ]; then 37 if [ "$2" = "start" ]; then 38 mstpctl addbridge "$BRIDGE" 39 exit 0 40 elif [ "$2" = "stop" ]; then 41 mstpctl delbridge "$BRIDGE" 42 exit 0 43 fi 44 exit 1 45 fi 46 done 47 exit 1 48 ''; 49 }); 50 51 # We must escape interfaces due to the systemd interpretation 52 subsystemDevice = interface: 53 "sys-subsystem-net-devices-${escapeSystemdPath interface}.device"; 54 55 addrOpts = v: 56 assert v == 4 || v == 6; 57 { options = { 58 address = mkOption { 59 type = types.str; 60 description = '' 61 IPv${toString v} address of the interface. Leave empty to configure the 62 interface using DHCP. 63 ''; 64 }; 65 66 prefixLength = mkOption { 67 type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128)); 68 description = '' 69 Subnet mask of the interface, specified as the number of 70 bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>). 71 ''; 72 }; 73 }; 74 }; 75 76 routeOpts = v: 77 { options = { 78 address = mkOption { 79 type = types.str; 80 description = "IPv${toString v} address of the network."; 81 }; 82 83 prefixLength = mkOption { 84 type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128)); 85 description = '' 86 Subnet mask of the network, specified as the number of 87 bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>). 88 ''; 89 }; 90 91 via = mkOption { 92 type = types.nullOr types.str; 93 default = null; 94 description = "IPv${toString v} address of the next hop."; 95 }; 96 97 options = mkOption { 98 type = types.attrsOf types.str; 99 default = { }; 100 example = { mtu = "1492"; window = "524288"; }; 101 description = '' 102 Other route options. See the symbol <literal>OPTIONS</literal> 103 in the <literal>ip-route(8)</literal> manual page for the details. 104 ''; 105 }; 106 107 }; 108 }; 109 110 gatewayCoerce = address: { inherit address; }; 111 112 gatewayOpts = { ... }: { 113 114 options = { 115 116 address = mkOption { 117 type = types.str; 118 description = "The default gateway address."; 119 }; 120 121 interface = mkOption { 122 type = types.nullOr types.str; 123 default = null; 124 example = "enp0s3"; 125 description = "The default gateway interface."; 126 }; 127 128 metric = mkOption { 129 type = types.nullOr types.int; 130 default = null; 131 example = 42; 132 description = "The default gateway metric/preference."; 133 }; 134 135 }; 136 137 }; 138 139 interfaceOpts = { name, ... }: { 140 141 options = { 142 name = mkOption { 143 example = "eth0"; 144 type = types.str; 145 description = "Name of the interface."; 146 }; 147 148 tempAddress = mkOption { 149 type = types.enum (lib.attrNames tempaddrValues); 150 default = cfg.tempAddresses; 151 defaultText = literalExpression ''config.networking.tempAddresses''; 152 description = '' 153 When IPv6 is enabled with SLAAC, this option controls the use of 154 temporary address (aka privacy extensions) on this 155 interface. This is used to reduce tracking. 156 157 See also the global option 158 <xref linkend="opt-networking.tempAddresses"/>, which 159 applies to all interfaces where this is not set. 160 161 Possible values are: 162 ${tempaddrDoc} 163 ''; 164 }; 165 166 useDHCP = mkOption { 167 type = types.nullOr types.bool; 168 default = null; 169 description = '' 170 Whether this interface should be configured with dhcp. 171 Null implies the old behavior which depends on whether ip addresses 172 are specified or not. 173 ''; 174 }; 175 176 ipv4.addresses = mkOption { 177 default = [ ]; 178 example = [ 179 { address = "10.0.0.1"; prefixLength = 16; } 180 { address = "192.168.1.1"; prefixLength = 24; } 181 ]; 182 type = with types; listOf (submodule (addrOpts 4)); 183 description = '' 184 List of IPv4 addresses that will be statically assigned to the interface. 185 ''; 186 }; 187 188 ipv6.addresses = mkOption { 189 default = [ ]; 190 example = [ 191 { address = "fdfd:b3f0:482::1"; prefixLength = 48; } 192 { address = "2001:1470:fffd:2098::e006"; prefixLength = 64; } 193 ]; 194 type = with types; listOf (submodule (addrOpts 6)); 195 description = '' 196 List of IPv6 addresses that will be statically assigned to the interface. 197 ''; 198 }; 199 200 ipv4.routes = mkOption { 201 default = []; 202 example = [ 203 { address = "10.0.0.0"; prefixLength = 16; } 204 { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; } 205 ]; 206 type = with types; listOf (submodule (routeOpts 4)); 207 description = '' 208 List of extra IPv4 static routes that will be assigned to the interface. 209 ''; 210 }; 211 212 ipv6.routes = mkOption { 213 default = []; 214 example = [ 215 { address = "fdfd:b3f0::"; prefixLength = 48; } 216 { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; } 217 ]; 218 type = with types; listOf (submodule (routeOpts 6)); 219 description = '' 220 List of extra IPv6 static routes that will be assigned to the interface. 221 ''; 222 }; 223 224 macAddress = mkOption { 225 default = null; 226 example = "00:11:22:33:44:55"; 227 type = types.nullOr (types.str); 228 description = '' 229 MAC address of the interface. Leave empty to use the default. 230 ''; 231 }; 232 233 mtu = mkOption { 234 default = null; 235 example = 9000; 236 type = types.nullOr types.int; 237 description = '' 238 MTU size for packets leaving the interface. Leave empty to use the default. 239 ''; 240 }; 241 242 virtual = mkOption { 243 default = false; 244 type = types.bool; 245 description = '' 246 Whether this interface is virtual and should be created by tunctl. 247 This is mainly useful for creating bridges between a host and a virtual 248 network such as VPN or a virtual machine. 249 ''; 250 }; 251 252 virtualOwner = mkOption { 253 default = "root"; 254 type = types.str; 255 description = '' 256 In case of a virtual device, the user who owns it. 257 ''; 258 }; 259 260 virtualType = mkOption { 261 default = if hasPrefix "tun" name then "tun" else "tap"; 262 defaultText = literalExpression ''if hasPrefix "tun" name then "tun" else "tap"''; 263 type = with types; enum [ "tun" "tap" ]; 264 description = '' 265 The type of interface to create. 266 The default is TUN for an interface name starting 267 with "tun", otherwise TAP. 268 ''; 269 }; 270 271 proxyARP = mkOption { 272 default = false; 273 type = types.bool; 274 description = '' 275 Turn on proxy_arp for this device. 276 This is mainly useful for creating pseudo-bridges between a real 277 interface and a virtual network such as VPN or a virtual machine for 278 interfaces that don't support real bridging (most wlan interfaces). 279 As ARP proxying acts slightly above the link-layer, below-ip traffic 280 isn't bridged, so things like DHCP won't work. The advantage above 281 using NAT lies in the fact that no IP addresses are shared, so all 282 hosts are reachable/routeable. 283 284 WARNING: turns on ip-routing, so if you have multiple interfaces, you 285 should think of the consequence and setup firewall rules to limit this. 286 ''; 287 }; 288 289 wakeOnLan = { 290 enable = mkOption { 291 type = types.bool; 292 default = false; 293 description = "Wether to enable wol on this interface."; 294 }; 295 }; 296 }; 297 298 config = { 299 name = mkDefault name; 300 }; 301 302 # Renamed or removed options 303 imports = 304 let 305 defined = x: x != "_mkMergedOptionModule"; 306 in [ 307 (mkChangedOptionModule [ "preferTempAddress" ] [ "tempAddress" ] 308 (config: 309 let bool = getAttrFromPath [ "preferTempAddress" ] config; 310 in if bool then "default" else "enabled" 311 )) 312 (mkRenamedOptionModule [ "ip4" ] [ "ipv4" "addresses"]) 313 (mkRenamedOptionModule [ "ip6" ] [ "ipv6" "addresses"]) 314 (mkRemovedOptionModule [ "subnetMask" ] '' 315 Supply a prefix length instead; use option 316 networking.interfaces.<name>.ipv{4,6}.addresses'') 317 (mkMergedOptionModule 318 [ [ "ipAddress" ] [ "prefixLength" ] ] 319 [ "ipv4" "addresses" ] 320 (cfg: with cfg; 321 optional (defined ipAddress && defined prefixLength) 322 { address = ipAddress; prefixLength = prefixLength; })) 323 (mkMergedOptionModule 324 [ [ "ipv6Address" ] [ "ipv6PrefixLength" ] ] 325 [ "ipv6" "addresses" ] 326 (cfg: with cfg; 327 optional (defined ipv6Address && defined ipv6PrefixLength) 328 { address = ipv6Address; prefixLength = ipv6PrefixLength; })) 329 330 ({ options.warnings = options.warnings; options.assertions = options.assertions; }) 331 ]; 332 333 }; 334 335 vswitchInterfaceOpts = {name, ...}: { 336 337 options = { 338 339 name = mkOption { 340 description = "Name of the interface"; 341 example = "eth0"; 342 type = types.str; 343 }; 344 345 vlan = mkOption { 346 description = "Vlan tag to apply to interface"; 347 example = 10; 348 type = types.nullOr types.int; 349 default = null; 350 }; 351 352 type = mkOption { 353 description = "Openvswitch type to assign to interface"; 354 example = "internal"; 355 type = types.nullOr types.str; 356 default = null; 357 }; 358 }; 359 }; 360 361 hexChars = stringToCharacters "0123456789abcdef"; 362 363 isHexString = s: all (c: elem c hexChars) (stringToCharacters (toLower s)); 364 365 tempaddrValues = { 366 disabled = { 367 sysctl = "0"; 368 description = "completely disable IPv6 temporary addresses"; 369 }; 370 enabled = { 371 sysctl = "1"; 372 description = "generate IPv6 temporary addresses but still use EUI-64 addresses as source addresses"; 373 }; 374 default = { 375 sysctl = "2"; 376 description = "generate IPv6 temporary addresses and use these as source addresses in routing"; 377 }; 378 }; 379 tempaddrDoc = '' 380 <itemizedlist> 381 ${concatStringsSep "\n" (mapAttrsToList (name: { description, ... }: '' 382 <listitem> 383 <para> 384 <literal>"${name}"</literal> to ${description}; 385 </para> 386 </listitem> 387 '') tempaddrValues)} 388 </itemizedlist> 389 ''; 390 391in 392 393{ 394 395 ###### interface 396 397 options = { 398 399 networking.hostName = mkOption { 400 default = "nixos"; 401 # Only allow hostnames without the domain name part (i.e. no FQDNs, see 402 # e.g. "man 5 hostname") and require valid DNS labels (recommended 403 # syntax). Note: We also allow underscores for compatibility/legacy 404 # reasons (as undocumented feature): 405 type = types.strMatching 406 "^$|^[[:alnum:]]([[:alnum:]_-]{0,61}[[:alnum:]])?$"; 407 description = '' 408 The name of the machine. Leave it empty if you want to obtain it from a 409 DHCP server (if using DHCP). The hostname must be a valid DNS label (see 410 RFC 1035 section 2.3.1: "Preferred name syntax", RFC 1123 section 2.1: 411 "Host Names and Numbers") and as such must not contain the domain part. 412 This means that the hostname must start with a letter or digit, 413 end with a letter or digit, and have as interior characters only 414 letters, digits, and hyphen. The maximum length is 63 characters. 415 Additionally it is recommended to only use lower-case characters. 416 If (e.g. for legacy reasons) a FQDN is required as the Linux kernel 417 network node hostname (uname --nodename) the option 418 boot.kernel.sysctl."kernel.hostname" can be used as a workaround (but 419 the 64 character limit still applies). 420 421 WARNING: Do not use underscores (_) or you may run into unexpected issues. 422 ''; 423 # warning until the issues in https://github.com/NixOS/nixpkgs/pull/138978 424 # are resolved 425 }; 426 427 networking.fqdn = mkOption { 428 readOnly = true; 429 type = types.str; 430 default = if (cfg.hostName != "" && cfg.domain != null) 431 then "${cfg.hostName}.${cfg.domain}" 432 else throw '' 433 The FQDN is required but cannot be determined. Please make sure that 434 both networking.hostName and networking.domain are set properly. 435 ''; 436 defaultText = literalExpression ''"''${networking.hostName}.''${networking.domain}"''; 437 description = '' 438 The fully qualified domain name (FQDN) of this host. It is the result 439 of combining networking.hostName and networking.domain. Using this 440 option will result in an evaluation error if the hostname is empty or 441 no domain is specified. 442 ''; 443 }; 444 445 networking.hostId = mkOption { 446 default = null; 447 example = "4e98920d"; 448 type = types.nullOr types.str; 449 description = '' 450 The 32-bit host ID of the machine, formatted as 8 hexadecimal characters. 451 452 You should try to make this ID unique among your machines. You can 453 generate a random 32-bit ID using the following commands: 454 455 <literal>head -c 8 /etc/machine-id</literal> 456 457 (this derives it from the machine-id that systemd generates) or 458 459 <literal>head -c4 /dev/urandom | od -A none -t x4</literal> 460 461 The primary use case is to ensure when using ZFS that a pool isn't imported 462 accidentally on a wrong machine. 463 ''; 464 }; 465 466 networking.enableIPv6 = mkOption { 467 default = true; 468 type = types.bool; 469 description = '' 470 Whether to enable support for IPv6. 471 ''; 472 }; 473 474 networking.defaultGateway = mkOption { 475 default = null; 476 example = { 477 address = "131.211.84.1"; 478 interface = "enp3s0"; 479 }; 480 type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts)); 481 description = '' 482 The default gateway. It can be left empty if it is auto-detected through DHCP. 483 It can be specified as a string or an option set along with a network interface. 484 ''; 485 }; 486 487 networking.defaultGateway6 = mkOption { 488 default = null; 489 example = { 490 address = "2001:4d0:1e04:895::1"; 491 interface = "enp3s0"; 492 }; 493 type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts)); 494 description = '' 495 The default ipv6 gateway. It can be left empty if it is auto-detected through DHCP. 496 It can be specified as a string or an option set along with a network interface. 497 ''; 498 }; 499 500 networking.defaultGatewayWindowSize = mkOption { 501 default = null; 502 example = 524288; 503 type = types.nullOr types.int; 504 description = '' 505 The window size of the default gateway. It limits maximal data bursts that TCP peers 506 are allowed to send to us. 507 ''; 508 }; 509 510 networking.nameservers = mkOption { 511 type = types.listOf types.str; 512 default = []; 513 example = ["130.161.158.4" "130.161.33.17"]; 514 description = '' 515 The list of nameservers. It can be left empty if it is auto-detected through DHCP. 516 ''; 517 }; 518 519 networking.search = mkOption { 520 default = []; 521 example = [ "example.com" "home.arpa" ]; 522 type = types.listOf types.str; 523 description = '' 524 The list of search paths used when resolving domain names. 525 ''; 526 }; 527 528 networking.domain = mkOption { 529 default = null; 530 example = "home.arpa"; 531 type = types.nullOr types.str; 532 description = '' 533 The domain. It can be left empty if it is auto-detected through DHCP. 534 ''; 535 }; 536 537 networking.useHostResolvConf = mkOption { 538 type = types.bool; 539 default = false; 540 description = '' 541 In containers, whether to use the 542 <filename>resolv.conf</filename> supplied by the host. 543 ''; 544 }; 545 546 networking.localCommands = mkOption { 547 type = types.lines; 548 default = ""; 549 example = "text=anything; echo You can put $text here."; 550 description = '' 551 Shell commands to be executed at the end of the 552 <literal>network-setup</literal> systemd service. Note that if 553 you are using DHCP to obtain the network configuration, 554 interfaces may not be fully configured yet. 555 ''; 556 }; 557 558 networking.interfaces = mkOption { 559 default = {}; 560 example = 561 { eth0.ipv4.addresses = [ { 562 address = "131.211.84.78"; 563 prefixLength = 25; 564 } ]; 565 }; 566 description = '' 567 The configuration for each network interface. If 568 <option>networking.useDHCP</option> is true, then every 569 interface not listed here will be configured using DHCP. 570 ''; 571 type = with types; attrsOf (submodule interfaceOpts); 572 }; 573 574 networking.vswitches = mkOption { 575 default = { }; 576 example = 577 { vs0.interfaces = { eth0 = { }; lo1 = { type="internal"; }; }; 578 vs1.interfaces = [ { name = "eth2"; } { name = "lo2"; type="internal"; } ]; 579 }; 580 description = 581 '' 582 This option allows you to define Open vSwitches that connect 583 physical networks together. The value of this option is an 584 attribute set. Each attribute specifies a vswitch, with the 585 attribute name specifying the name of the vswitch's network 586 interface. 587 ''; 588 589 type = with types; attrsOf (submodule { 590 591 options = { 592 593 interfaces = mkOption { 594 description = "The physical network interfaces connected by the vSwitch."; 595 type = with types; attrsOf (submodule vswitchInterfaceOpts); 596 }; 597 598 controllers = mkOption { 599 type = types.listOf types.str; 600 default = []; 601 example = [ "ptcp:6653:[::1]" ]; 602 description = '' 603 Specify the controller targets. For the allowed options see <literal>man 8 ovs-vsctl</literal>. 604 ''; 605 }; 606 607 openFlowRules = mkOption { 608 type = types.lines; 609 default = ""; 610 example = '' 611 actions=normal 612 ''; 613 description = '' 614 OpenFlow rules to insert into the Open vSwitch. All <literal>openFlowRules</literal> are 615 loaded with <literal>ovs-ofctl</literal> within one atomic operation. 616 ''; 617 }; 618 619 # TODO: custom "openflow version" type, with list from existing openflow protocols 620 supportedOpenFlowVersions = mkOption { 621 type = types.listOf types.str; 622 example = [ "OpenFlow10" "OpenFlow13" "OpenFlow14" ]; 623 default = [ "OpenFlow13" ]; 624 description = '' 625 Supported versions to enable on this switch. 626 ''; 627 }; 628 629 # TODO: use same type as elements from supportedOpenFlowVersions 630 openFlowVersion = mkOption { 631 type = types.str; 632 default = "OpenFlow13"; 633 description = '' 634 Version of OpenFlow protocol to use when communicating with the switch internally (e.g. with <literal>openFlowRules</literal>). 635 ''; 636 }; 637 638 extraOvsctlCmds = mkOption { 639 type = types.lines; 640 default = ""; 641 example = '' 642 set-fail-mode <switch_name> secure 643 set Bridge <switch_name> stp_enable=true 644 ''; 645 description = '' 646 Commands to manipulate the Open vSwitch database. Every line executed with <literal>ovs-vsctl</literal>. 647 All commands are bundled together with the operations for adding the interfaces 648 into one atomic operation. 649 ''; 650 }; 651 652 }; 653 654 }); 655 656 }; 657 658 networking.bridges = mkOption { 659 default = { }; 660 example = 661 { br0.interfaces = [ "eth0" "eth1" ]; 662 br1.interfaces = [ "eth2" "wlan0" ]; 663 }; 664 description = 665 '' 666 This option allows you to define Ethernet bridge devices 667 that connect physical networks together. The value of this 668 option is an attribute set. Each attribute specifies a 669 bridge, with the attribute name specifying the name of the 670 bridge's network interface. 671 ''; 672 673 type = with types; attrsOf (submodule { 674 675 options = { 676 677 interfaces = mkOption { 678 example = [ "eth0" "eth1" ]; 679 type = types.listOf types.str; 680 description = 681 "The physical network interfaces connected by the bridge."; 682 }; 683 684 rstp = mkOption { 685 default = false; 686 type = types.bool; 687 description = "Whether the bridge interface should enable rstp."; 688 }; 689 690 }; 691 692 }); 693 694 }; 695 696 networking.bonds = 697 let 698 driverOptionsExample = '' 699 { 700 miimon = "100"; 701 mode = "active-backup"; 702 } 703 ''; 704 in mkOption { 705 default = { }; 706 example = literalExpression '' 707 { 708 bond0 = { 709 interfaces = [ "eth0" "wlan0" ]; 710 driverOptions = ${driverOptionsExample}; 711 }; 712 anotherBond.interfaces = [ "enp4s0f0" "enp4s0f1" "enp5s0f0" "enp5s0f1" ]; 713 } 714 ''; 715 description = '' 716 This option allows you to define bond devices that aggregate multiple, 717 underlying networking interfaces together. The value of this option is 718 an attribute set. Each attribute specifies a bond, with the attribute 719 name specifying the name of the bond's network interface 720 ''; 721 722 type = with types; attrsOf (submodule { 723 724 options = { 725 726 interfaces = mkOption { 727 example = [ "enp4s0f0" "enp4s0f1" "wlan0" ]; 728 type = types.listOf types.str; 729 description = "The interfaces to bond together"; 730 }; 731 732 driverOptions = mkOption { 733 type = types.attrsOf types.str; 734 default = {}; 735 example = literalExpression driverOptionsExample; 736 description = '' 737 Options for the bonding driver. 738 Documentation can be found in 739 <link xlink:href="https://www.kernel.org/doc/Documentation/networking/bonding.txt" /> 740 ''; 741 742 }; 743 744 lacp_rate = mkOption { 745 default = null; 746 example = "fast"; 747 type = types.nullOr types.str; 748 description = '' 749 DEPRECATED, use `driverOptions`. 750 Option specifying the rate in which we'll ask our link partner 751 to transmit LACPDU packets in 802.3ad mode. 752 ''; 753 }; 754 755 miimon = mkOption { 756 default = null; 757 example = 100; 758 type = types.nullOr types.int; 759 description = '' 760 DEPRECATED, use `driverOptions`. 761 Miimon is the number of millisecond in between each round of polling 762 by the device driver for failed links. By default polling is not 763 enabled and the driver is trusted to properly detect and handle 764 failure scenarios. 765 ''; 766 }; 767 768 mode = mkOption { 769 default = null; 770 example = "active-backup"; 771 type = types.nullOr types.str; 772 description = '' 773 DEPRECATED, use `driverOptions`. 774 The mode which the bond will be running. The default mode for 775 the bonding driver is balance-rr, optimizing for throughput. 776 More information about valid modes can be found at 777 https://www.kernel.org/doc/Documentation/networking/bonding.txt 778 ''; 779 }; 780 781 xmit_hash_policy = mkOption { 782 default = null; 783 example = "layer2+3"; 784 type = types.nullOr types.str; 785 description = '' 786 DEPRECATED, use `driverOptions`. 787 Selects the transmit hash policy to use for slave selection in 788 balance-xor, 802.3ad, and tlb modes. 789 ''; 790 }; 791 792 }; 793 794 }); 795 }; 796 797 networking.macvlans = mkOption { 798 default = { }; 799 example = literalExpression '' 800 { 801 wan = { 802 interface = "enp2s0"; 803 mode = "vepa"; 804 }; 805 } 806 ''; 807 description = '' 808 This option allows you to define macvlan interfaces which should 809 be automatically created. 810 ''; 811 type = with types; attrsOf (submodule { 812 options = { 813 814 interface = mkOption { 815 example = "enp4s0"; 816 type = types.str; 817 description = "The interface the macvlan will transmit packets through."; 818 }; 819 820 mode = mkOption { 821 default = null; 822 type = types.nullOr types.str; 823 example = "vepa"; 824 description = "The mode of the macvlan device."; 825 }; 826 827 }; 828 829 }); 830 }; 831 832 networking.fooOverUDP = mkOption { 833 default = { }; 834 example = 835 { 836 primary = { port = 9001; local = { address = "192.0.2.1"; dev = "eth0"; }; }; 837 backup = { port = 9002; }; 838 }; 839 description = '' 840 This option allows you to configure Foo Over UDP and Generic UDP Encapsulation 841 endpoints. See <citerefentry><refentrytitle>ip-fou</refentrytitle> 842 <manvolnum>8</manvolnum></citerefentry> for details. 843 ''; 844 type = with types; attrsOf (submodule { 845 options = { 846 port = mkOption { 847 type = port; 848 description = '' 849 Local port of the encapsulation UDP socket. 850 ''; 851 }; 852 853 protocol = mkOption { 854 type = nullOr (ints.between 1 255); 855 default = null; 856 description = '' 857 Protocol number of the encapsulated packets. Specifying <literal>null</literal> 858 (the default) creates a GUE endpoint, specifying a protocol number will create 859 a FOU endpoint. 860 ''; 861 }; 862 863 local = mkOption { 864 type = nullOr (submodule { 865 options = { 866 address = mkOption { 867 type = types.str; 868 description = '' 869 Local address to bind to. The address must be available when the FOU 870 endpoint is created, using the scripted network setup this can be achieved 871 either by setting <literal>dev</literal> or adding dependency information to 872 <literal>systemd.services.&lt;name&gt;-fou-encap</literal>; it isn't supported 873 when using networkd. 874 ''; 875 }; 876 877 dev = mkOption { 878 type = nullOr str; 879 default = null; 880 example = "eth0"; 881 description = '' 882 Network device to bind to. 883 ''; 884 }; 885 }; 886 }); 887 default = null; 888 example = { address = "203.0.113.22"; }; 889 description = '' 890 Local address (and optionally device) to bind to using the given port. 891 ''; 892 }; 893 }; 894 }); 895 }; 896 897 networking.sits = mkOption { 898 default = { }; 899 example = literalExpression '' 900 { 901 hurricane = { 902 remote = "10.0.0.1"; 903 local = "10.0.0.22"; 904 ttl = 255; 905 }; 906 msipv6 = { 907 remote = "192.168.0.1"; 908 dev = "enp3s0"; 909 ttl = 127; 910 }; 911 } 912 ''; 913 description = '' 914 This option allows you to define 6-to-4 interfaces which should be automatically created. 915 ''; 916 type = with types; attrsOf (submodule { 917 options = { 918 919 remote = mkOption { 920 type = types.nullOr types.str; 921 default = null; 922 example = "10.0.0.1"; 923 description = '' 924 The address of the remote endpoint to forward traffic over. 925 ''; 926 }; 927 928 local = mkOption { 929 type = types.nullOr types.str; 930 default = null; 931 example = "10.0.0.22"; 932 description = '' 933 The address of the local endpoint which the remote 934 side should send packets to. 935 ''; 936 }; 937 938 ttl = mkOption { 939 type = types.nullOr types.int; 940 default = null; 941 example = 255; 942 description = '' 943 The time-to-live of the connection to the remote tunnel endpoint. 944 ''; 945 }; 946 947 dev = mkOption { 948 type = types.nullOr types.str; 949 default = null; 950 example = "enp4s0f0"; 951 description = '' 952 The underlying network device on which the tunnel resides. 953 ''; 954 }; 955 956 encapsulation = with types; mkOption { 957 type = nullOr (submodule { 958 options = { 959 type = mkOption { 960 type = enum [ "fou" "gue" ]; 961 description = '' 962 Selects encapsulation type. See 963 <citerefentry><refentrytitle>ip-link</refentrytitle> 964 <manvolnum>8</manvolnum></citerefentry> for details. 965 ''; 966 }; 967 968 port = mkOption { 969 type = port; 970 example = 9001; 971 description = '' 972 Destination port for encapsulated packets. 973 ''; 974 }; 975 976 sourcePort = mkOption { 977 type = nullOr types.port; 978 default = null; 979 example = 9002; 980 description = '' 981 Source port for encapsulated packets. Will be chosen automatically by 982 the kernel if unset. 983 ''; 984 }; 985 }; 986 }); 987 default = null; 988 example = { type = "fou"; port = 9001; }; 989 description = '' 990 Configures encapsulation in UDP packets. 991 ''; 992 }; 993 994 }; 995 996 }); 997 }; 998 999 networking.vlans = mkOption { 1000 default = { }; 1001 example = literalExpression '' 1002 { 1003 vlan0 = { 1004 id = 3; 1005 interface = "enp3s0"; 1006 }; 1007 vlan1 = { 1008 id = 1; 1009 interface = "wlan0"; 1010 }; 1011 } 1012 ''; 1013 description = 1014 '' 1015 This option allows you to define vlan devices that tag packets 1016 on top of a physical interface. The value of this option is an 1017 attribute set. Each attribute specifies a vlan, with the name 1018 specifying the name of the vlan interface. 1019 ''; 1020 1021 type = with types; attrsOf (submodule { 1022 1023 options = { 1024 1025 id = mkOption { 1026 example = 1; 1027 type = types.int; 1028 description = "The vlan identifier"; 1029 }; 1030 1031 interface = mkOption { 1032 example = "enp4s0"; 1033 type = types.str; 1034 description = "The interface the vlan will transmit packets through."; 1035 }; 1036 1037 }; 1038 1039 }); 1040 1041 }; 1042 1043 networking.wlanInterfaces = mkOption { 1044 default = { }; 1045 example = literalExpression '' 1046 { 1047 wlan-station0 = { 1048 device = "wlp6s0"; 1049 }; 1050 wlan-adhoc0 = { 1051 type = "ibss"; 1052 device = "wlp6s0"; 1053 mac = "02:00:00:00:00:01"; 1054 }; 1055 wlan-p2p0 = { 1056 device = "wlp6s0"; 1057 mac = "02:00:00:00:00:02"; 1058 }; 1059 wlan-ap0 = { 1060 device = "wlp6s0"; 1061 mac = "02:00:00:00:00:03"; 1062 }; 1063 } 1064 ''; 1065 description = 1066 '' 1067 Creating multiple WLAN interfaces on top of one physical WLAN device (NIC). 1068 1069 The name of the WLAN interface corresponds to the name of the attribute. 1070 A NIC is referenced by the persistent device name of the WLAN interface that 1071 <literal>udev</literal> assigns to a NIC by default. 1072 If a NIC supports multiple WLAN interfaces, then the one NIC can be used as 1073 <literal>device</literal> for multiple WLAN interfaces. 1074 If a NIC is used for creating WLAN interfaces, then the default WLAN interface 1075 with a persistent device name form <literal>udev</literal> is not created. 1076 A WLAN interface with the persistent name assigned from <literal>udev</literal> 1077 would have to be created explicitly. 1078 ''; 1079 1080 type = with types; attrsOf (submodule { 1081 1082 options = { 1083 1084 device = mkOption { 1085 type = types.str; 1086 example = "wlp6s0"; 1087 description = "The name of the underlying hardware WLAN device as assigned by <literal>udev</literal>."; 1088 }; 1089 1090 type = mkOption { 1091 type = types.enum [ "managed" "ibss" "monitor" "mesh" "wds" ]; 1092 default = "managed"; 1093 example = "ibss"; 1094 description = '' 1095 The type of the WLAN interface. 1096 The type has to be supported by the underlying hardware of the device. 1097 ''; 1098 }; 1099 1100 meshID = mkOption { 1101 type = types.nullOr types.str; 1102 default = null; 1103 description = "MeshID of interface with type <literal>mesh</literal>."; 1104 }; 1105 1106 flags = mkOption { 1107 type = with types; nullOr (enum [ "none" "fcsfail" "control" "otherbss" "cook" "active" ]); 1108 default = null; 1109 example = "control"; 1110 description = '' 1111 Flags for interface of type <literal>monitor</literal>. 1112 ''; 1113 }; 1114 1115 fourAddr = mkOption { 1116 type = types.nullOr types.bool; 1117 default = null; 1118 description = "Whether to enable <literal>4-address mode</literal> with type <literal>managed</literal>."; 1119 }; 1120 1121 mac = mkOption { 1122 type = types.nullOr types.str; 1123 default = null; 1124 example = "02:00:00:00:00:01"; 1125 description = '' 1126 MAC address to use for the device. If <literal>null</literal>, then the MAC of the 1127 underlying hardware WLAN device is used. 1128 1129 INFO: Locally administered MAC addresses are of the form: 1130 <itemizedlist> 1131 <listitem><para>x2:xx:xx:xx:xx:xx</para></listitem> 1132 <listitem><para>x6:xx:xx:xx:xx:xx</para></listitem> 1133 <listitem><para>xA:xx:xx:xx:xx:xx</para></listitem> 1134 <listitem><para>xE:xx:xx:xx:xx:xx</para></listitem> 1135 </itemizedlist> 1136 ''; 1137 }; 1138 1139 }; 1140 1141 }); 1142 1143 }; 1144 1145 networking.useDHCP = mkOption { 1146 type = types.bool; 1147 default = true; 1148 description = '' 1149 Whether to use DHCP to obtain an IP address and other 1150 configuration for all network interfaces that are not manually 1151 configured. 1152 1153 Using this option is highly discouraged and also incompatible with 1154 <option>networking.useNetworkd</option>. Please use 1155 <option>networking.interfaces.&lt;name&gt;.useDHCP</option> instead 1156 and set this to false. 1157 ''; 1158 }; 1159 1160 networking.useNetworkd = mkOption { 1161 default = false; 1162 type = types.bool; 1163 description = '' 1164 Whether we should use networkd as the network configuration backend or 1165 the legacy script based system. Note that this option is experimental, 1166 enable at your own risk. 1167 ''; 1168 }; 1169 1170 networking.tempAddresses = mkOption { 1171 default = if cfg.enableIPv6 then "default" else "disabled"; 1172 type = types.enum (lib.attrNames tempaddrValues); 1173 description = '' 1174 Whether to enable IPv6 Privacy Extensions for interfaces not 1175 configured explicitly in 1176 <xref linkend="opt-networking.interfaces._name_.tempAddress" />. 1177 1178 This sets the ipv6.conf.*.use_tempaddr sysctl for all 1179 interfaces. Possible values are: 1180 1181 ${tempaddrDoc} 1182 ''; 1183 }; 1184 1185 }; 1186 1187 1188 ###### implementation 1189 1190 config = { 1191 1192 warnings = concatMap (i: i.warnings) interfaces; 1193 1194 assertions = 1195 (forEach interfaces (i: { 1196 # With the linux kernel, interface name length is limited by IFNAMSIZ 1197 # to 16 bytes, including the trailing null byte. 1198 # See include/linux/if.h in the kernel sources 1199 assertion = stringLength i.name < 16; 1200 message = '' 1201 The name of networking.interfaces."${i.name}" is too long, it needs to be less than 16 characters. 1202 ''; 1203 })) ++ (forEach slaveIfs (i: { 1204 assertion = i.ipv4.addresses == [ ] && i.ipv6.addresses == [ ]; 1205 message = '' 1206 The networking.interfaces."${i.name}" must not have any defined ips when it is a slave. 1207 ''; 1208 })) ++ (forEach interfaces (i: { 1209 assertion = i.tempAddress != "disabled" -> cfg.enableIPv6; 1210 message = '' 1211 Temporary addresses are only needed when IPv6 is enabled. 1212 ''; 1213 })) ++ (forEach interfaces (i: { 1214 assertion = (i.virtual && i.virtualType == "tun") -> i.macAddress == null; 1215 message = '' 1216 Setting a MAC Address for tun device ${i.name} isn't supported. 1217 ''; 1218 })) ++ [ 1219 { 1220 assertion = cfg.hostId == null || (stringLength cfg.hostId == 8 && isHexString cfg.hostId); 1221 message = "Invalid value given to the networking.hostId option."; 1222 } 1223 ]; 1224 1225 boot.kernelModules = [ ] 1226 ++ optional hasVirtuals "tun" 1227 ++ optional hasSits "sit" 1228 ++ optional hasBonds "bonding" 1229 ++ optional hasFous "fou"; 1230 1231 boot.extraModprobeConfig = 1232 # This setting is intentional as it prevents default bond devices 1233 # from being created. 1234 optionalString hasBonds "options bonding max_bonds=0"; 1235 1236 boot.kernel.sysctl = { 1237 "net.ipv4.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces); 1238 "net.ipv6.conf.all.disable_ipv6" = mkDefault (!cfg.enableIPv6); 1239 "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6); 1240 # networkmanager falls back to "/proc/sys/net/ipv6/conf/default/use_tempaddr" 1241 "net.ipv6.conf.default.use_tempaddr" = tempaddrValues.${cfg.tempAddresses}.sysctl; 1242 } // listToAttrs (flip concatMap (filter (i: i.proxyARP) interfaces) 1243 (i: [(nameValuePair "net.ipv4.conf.${replaceChars ["."] ["/"] i.name}.proxy_arp" true)])) 1244 // listToAttrs (forEach interfaces 1245 (i: let 1246 opt = i.tempAddress; 1247 val = tempaddrValues.${opt}.sysctl; 1248 in nameValuePair "net.ipv6.conf.${replaceChars ["."] ["/"] i.name}.use_tempaddr" val)); 1249 1250 # Capabilities won't work unless we have at-least a 4.3 Linux 1251 # kernel because we need the ambient capability 1252 security.wrappers = if (versionAtLeast (getVersion config.boot.kernelPackages.kernel) "4.3") then { 1253 ping = { 1254 owner = "root"; 1255 group = "root"; 1256 capabilities = "cap_net_raw+p"; 1257 source = "${pkgs.iputils.out}/bin/ping"; 1258 }; 1259 } else { 1260 ping = { 1261 setuid = true; 1262 owner = "root"; 1263 group = "root"; 1264 source = "${pkgs.iputils.out}/bin/ping"; 1265 }; 1266 }; 1267 security.apparmor.policies."bin.ping".profile = lib.mkIf config.security.apparmor.policies."bin.ping".enable (lib.mkAfter '' 1268 /run/wrappers/bin/ping { 1269 include <abstractions/base> 1270 include <nixos/security.wrappers> 1271 rpx /run/wrappers/wrappers.*/ping, 1272 } 1273 /run/wrappers/wrappers.*/ping { 1274 include <abstractions/base> 1275 include <nixos/security.wrappers> 1276 r /run/wrappers/wrappers.*/ping.real, 1277 mrpx ${config.security.wrappers.ping.source}, 1278 capability net_raw, 1279 capability setpcap, 1280 } 1281 ''); 1282 1283 # Set the host and domain names in the activation script. Don't 1284 # clear it if it's not configured in the NixOS configuration, 1285 # since it may have been set by dhcpcd in the meantime. 1286 system.activationScripts.hostname = 1287 optionalString (cfg.hostName != "") '' 1288 hostname "${cfg.hostName}" 1289 ''; 1290 system.activationScripts.domain = 1291 optionalString (cfg.domain != null) '' 1292 domainname "${cfg.domain}" 1293 ''; 1294 1295 environment.etc.hostid = mkIf (cfg.hostId != null) 1296 { source = pkgs.runCommand "gen-hostid" { preferLocalBuild = true; } '' 1297 hi="${cfg.hostId}" 1298 ${if pkgs.stdenv.isBigEndian then '' 1299 echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > $out 1300 '' else '' 1301 echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > $out 1302 ''} 1303 ''; 1304 }; 1305 1306 # static hostname configuration needed for hostnamectl and the 1307 # org.freedesktop.hostname1 dbus service (both provided by systemd) 1308 environment.etc.hostname = mkIf (cfg.hostName != "") 1309 { 1310 text = cfg.hostName + "\n"; 1311 }; 1312 1313 environment.systemPackages = 1314 [ pkgs.host 1315 pkgs.iproute2 1316 pkgs.iputils 1317 pkgs.nettools 1318 ] 1319 ++ optionals config.networking.wireless.enable [ 1320 pkgs.wirelesstools # FIXME: obsolete? 1321 pkgs.iw 1322 ] 1323 ++ bridgeStp; 1324 1325 # The network-interfaces target is kept for backwards compatibility. 1326 # New modules must NOT use it. 1327 systemd.targets.network-interfaces = 1328 { description = "All Network Interfaces (deprecated)"; 1329 wantedBy = [ "network.target" ]; 1330 before = [ "network.target" ]; 1331 after = [ "network-pre.target" ]; 1332 unitConfig.X-StopOnReconfiguration = true; 1333 }; 1334 1335 systemd.services = { 1336 network-local-commands = { 1337 description = "Extra networking commands."; 1338 before = [ "network.target" ]; 1339 wantedBy = [ "network.target" ]; 1340 after = [ "network-pre.target" ]; 1341 unitConfig.ConditionCapability = "CAP_NET_ADMIN"; 1342 path = [ pkgs.iproute2 ]; 1343 serviceConfig.Type = "oneshot"; 1344 serviceConfig.RemainAfterExit = true; 1345 script = '' 1346 # Run any user-specified commands. 1347 ${cfg.localCommands} 1348 ''; 1349 }; 1350 }; 1351 services.mstpd = mkIf needsMstpd { enable = true; }; 1352 1353 virtualisation.vswitch = mkIf (cfg.vswitches != { }) { enable = true; }; 1354 1355 services.udev.packages = [ 1356 (pkgs.writeTextFile rec { 1357 name = "ipv6-privacy-extensions.rules"; 1358 destination = "/etc/udev/rules.d/98-${name}"; 1359 text = let 1360 sysctl-value = tempaddrValues.${cfg.tempAddresses}.sysctl; 1361 in '' 1362 # enable and prefer IPv6 privacy addresses by default 1363 ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.bash}/bin/sh -c 'echo ${sysctl-value} > /proc/sys/net/ipv6/conf/%k/use_tempaddr'" 1364 ''; 1365 }) 1366 (pkgs.writeTextFile rec { 1367 name = "ipv6-privacy-extensions.rules"; 1368 destination = "/etc/udev/rules.d/99-${name}"; 1369 text = concatMapStrings (i: 1370 let 1371 opt = i.tempAddress; 1372 val = tempaddrValues.${opt}.sysctl; 1373 msg = tempaddrValues.${opt}.description; 1374 in 1375 '' 1376 # override to ${msg} for ${i.name} 1377 ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.procps}/bin/sysctl net.ipv6.conf.${replaceChars ["."] ["/"] i.name}.use_tempaddr=${val}" 1378 '') (filter (i: i.tempAddress != cfg.tempAddresses) interfaces); 1379 }) 1380 ] ++ lib.optional (cfg.wlanInterfaces != {}) 1381 (pkgs.writeTextFile { 1382 name = "99-zzz-40-wlanInterfaces.rules"; 1383 destination = "/etc/udev/rules.d/99-zzz-40-wlanInterfaces.rules"; 1384 text = 1385 let 1386 # Collect all interfaces that are defined for a device 1387 # as device:interface key:value pairs. 1388 wlanDeviceInterfaces = 1389 let 1390 allDevices = unique (mapAttrsToList (_: v: v.device) cfg.wlanInterfaces); 1391 interfacesOfDevice = d: filterAttrs (_: v: v.device == d) cfg.wlanInterfaces; 1392 in 1393 genAttrs allDevices (d: interfacesOfDevice d); 1394 1395 # Convert device:interface key:value pairs into a list, and if it exists, 1396 # place the interface which is named after the device at the beginning. 1397 wlanListDeviceFirst = device: interfaces: 1398 if hasAttr device interfaces 1399 then mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n==device) interfaces) ++ mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n!=device) interfaces) 1400 else mapAttrsToList (n: v: v // {_iName = n;}) interfaces; 1401 1402 # Udev script to execute for the default WLAN interface with the persistend udev name. 1403 # The script creates the required, new WLAN interfaces interfaces and configures the 1404 # existing, default interface. 1405 curInterfaceScript = device: current: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${device}.sh" '' 1406 #!${pkgs.runtimeShell} 1407 # Change the wireless phy device to a predictable name. 1408 ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/$INTERFACE/phy80211/name` set name ${device} 1409 1410 # Add new WLAN interfaces 1411 ${flip concatMapStrings new (i: '' 1412 ${pkgs.iw}/bin/iw phy ${device} interface add ${i._iName} type managed 1413 '')} 1414 1415 # Configure the current interface 1416 ${pkgs.iw}/bin/iw dev ${device} set type ${current.type} 1417 ${optionalString (current.type == "mesh" && current.meshID!=null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${current.meshID}"} 1418 ${optionalString (current.type == "monitor" && current.flags!=null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${current.flags}"} 1419 ${optionalString (current.type == "managed" && current.fourAddr!=null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if current.fourAddr then "on" else "off"}"} 1420 ${optionalString (current.mac != null) "${pkgs.iproute2}/bin/ip link set dev ${device} address ${current.mac}"} 1421 ''; 1422 1423 # Udev script to execute for a new WLAN interface. The script configures the new WLAN interface. 1424 newInterfaceScript = new: pkgs.writeScript "udev-run-script-wlan-interfaces-${new._iName}.sh" '' 1425 #!${pkgs.runtimeShell} 1426 # Configure the new interface 1427 ${pkgs.iw}/bin/iw dev ${new._iName} set type ${new.type} 1428 ${optionalString (new.type == "mesh" && new.meshID!=null) "${pkgs.iw}/bin/iw dev ${new._iName} set meshid ${new.meshID}"} 1429 ${optionalString (new.type == "monitor" && new.flags!=null) "${pkgs.iw}/bin/iw dev ${new._iName} set monitor ${new.flags}"} 1430 ${optionalString (new.type == "managed" && new.fourAddr!=null) "${pkgs.iw}/bin/iw dev ${new._iName} set 4addr ${if new.fourAddr then "on" else "off"}"} 1431 ${optionalString (new.mac != null) "${pkgs.iproute2}/bin/ip link set dev ${new._iName} address ${new.mac}"} 1432 ''; 1433 1434 # Udev attributes for systemd to name the device and to create a .device target. 1435 systemdAttrs = n: ''NAME:="${n}", ENV{INTERFACE}="${n}", ENV{SYSTEMD_ALIAS}="/sys/subsystem/net/devices/${n}", TAG+="systemd"''; 1436 in 1437 flip (concatMapStringsSep "\n") (attrNames wlanDeviceInterfaces) (device: 1438 let 1439 interfaces = wlanListDeviceFirst device wlanDeviceInterfaces.${device}; 1440 curInterface = elemAt interfaces 0; 1441 newInterfaces = drop 1 interfaces; 1442 in '' 1443 # It is important to have that rule first as overwriting the NAME attribute also prevents the 1444 # next rules from matching. 1445 ${flip (concatMapStringsSep "\n") (wlanListDeviceFirst device wlanDeviceInterfaces.${device}) (interface: 1446 ''ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", ENV{INTERFACE}=="${interface._iName}", ${systemdAttrs interface._iName}, RUN+="${newInterfaceScript interface}"'')} 1447 1448 # Add the required, new WLAN interfaces to the default WLAN interface with the 1449 # persistent, default name as assigned by udev. 1450 ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", NAME=="${device}", ${systemdAttrs curInterface._iName}, RUN+="${curInterfaceScript device curInterface newInterfaces}" 1451 # Generate the same systemd events for both 'add' and 'move' udev events. 1452 ACTION=="move", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", NAME=="${device}", ${systemdAttrs curInterface._iName} 1453 ''); 1454 }); 1455 }; 1456 1457}