{ config, lib, pkgs, ... }:
# TODO:
#
# asserts
# ensure that the nl80211 module is loaded/compiled in the kernel
# wpa_supplicant and hostapd on the same wireless interface doesn't make any sense
with lib;
let
cfg = config.services.hostapd;
configFile = pkgs.writeText "hostapd.conf" ''
interface=${cfg.interface}
driver=${cfg.driver}
ssid=${cfg.ssid}
hw_mode=${cfg.hwMode}
channel=${toString cfg.channel}
# logging (debug level)
logger_syslog=-1
logger_syslog_level=2
logger_stdout=-1
logger_stdout_level=2
ctrl_interface=/var/run/hostapd
ctrl_interface_group=${cfg.group}
${if cfg.wpa then ''
wpa=1
wpa_passphrase=${cfg.wpaPassphrase}
'' else ""}
${cfg.extraConfig}
'' ;
in
{
###### interface
options = {
services.hostapd = {
enable = mkOption {
default = false;
description = ''
Enable putting a wireless interface into infrastructure mode,
allowing other wireless devices to associate with the wireless
interface and do wireless networking. A simple access point will
,
, and
, as well as DHCP on the wireless
interface to provide IP addresses to the associated stations, and
NAT (from the wireless interface to an upstream interface).
'';
};
interface = mkOption {
default = "";
example = "wlp2s0";
description = ''
The interfaces hostapd will use.
'';
};
driver = mkOption {
default = "nl80211";
example = "hostapd";
type = types.string;
description = ''
Which driver hostapd 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
hostapd and the channel will need to be configured
separately with iwconfig.
'';
};
group = mkOption {
default = "wheel";
example = "network";
type = types.string;
description = ''
Members of this group can control hostapd.
'';
};
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.";
};
};
};
###### implementation
config = mkIf cfg.enable {
assertions = [
{ assertion = (cfg.channel >= 1 && cfg.channel <= 13);
message = "channel must be between 1 and 13";
}];
environment.systemPackages = [ pkgs.hostapd ];
systemd.services.hostapd =
{ description = "hostapd wireless AP";
path = [ pkgs.hostapd ];
wantedBy = [ "network.target" ];
after = [ "${cfg.interface}-cfg.service" "nat.service" "bind.service" "dhcpd.service" "sys-subsystem-net-devices-${cfg.interface}.device" ];
serviceConfig =
{ ExecStart = "${pkgs.hostapd}/bin/hostapd ${configFile}";
Restart = "always";
};
};
};
}