summary refs log tree commit diff
path: root/nixos/modules/tasks/network-interfaces.nix
diff options
context:
space:
mode:
authorThomas Strobel <ts468@cam.ac.uk>2015-09-28 18:43:40 +0200
committerThomas Strobel <ts468@cam.ac.uk>2015-10-01 15:35:30 +0200
commitc0248c0c1f46f42ed736386e0d0ee67da5b329a6 (patch)
tree57648b5d42386645e877c0ed6e2c3e855a56f498 /nixos/modules/tasks/network-interfaces.nix
parente0d5b14ce412f6ff629d4fb5104d34a0b00804b7 (diff)
downloadnixpkgs-c0248c0c1f46f42ed736386e0d0ee67da5b329a6.tar
nixpkgs-c0248c0c1f46f42ed736386e0d0ee67da5b329a6.tar.gz
nixpkgs-c0248c0c1f46f42ed736386e0d0ee67da5b329a6.tar.bz2
nixpkgs-c0248c0c1f46f42ed736386e0d0ee67da5b329a6.tar.lz
nixpkgs-c0248c0c1f46f42ed736386e0d0ee67da5b329a6.tar.xz
nixpkgs-c0248c0c1f46f42ed736386e0d0ee67da5b329a6.tar.zst
nixpkgs-c0248c0c1f46f42ed736386e0d0ee67da5b329a6.zip
networking module: init 'wlanInterfaces' option
Configuration option for setting up virtual WLAN interfaces.

If the hardware NIC supports it, then multiple virtual WLAN interfaces can be
configured through the options of the new 'networking.wlanInterfaces' module.
For example, the following configuration transforms the device with the persistent
udev name 'wlp6s0' into a managed and a ad hoc device with the device names
'wlan-managed0' and 'wlan-adhoc0', respectively:

networking.wlanInterfaces = {
    "wlan-managed0" = {
        type = "managed";
        device = "wlp6s0";
    };
    "wlan-adhoc0" = {
        type = "ibss";
        device = "wlp6s0";
    };
};

Internally, a udev rule is created that matches wlp6s0 and runs a script which adds
the missing virtual interfaces and re-configures the wlp6s0 interface accordingly.
Once the new interfaces are created by the Linux kernel, the configuration of the
interfaces is managed by udev and systemd in the usual way.
Diffstat (limited to 'nixos/modules/tasks/network-interfaces.nix')
-rw-r--r--nixos/modules/tasks/network-interfaces.nix168
1 files changed, 168 insertions, 0 deletions
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 7af3160e2d4..f9410d75922 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -46,6 +46,51 @@ 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 [{"${device}"=interfaces.device; _iName=device;}] ++ mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n!=device) interfaces)
+    else mapAttrsToList (n: v: v // {_iName = n;}) interfaces;
+
+  # udev script that configures a physical wlan device and adds virtual interfaces
+  wlanDeviceUdevScript = device: interfaceList: pkgs.writeScript "wlan-${device}-udev-script" ''
+    #!${pkgs.stdenv.shell}
+
+    # Change the wireless phy device to a predictable name.
+    if [ -e "/sys/class/net/${device}/phy80211/name" ]; then
+      ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/${device}/phy80211/name` set name ${device} || true
+    fi
+
+    # Crate new, virtual interfaces and configure them at the same time
+    ${flip concatMapStrings (drop 1 interfaceList) (i: ''
+    ${pkgs.iw}/bin/iw dev ${device} interface add ${i._iName} type ${i.type} \
+      ${optionalString (i.type == "mesh" && i.meshID != null) "mesh_id ${i.meshID}"} \
+      ${optionalString (i.type == "monitor" && i.flags != null) "flags ${i.flags}"} \
+      ${optionalString (i.type == "managed" && i.fourAddr != null) "4addr ${if i.fourAddr then "on" else "off"}"} \
+      ${optionalString (i.mac != null) "addr ${i.mac}"}
+    '')}
+
+    # Reconfigure and rename the default interface that already exists
+    ${flip concatMapStrings (take 1 interfaceList) (i: ''
+      ${pkgs.iw}/bin/iw dev ${device} set type ${i.type}
+      ${optionalString (i.type == "mesh" && i.meshID != null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${i.meshID}"}
+      ${optionalString (i.type == "monitor" && i.flags != null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${i.flags}"}
+      ${optionalString (i.type == "managed" && i.fourAddr != null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if i.fourAddr then "on" else "off"}"}
+      ${optionalString (i.mac != null) "${pkgs.iproute}/bin/ip link set dev ${device} address ${i.mac}"}
+      ${optionalString (device != i._iName) "${pkgs.iproute}/bin/ip link set dev ${device} name ${i._iName}"}
+    '')}
+  '';
+
   # We must escape interfaces due to the systemd interpretation
   subsystemDevice = interface:
     "sys-subsystem-net-devices-${escapeSystemdPath interface}.device";
@@ -688,6 +733,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>x2:xx:xx:xx:xx:xx</listitem>
+            <listitem>x6:xx:xx:xx:xx:xx</listitem>
+            <listitem>xA:xx:xx:xx:xx:xx</listitem>
+            <listitem>xE:xx:xx:xx:xx:xx</listitem>
+            </itemizedlist>
+          '';
+        };
+
+      };
+    };
+
     networking.useDHCP = mkOption {
       type = types.bool;
       default = true;
@@ -844,6 +993,25 @@ in
 
     virtualisation.vswitch = mkIf (cfg.vswitches != { }) { enable = true; };
 
+    services.udev.packages = mkIf (cfg.wlanInterfaces != {}) [
+      (pkgs.writeTextFile {
+        name = "99-zzz-wlanInterfaces-last.rules";
+        destination = "/etc/udev/rules.d/99-zzz-wlanInterfaces-last.rules";
+        text = ''
+          # If persistent udev device name is not used for an interface, then do not
+          # call systemd for that udev device name and only execute the script that
+          # modifies or prepares the WLAN interfaces. All other commands that would
+          # otherwise be executed when the udev device is added, like, e.g., the calling
+          # of systemd-sysctl or the activation of wpa_supplicant is disabled when the
+          # persistend udev device name is not usef for an interface.
+          ${flip (concatMapStringsSep "\n") (attrNames wlanDeviceInterfaces) (device:
+          let script = wlanDeviceUdevScript device (wlanListDeviceFirst device wlanDeviceInterfaces."${device}"); in
+          if hasAttr device cfg.wlanInterfaces
+          then ''ACTION=="add", SUBSYSTEM=="net", NAME=="${device}", ENV{DEVTYPE}=="wlan", RUN+="${script}"''
+          else ''ACTION=="add", SUBSYSTEM=="net", NAME=="${device}", ENV{DEVTYPE}=="wlan", NAME="", TAG-="systemd", RUN:="${script}"'')}
+        '';
+      }) ];
+
   };
 
 }