diff options
Diffstat (limited to 'nixos/modules/tasks/network-interfaces.nix')
-rw-r--r-- | nixos/modules/tasks/network-interfaces.nix | 194 |
1 files changed, 182 insertions, 12 deletions
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix index 7af3160e2d42..2d6522a1bf9d 100644 --- a/nixos/modules/tasks/network-interfaces.nix +++ b/nixos/modules/tasks/network-interfaces.nix @@ -381,8 +381,8 @@ in description = '' This option allows you to define Open vSwitches that connect - physical networks together. The value of this option is an - attribute set. Each attribute specifies a vswitch, with the + physical networks together. The value of this option is an + attribute set. Each attribute specifies a vswitch, with the attribute name specifying the name of the vswitch's network interface. ''; @@ -398,16 +398,6 @@ in "The physical network interfaces connected by the vSwitch."; }; - bindInterfaces = mkOption { - type = types.bool; - default = false; - description = '' - If true, then the interfaces of the vSwitch are brought 'up' and especially - also 'down' together with the vSwitch. That requires that every interfaces - is configured as a systemd network services. - ''; - }; - controllers = mkOption { type = types.listOf types.str; default = []; @@ -688,6 +678,110 @@ in }; }; + networking.wlanInterfaces = mkOption { + default = { }; + example = { + "wlan-station0" = { + device = "wlp6s0"; + }; + "wlan-adhoc0" = { + type = "ibss"; + device = "wlp6s0"; + mac = "02:00:00:00:00:01"; + }; + "wlan-p2p0" = { + device = "wlp6s0"; + mac = "02:00:00:00:00:02"; + }; + "wlan-ap0" = { + device = "wlp6s0"; + mac = "02:00:00:00:00:03"; + }; + }; + description = + '' + Creating multiple WLAN interfaces on top of one physical WLAN device (NIC). + + The name of the WLAN interface corresponds to the name of the attribute. + A NIC is referenced by the persistent device name of the WLAN interface that + <literal>udev</literal> assigns to a NIC by default. + If a NIC supports multiple WLAN interfaces, then the one NIC can be used as + <literal>device</literal> for multiple WLAN interfaces. + If a NIC is used for creating WLAN interfaces, then the default WLAN interface + with a persistent device name form <literal>udev</literal> is not created. + A WLAN interface with the persistent name assigned from <literal>udev</literal> + would have to be created explicitly. + ''; + + type = types.attrsOf types.optionSet; + + options = { + + device = mkOption { + type = types.string; + example = "wlp6s0"; + description = "The name of the underlying hardware WLAN device as assigned by <literal>udev</literal>."; + }; + + type = mkOption { + type = types.string; + default = "managed"; + example = "ibss"; + description = '' + The type of the WLAN interface. The type has to be either <literal>managed</literal>, + <literal>ibss</literal>, <literal>monitor</literal>, <literal>mesh</literal> or <literal>wds</literal>. + Also, the type has to be supported by the underlying hardware of the device. + ''; + }; + + meshID = mkOption { + type = types.nullOr types.string; + default = null; + description = "MeshID of interface with type <literal>mesh</literal>."; + }; + + flags = mkOption { + type = types.nullOr types.string; + default = null; + example = "control"; + description = '' + Flags for interface of type <literal>monitor</literal>. The valid flags are: + none: no special flags + fcsfail: show frames with FCS errors + control: show control frames + otherbss: show frames from other BSSes + cook: use cooked mode + active: use active mode (ACK incoming unicast packets) + ''; + }; + + fourAddr = mkOption { + type = types.nullOr types.bool; + default = null; + description = "Whether to enable <literal>4-address mode</literal> with type <literal>managed</literal>."; + }; + + mac = mkOption { + type = types.nullOr types.str; + default = null; + example = "02:00:00:00:00:01"; + description = '' + MAC address to use for the device. If <literal>null</literal>, then the MAC of the + underlying hardware WLAN device is used. + + INFO: Locally administered MAC addresses are of the form: + <itemizedlist> + <listitem><para>x2:xx:xx:xx:xx:xx</para></listitem> + <listitem><para>x6:xx:xx:xx:xx:xx</para></listitem> + <listitem><para>xA:xx:xx:xx:xx:xx</para></listitem> + <listitem><para>xE:xx:xx:xx:xx:xx</para></listitem> + </itemizedlist> + ''; + }; + + }; + }; + networking.useDHCP = mkOption { type = types.bool; default = true; @@ -844,6 +938,82 @@ in virtualisation.vswitch = mkIf (cfg.vswitches != { }) { enable = true; }; + services.udev.packages = mkIf (cfg.wlanInterfaces != {}) [ + (pkgs.writeTextFile { + name = "99-zzz-40-wlanInterfaces.rules"; + destination = "/etc/udev/rules.d/99-zzz-40-wlanInterfaces.rules"; + text = + let + # Collect all interfaces that are defined for a device + # as device:interface key:value pairs. + wlanDeviceInterfaces = + let + allDevices = unique (mapAttrsToList (_: v: v.device) cfg.wlanInterfaces); + interfacesOfDevice = d: filterAttrs (_: v: v.device == d) cfg.wlanInterfaces; + in + genAttrs allDevices (d: interfacesOfDevice d); + + # Convert device:interface key:value pairs into a list, and if it exists, + # place the interface which is named after the device at the beginning. + wlanListDeviceFirst = device: interfaces: + if hasAttr device interfaces + then mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n==device) interfaces) ++ mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n!=device) interfaces) + else mapAttrsToList (n: v: v // {_iName = n;}) interfaces; + + # Udev script to execute for the default WLAN interface with the persistend udev name. + # The script creates the required, new WLAN interfaces interfaces and configures the + # existing, default interface. + curInterfaceScript = device: current: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${device}.sh" '' + #!${pkgs.stdenv.shell} + # Change the wireless phy device to a predictable name. + ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/$INTERFACE/phy80211/name` set name ${device} + + # Add new WLAN interfaces + ${flip concatMapStrings new (i: '' + ${pkgs.iw}/bin/iw phy ${device} interface add ${i._iName} type managed + '')} + + # Configure the current interface + ${pkgs.iw}/bin/iw dev ${device} set type ${current.type} + ${optionalString (current.type == "mesh" && current.meshID!=null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${current.meshID}"} + ${optionalString (current.type == "monitor" && current.flags!=null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${current.flags}"} + ${optionalString (current.type == "managed" && current.fourAddr!=null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if current.fourAddr then "on" else "off"}"} + ${optionalString (current.mac != null) "${pkgs.iproute}/bin/ip link set dev ${device} address ${current.mac}"} + ''; + + # Udev script to execute for a new WLAN interface. The script configures the new WLAN interface. + newInterfaceScript = new: pkgs.writeScript "udev-run-script-wlan-interfaces-${new._iName}.sh" '' + #!${pkgs.stdenv.shell} + # Configure the new interface + ${pkgs.iw}/bin/iw dev ${new._iName} set type ${new.type} + ${optionalString (new.type == "mesh" && new.meshID!=null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${new.meshID}"} + ${optionalString (new.type == "monitor" && new.flags!=null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${new.flags}"} + ${optionalString (new.type == "managed" && new.fourAddr!=null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if new.fourAddr then "on" else "off"}"} + ${optionalString (new.mac != null) "${pkgs.iproute}/bin/ip link set dev ${device} address ${new.mac}"} + ''; + + # Udev attributes for systemd to name the device and to create a .device target. + systemdAttrs = n: ''NAME:="${n}", ENV{INTERFACE}:="${n}", ENV{SYSTEMD_ALIAS}:="/sys/subsystem/net/devices/${n}", TAG+="systemd"''; + in + flip (concatMapStringsSep "\n") (attrNames wlanDeviceInterfaces) (device: + let + interfaces = wlanListDeviceFirst device wlanDeviceInterfaces."${device}"; + curInterface = elemAt interfaces 0; + newInterfaces = drop 1 interfaces; + in '' + # It is important to have that rule first as overwriting the NAME attribute also prevents the + # next rules from matching. + ${flip (concatMapStringsSep "\n") (wlanListDeviceFirst device wlanDeviceInterfaces."${device}") (interface: + ''ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", ENV{INTERFACE}=="${interface._iName}", ${systemdAttrs interface._iName}, RUN+="${newInterfaceScript interface}"'')} + + # Add the required, new WLAN interfaces to the default WLAN interface with the + # persistent, default name as assigned by udev. + ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", NAME=="${device}", ${systemdAttrs curInterface._iName}, RUN+="${curInterfaceScript device curInterface newInterfaces}" + # Generate the same systemd events for both 'add' and 'move' udev events. + ACTION=="move", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", NAME=="${device}", ${systemdAttrs curInterface._iName} + ''); + }) ]; + }; } |