1{ config, lib, pkgs, ... }:
2
3# TODO:
4#
5# asserts
6# ensure that the nl80211 module is loaded/compiled in the kernel
7# wpa_supplicant and hostapd on the same wireless interface doesn't make any sense
8
9with lib;
10
11let
12
13 cfg = config.services.hostapd;
14
15 configFile = pkgs.writeText "hostapd.conf" ''
16 interface=${cfg.interface}
17 driver=${cfg.driver}
18 ssid=${cfg.ssid}
19 hw_mode=${cfg.hwMode}
20 channel=${toString cfg.channel}
21
22 # logging (debug level)
23 logger_syslog=-1
24 logger_syslog_level=2
25 logger_stdout=-1
26 logger_stdout_level=2
27
28 ctrl_interface=/var/run/hostapd
29 ctrl_interface_group=${cfg.group}
30
31 ${if cfg.wpa then ''
32 wpa=1
33 wpa_passphrase=${cfg.wpaPassphrase}
34 '' else ""}
35
36 ${cfg.extraConfig}
37 '' ;
38
39in
40
41{
42 ###### interface
43
44 options = {
45
46 services.hostapd = {
47
48 enable = mkOption {
49 default = false;
50 description = ''
51 Enable putting a wireless interface into infrastructure mode,
52 allowing other wireless devices to associate with the wireless
53 interface and do wireless networking. A simple access point will
54 <option>enable hostapd.wpa</option>,
55 <option>hostapd.wpaPassphrase</option>, and
56 <option>hostapd.ssid</option>, as well as DHCP on the wireless
57 interface to provide IP addresses to the associated stations, and
58 NAT (from the wireless interface to an upstream interface).
59 '';
60 };
61
62 interface = mkOption {
63 default = "";
64 example = "wlp2s0";
65 description = ''
66 The interfaces <command>hostapd</command> will use.
67 '';
68 };
69
70 driver = mkOption {
71 default = "nl80211";
72 example = "hostapd";
73 type = types.string;
74 description = ''
75 Which driver <command>hostapd</command> will use.
76 Most applications will probably use the default.
77 '';
78 };
79
80 ssid = mkOption {
81 default = "nixos";
82 example = "mySpecialSSID";
83 type = types.string;
84 description = "SSID to be used in IEEE 802.11 management frames.";
85 };
86
87 hwMode = mkOption {
88 default = "g";
89 type = types.string;
90 description = ''
91 Operation mode.
92 (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g).
93 '';
94 };
95
96 channel = mkOption {
97 default = 7;
98 example = 11;
99 type = types.int;
100 description = ''
101 Channel number (IEEE 802.11)
102 Please note that some drivers do not use this value from
103 <command>hostapd</command> and the channel will need to be configured
104 separately with <command>iwconfig</command>.
105 '';
106 };
107
108 group = mkOption {
109 default = "wheel";
110 example = "network";
111 type = types.string;
112 description = ''
113 Members of this group can control <command>hostapd</command>.
114 '';
115 };
116
117 wpa = mkOption {
118 default = true;
119 description = ''
120 Enable WPA (IEEE 802.11i/D3.0) to authenticate with the access point.
121 '';
122 };
123
124 wpaPassphrase = mkOption {
125 default = "my_sekret";
126 example = "any_64_char_string";
127 type = types.string;
128 description = ''
129 WPA-PSK (pre-shared-key) passphrase. Clients will need this
130 passphrase to associate with this access point.
131 Warning: This passphrase will get put into a world-readable file in
132 the Nix store!
133 '';
134 };
135
136 extraConfig = mkOption {
137 default = "";
138 example = ''
139 auth_algo=0
140 ieee80211n=1
141 ht_capab=[HT40-][SHORT-GI-40][DSSS_CCK-40]
142 '';
143 type = types.string;
144 description = "Extra configuration options to put in hostapd.conf.";
145 };
146 };
147 };
148
149
150 ###### implementation
151
152 config = mkIf cfg.enable {
153
154 assertions = [
155 { assertion = (cfg.hwMode == "a" || cfg.hwMode == "b" || cfg.hwMode == "g");
156 message = "hwMode must be a/b/g";
157 }
158 { assertion = (cfg.channel >= 1 && cfg.channel <= 13);
159 message = "channel must be between 1 and 13";
160 }];
161
162 environment.systemPackages = [ pkgs.hostapd ];
163
164 systemd.services.hostapd =
165 { description = "hostapd wireless AP";
166
167 path = [ pkgs.hostapd ];
168 wantedBy = [ "network.target" ];
169
170 after = [ "${cfg.interface}-cfg.service" "nat.service" "bind.service" "dhcpd.service"];
171
172 serviceConfig =
173 { ExecStart = "${pkgs.hostapd}/bin/hostapd ${configFile}";
174 Restart = "always";
175 };
176 };
177 };
178}