Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: hostapd: extend module to allow multiple APs. #49171

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
257 changes: 143 additions & 114 deletions nixos/modules/services/networking/hostapd.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# TODO:
#
# asserts
# ensure interface name is set
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably not too complicated to add code for this inside this PR.

# ensure that the nl80211 module is loaded/compiled in the kernel
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be moved into the generic services.hostapd description.

# wpa_supplicant and hostapd on the same wireless interface doesn't make any sense
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably the same applies for this one as well - In networking.supplicant.<name>, <name> can be separated by spaces, so matching automatically would be a bit more complicated.


Expand All @@ -12,14 +13,14 @@ let

cfg = config.services.hostapd;

escapedInterface = utils.escapeSystemdPath cfg.interface;
generateConfigFile = AP: pkgs.writeText "hostapd-${AP.interface}.conf" ''
interface=${AP.interface}
driver=${AP.driver}
ssid=${AP.ssid}
hw_mode=${AP.hwMode}
channel=${toString AP.channel}

configFile = pkgs.writeText "hostapd.conf" ''
interface=${cfg.interface}
driver=${cfg.driver}
ssid=${cfg.ssid}
hw_mode=${cfg.hwMode}
channel=${toString cfg.channel}
${optionalString (AP.bridge != null) "bridge=${AP.bridge}"}

# logging (debug level)
logger_syslog=-1
Expand All @@ -28,17 +29,35 @@ let
logger_stdout_level=2

ctrl_interface=/run/hostapd
ctrl_interface_group=${cfg.group}
ctrl_interface_group=${AP.group}

${optionalString cfg.wpa ''
${optionalString AP.wpa ''
wpa=2
wpa_passphrase=${cfg.wpaPassphrase}
wpa_passphrase=${AP.wpaPassphrase}
''}
${optionalString cfg.noScan "noscan=1"}

${cfg.extraConfig}
${optionalString AP.noScan "noscan=1"}

${AP.extraConfig}
'' ;

generateUnit = AP: let
escapedInterfaceName = utils.escapeSystemdPath (if AP.bridge != null then AP.bridge else AP.interface);
in nameValuePair "hostapd-${escapedInterfaceName}" {
description = "hostapd wireless AP on ${AP.interface}";

path = [ pkgs.hostapd ];

after = [ "sys-subsystem-net-devices-${escapedInterfaceName}.device" "${escapedInterfaceName}-cfg.service" "network.target" ];
bindsTo = [ "sys-subsystem-net-devices-${escapedInterfaceName}.device" ];

serviceConfig = {
ExecStart = "${pkgs.hostapd}/bin/hostapd ${generateConfigFile AP}";
Restart = "always";
};
};


in

{
Expand All @@ -62,97 +81,120 @@ in
'';
};

interface = mkOption {
default = "";
example = "wlp2s0";
APs = mkOption {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I'm so happy with the APs = [] part - it's quite unintuitive compared to how the rest of the module system looks like, and hostapd too only knows about bssids and interfaces, not APs.

Can we make this similar to networking.supplicant? Having the description in services.hostapd, and a services.hostapd.<(interface_)name>.*, like networking.supplicant.<name>.*?

From my understanding, there can only be one hostapd process per physical interface, and configuring multiple bssids would be part of the per-interface config file, which would need to duplicated for dual/multi-band setups.

default = [];
description = ''
The interfaces <command>hostapd</command> will use.
A list of complete hostapd configurations (one per network interface/AP
you want to run).
'';
};

noScan = mkOption {
default = false;
description = ''
Do not scan for overlapping BSSs in HT40+/- mode.
Caution: turning this on will violate regulatory requirements!
'';
};

driver = mkOption {
default = "nl80211";
example = "hostapd";
type = types.string;
description = ''
Which driver <command>hostapd</command> will use.
Most applications will probably use the default.
'';
};

ssid = mkOption {
default = "nixos";
example = "mySpecialSSID";
type = types.string;
description = "SSID to be used in IEEE 802.11 management frames.";
};

hwMode = mkOption {
default = "g";
type = types.enum [ "a" "b" "g" ];
description = ''
Operation mode.
(a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g).
'';
};

channel = mkOption {
default = 7;
example = 11;
type = types.int;
description = ''
Channel number (IEEE 802.11)
Please note that some drivers do not use this value from
<command>hostapd</command> and the channel will need to be configured
separately with <command>iwconfig</command>.
'';
};

group = mkOption {
default = "wheel";
example = "network";
type = types.string;
description = ''
Members of this group can control <command>hostapd</command>.
'';
};

wpa = mkOption {
default = true;
description = ''
Enable WPA (IEEE 802.11i/D3.0) to authenticate with the access point.
'';
};

wpaPassphrase = mkOption {
default = "my_sekret";
example = "any_64_char_string";
type = types.string;
description = ''
WPA-PSK (pre-shared-key) passphrase. Clients will need this
passphrase to associate with this access point.
Warning: This passphrase will get put into a world-readable file in
the Nix store!
'';
};

extraConfig = mkOption {
default = "";
example = ''
auth_algo=0
ieee80211n=1
ht_capab=[HT40-][SHORT-GI-40][DSSS_CCK-40]
'';
type = types.lines;
description = "Extra configuration options to put in hostapd.conf.";
type = with types; listOf (submodule {

options = {

interface = mkOption {
default = "";
example = "wlp2s0";
description = ''
The interfaces <command>hostapd</command> will use.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be singular.

'';
};

noScan = mkOption {
default = false;
description = ''
Do not scan for overlapping BSSs in HT40+/- mode.
Caution: turning this on will violate regulatory requirements!
'';
};

bridge = mkOption {
default = null;
type = types.nullOr types.str;
example = "br0";
description = ''
The (optional) name of the bridge to add this AP to.
'';
};

driver = mkOption {
default = "nl80211";
example = "hostapd";
type = types.string;
description = ''
Which driver <command>hostapd</command> will use.
Most applications will probably use the default.
'';
};

ssid = mkOption {
default = "nixos";
example = "mySpecialSSID";
type = types.string;
description = "SSID to be used in IEEE 802.11 management frames.";
};

hwMode = mkOption {
default = "g";
type = types.enum [ "a" "b" "g" ];
description = ''
Operation mode.
(a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g).
'';
};

channel = mkOption {
default = 7;
example = 11;
type = types.int;
description = ''
Channel number (IEEE 802.11)
Please note that some drivers do not use this value from
<command>hostapd</command> and the channel will need to be configured
separately with <command>iwconfig</command>.
'';
};

group = mkOption {
default = "wheel";
example = "network";
type = types.string;
description = ''
Members of this group can control <command>hostapd</command>.
'';
};

wpa = mkOption {
default = true;
description = ''
Enable WPA (IEEE 802.11i/D3.0) to authenticate with the access point.
'';
};

wpaPassphrase = mkOption {
default = "my_sekret";
example = "any_64_char_string";
type = types.string;
description = ''
WPA-PSK (pre-shared-key) passphrase. Clients will need this
passphrase to associate with this access point.
Warning: This passphrase will get put into a world-readable file in
the Nix store!
'';
};

extraConfig = mkOption {
default = "";
example = ''
auth_algo=0
ieee80211n=1
ht_capab=[HT40-][SHORT-GI-40][DSSS_CCK-40]
'';
type = types.lines;
description = "Extra configuration options to put in hostapd.conf.";
};
};
});
};
};
};
Expand All @@ -164,19 +206,6 @@ in

environment.systemPackages = [ pkgs.hostapd ];

systemd.services.hostapd =
{ description = "hostapd wireless AP";

path = [ pkgs.hostapd ];
after = [ "sys-subsystem-net-devices-${escapedInterface}.device" ];
bindsTo = [ "sys-subsystem-net-devices-${escapedInterface}.device" ];
requiredBy = [ "network-link-${cfg.interface}.service" ];
wantedBy = [ "multi-user.target" ];

serviceConfig =
{ ExecStart = "${pkgs.hostapd}/bin/hostapd ${configFile}";
Restart = "always";
};
};
systemd.services = listToAttrs (map generateUnit cfg.APs);
};
}