Merge pull request #2191 from Ansuel/zram
[oweals/luci.git] / modules / luci-mod-network / luasrc / model / cbi / admin_network / wifi.lua
1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Licensed to the public under the Apache License 2.0.
3
4 local wa = require "luci.tools.webadmin"
5 local nw = require "luci.model.network"
6 local ut = require "luci.util"
7 local nt = require "luci.sys".net
8 local fs = require "nixio.fs"
9
10 local acct_port, acct_secret, acct_server, anonymous_identity, ant1, ant2,
11         auth, auth_port, auth_secret, auth_server, bssid, cacert, cacert2,
12         cc, ch, cipher, clientcert, clientcert2, ea, eaptype, en, encr,
13         ft_protocol, ft_psk_generate_local, hidden, htmode, identity,
14         ieee80211r, ieee80211w, ifname, isolate, key_retries,
15         legacyrates, max_timeout, meshfwd, meshid, ml, mobility_domain, mode,
16         mp, nasid, network, password, pmk_r1_push, privkey, privkey2, privkeypwd,
17         privkeypwd2, r0_key_lifetime, r0kh, r1_key_holder, r1kh,
18         reassociation_deadline, retry_timeout, ssid, st, tp, wepkey, wepslot,
19         wmm, wpakey, wps, disassoc_low_ack, short_preamble, beacon_int, dtim_period,
20         wparekey, inactivitypool, maxinactivity, listeninterval
21
22 arg[1] = arg[1] or ""
23
24 m = Map("wireless", "",
25         translate("The <em>Device Configuration</em> section covers physical settings of the radio " ..
26                 "hardware such as channel, transmit power or antenna selection which are shared among all " ..
27                 "defined wireless networks (if the radio hardware is multi-SSID capable). Per network settings " ..
28                 "like encryption or operation mode are grouped in the <em>Interface Configuration</em>."))
29
30 m:chain("network")
31 m:chain("firewall")
32 m.redirect = luci.dispatcher.build_url("admin/network/wireless")
33
34 nw.init(m.uci)
35
36 local wnet = nw:get_wifinet(arg[1])
37 local wdev = wnet and wnet:get_device()
38
39 -- redirect to overview page if network does not exist anymore (e.g. after a revert)
40 if not wnet or not wdev then
41         luci.http.redirect(luci.dispatcher.build_url("admin/network/wireless"))
42         return
43 end
44
45 local function txpower_list(iw)
46         local list = iw.txpwrlist or { }
47         local off  = tonumber(iw.txpower_offset) or 0
48         local new  = { }
49         local prev = -1
50         local _, val
51         for _, val in ipairs(list) do
52                 local dbm = val.dbm + off
53                 local mw  = math.floor(10 ^ (dbm / 10))
54                 if mw ~= prev then
55                         prev = mw
56                         new[#new+1] = {
57                                 display_dbm = dbm,
58                                 display_mw  = mw,
59                                 driver_dbm  = val.dbm,
60                                 driver_mw   = val.mw
61                         }
62                 end
63         end
64         return new
65 end
66
67 local function txpower_current(pwr, list)
68         pwr = tonumber(pwr)
69         if pwr ~= nil then
70                 local _, item
71                 for _, item in ipairs(list) do
72                         if item.driver_dbm >= pwr then
73                                 return item.driver_dbm
74                         end
75                 end
76         end
77         return pwr or ""
78 end
79
80 local iw = luci.sys.wifi.getiwinfo(arg[1])
81 local hw_modes      = iw.hwmodelist or { }
82 local tx_power_list = txpower_list(iw)
83 local tx_power_cur  = txpower_current(wdev:get("txpower"), tx_power_list)
84
85 -- wireless toggle was requested, commit and reload page
86 function m.parse(map)
87         local new_cc = m:formvalue("cbid.wireless.%s.country" % wdev:name())
88         local old_cc = m:get(wdev:name(), "country")
89
90         if m:formvalue("cbid.wireless.%s.__toggle" % wdev:name()) then
91                 if wdev:get("disabled") == "1" or wnet:get("disabled") == "1" then
92                         wnet:set("disabled", nil)
93                 else
94                         wnet:set("disabled", "1")
95                 end
96                 wdev:set("disabled", nil)
97                 m.apply_needed = true
98                 m.redirect = nil
99         end
100
101         Map.parse(map)
102
103         if m:get(wdev:name(), "type") == "mac80211" and new_cc and new_cc ~= old_cc then
104                 luci.sys.call("iw reg set %s" % ut.shellquote(new_cc))
105
106                 local old_ch = tonumber(m:formvalue("cbid.wireless.%s._mode_freq.channel" % wdev:name()) or "")
107                 if old_ch then
108                         local _, c, new_ch
109                         for _, c in ipairs(iw.freqlist) do
110                                 if c.channel > old_ch or (old_ch <= 14 and c.channel > 14) then
111                                         break
112                                 end
113                                 new_ch = c.channel
114                         end
115                         if new_ch ~= old_ch then
116                                 wdev:set("channel", new_ch)
117                                 m.message = translatef("Channel %d is not available in the %s regulatory domain and has been auto-adjusted to %d.",
118                                         old_ch, new_cc, new_ch)
119                         end
120                 end
121         end
122
123         if wdev:get("disabled") == "1" or wnet:get("disabled") == "1" then
124                 en.title      = translate("Wireless network is disabled")
125                 en.inputtitle = translate("Enable")
126                 en.inputstyle = "apply"
127         else
128                 en.title      = translate("Wireless network is enabled")
129                 en.inputtitle = translate("Disable")
130                 en.inputstyle = "reset"
131         end
132 end
133
134 m.title = luci.util.pcdata(wnet:get_i18n())
135
136 s = m:section(NamedSection, wdev:name(), "wifi-device", translate("Device Configuration"))
137 s.addremove = false
138
139 s:tab("general", translate("General Setup"))
140 s:tab("macfilter", translate("MAC-Filter"))
141 s:tab("advanced", translate("Advanced Settings"))
142
143 st = s:taboption("general", DummyValue, "__status", translate("Status"))
144 st.template = "admin_network/wifi_status"
145 st.ifname   = arg[1]
146
147 en = s:taboption("general", Button, "__toggle")
148
149 local hwtype = wdev:get("type")
150
151 -- NanoFoo
152 local nsantenna = wdev:get("antenna")
153
154 -- Check whether there are client interfaces on the same radio,
155 -- if yes, lock the channel choice as these stations will dicatate the freq
156 local found_sta = nil
157 local _, net
158 if wnet:mode() ~= "sta" then
159         for _, net in ipairs(wdev:get_wifinets()) do
160                 if net:mode() == "sta" and net:get("disabled") ~= "1" then
161                         if not found_sta then
162                                 found_sta = {}
163                                 found_sta.channel = net:channel()
164                                 found_sta.names = {}
165                         end
166                         found_sta.names[#found_sta.names+1] = net:shortname()
167                 end
168         end
169 end
170
171 if found_sta then
172         ch = s:taboption("general", DummyValue, "choice", translate("Channel"))
173         ch.value = translatef("Locked to channel %s used by: %s",
174                 found_sta.channel or "(auto)", table.concat(found_sta.names, ", "))
175 else
176         ch = s:taboption("general", Value, "_mode_freq", '<br />'..translate("Operating frequency"))
177         ch.iwinfo = iw
178         ch.template = "cbi/wireless_modefreq"
179
180         function ch.cfgvalue(self, section)
181                 return {
182                         m:get(section, "hwmode") or "",
183                         m:get(section, "channel") or "auto",
184                         m:get(section, "htmode") or ""
185                 }
186         end
187
188         function ch.formvalue(self, section)
189                 return {
190                         m:formvalue(self:cbid(section) .. ".band") or (hw_modes.g and "11g" or "11a"),
191                         m:formvalue(self:cbid(section) .. ".channel") or "auto",
192                         m:formvalue(self:cbid(section) .. ".htmode") or ""
193                 }
194         end
195
196         function ch.write(self, section, value)
197                 m:set(section, "hwmode", value[1])
198                 m:set(section, "channel", value[2])
199                 m:set(section, "htmode", value[3])
200         end
201 end
202
203 ------------------- MAC80211 Device ------------------
204
205 if hwtype == "mac80211" then
206         if #tx_power_list > 0 then
207                 tp = s:taboption("general", ListValue,
208                         "txpower", translate("Transmit Power"), "dBm")
209                 tp.rmempty = true
210                 tp.default = tx_power_cur
211                 function tp.cfgvalue(...)
212                         return txpower_current(Value.cfgvalue(...), tx_power_list)
213                 end
214
215                 tp:value("", translate("auto"))
216                 for _, p in ipairs(tx_power_list) do
217                         tp:value(p.driver_dbm, "%i dBm (%i mW)"
218                                 %{ p.display_dbm, p.display_mw })
219                 end
220         end
221
222         local cl = iw and iw.countrylist
223         if cl and #cl > 0 then
224                 cc = s:taboption("advanced", ListValue, "country", translate("Country Code"), translate("Use ISO/IEC 3166 alpha2 country codes."))
225                 cc.default = tostring(iw and iw.country or "00")
226                 for _, c in ipairs(cl) do
227                         cc:value(c.alpha2, "%s - %s" %{ c.alpha2, c.name })
228                 end
229         else
230                 s:taboption("advanced", Value, "country", translate("Country Code"), translate("Use ISO/IEC 3166 alpha2 country codes."))
231         end
232
233         legacyrates = s:taboption("advanced", Flag, "legacy_rates", translate("Allow legacy 802.11b rates"))
234         legacyrates.rmempty = false
235         legacyrates.default = "1"
236
237         s:taboption("advanced", Value, "distance", translate("Distance Optimization"),
238                 translate("Distance to farthest network member in meters."))
239
240         -- external antenna profiles
241         local eal = iw and iw.extant
242         if eal and #eal > 0 then
243                 ea = s:taboption("advanced", ListValue, "extant", translate("Antenna Configuration"))
244                 for _, eap in ipairs(eal) do
245                         ea:value(eap.id, "%s (%s)" %{ eap.name, eap.description })
246                         if eap.selected then
247                                 ea.default = eap.id
248                         end
249                 end
250         end
251
252         s:taboption("advanced", Value, "frag", translate("Fragmentation Threshold"))
253         s:taboption("advanced", Value, "rts", translate("RTS/CTS Threshold"))
254         
255         s:taboption("advanced", Flag, "noscan", translate("Force 40MHz mode"),
256                 translate("Always use 40MHz channels even if the secondary channel overlaps. Using this option does not comply with IEEE 802.11n-2009!")).optional = true
257
258         beacon_int = s:taboption("advanced", Value, "beacon_int", translate("Beacon Interval"))
259         beacon_int.optional = true
260         beacon_int.placeholder = 100
261         beacon_int.datatype = "range(15,65535)"
262 end
263
264
265 ------------------- Broadcom Device ------------------
266
267 if hwtype == "broadcom" then
268         tp = s:taboption("general",
269                 (#tx_power_list > 0) and ListValue or Value,
270                 "txpower", translate("Transmit Power"), "dBm")
271
272         tp.rmempty = true
273         tp.default = tx_power_cur
274
275         function tp.cfgvalue(...)
276                 return txpower_current(Value.cfgvalue(...), tx_power_list)
277         end
278
279         tp:value("", translate("auto"))
280         for _, p in ipairs(tx_power_list) do
281                 tp:value(p.driver_dbm, "%i dBm (%i mW)"
282                         %{ p.display_dbm, p.display_mw })
283         end
284
285         mode = s:taboption("advanced", ListValue, "hwmode", translate("Band"))
286         if hw_modes.b then
287                 mode:value("11b", "2.4GHz (802.11b)")
288                 if hw_modes.g then
289                         mode:value("11bg", "2.4GHz (802.11b+g)")
290                 end
291         end
292         if hw_modes.g then
293                 mode:value("11g", "2.4GHz (802.11g)")
294                 mode:value("11gst", "2.4GHz (802.11g + Turbo)")
295                 mode:value("11lrs", "2.4GHz (802.11g Limited Rate Support)")
296         end
297         if hw_modes.a then mode:value("11a", "5GHz (802.11a)") end
298         if hw_modes.n then
299                 if hw_modes.g then
300                         mode:value("11ng", "2.4GHz (802.11g+n)")
301                         mode:value("11n", "2.4GHz (802.11n)")
302                 end
303                 if hw_modes.a then
304                         mode:value("11na", "5GHz (802.11a+n)")
305                         mode:value("11n", "5GHz (802.11n)")
306                 end
307                 htmode = s:taboption("advanced", ListValue, "htmode", translate("HT mode (802.11n)"))
308                 htmode:depends("hwmode", "11ng")
309                 htmode:depends("hwmode", "11na")
310                 htmode:depends("hwmode", "11n")
311                 htmode:value("HT20", "20MHz")
312                 htmode:value("HT40", "40MHz")
313         end
314
315         ant1 = s:taboption("advanced", ListValue, "txantenna", translate("Transmitter Antenna"))
316         ant1.widget = "radio"
317         ant1:depends("diversity", "")
318         ant1:value("3", translate("auto"))
319         ant1:value("0", translate("Antenna 1"))
320         ant1:value("1", translate("Antenna 2"))
321
322         ant2 = s:taboption("advanced", ListValue, "rxantenna", translate("Receiver Antenna"))
323         ant2.widget = "radio"
324         ant2:depends("diversity", "")
325         ant2:value("3", translate("auto"))
326         ant2:value("0", translate("Antenna 1"))
327         ant2:value("1", translate("Antenna 2"))
328
329         s:taboption("advanced", Flag, "frameburst", translate("Frame Bursting"))
330
331         s:taboption("advanced", Value, "distance", translate("Distance Optimization"))
332         --s:option(Value, "slottime", translate("Slot time"))
333
334         s:taboption("advanced", Value, "country", translate("Country Code"))
335         s:taboption("advanced", Value, "maxassoc", translate("Connection Limit"))
336 end
337
338
339 --------------------- HostAP Device ---------------------
340
341 if hwtype == "prism2" then
342         s:taboption("advanced", Value, "txpower", translate("Transmit Power"), "att units").rmempty = true
343
344         s:taboption("advanced", Flag, "diversity", translate("Diversity")).rmempty = false
345
346         s:taboption("advanced", Value, "txantenna", translate("Transmitter Antenna"))
347         s:taboption("advanced", Value, "rxantenna", translate("Receiver Antenna"))
348 end
349
350
351 ----------------------- Interface -----------------------
352
353 s = m:section(NamedSection, wnet.sid, "wifi-iface", translate("Interface Configuration"))
354 s.addremove = false
355 s.anonymous = true
356 s.defaults.device = wdev:name()
357
358 s:tab("general", translate("General Setup"))
359 s:tab("encryption", translate("Wireless Security"))
360 s:tab("macfilter", translate("MAC-Filter"))
361 s:tab("advanced", translate("Advanced Settings"))
362
363 mode = s:taboption("general", ListValue, "mode", translate("Mode"))
364 mode.override_values = true
365 mode:value("ap", translate("Access Point"))
366 mode:value("sta", translate("Client"))
367 mode:value("adhoc", translate("Ad-Hoc"))
368
369 meshid = s:taboption("general", Value, "mesh_id", translate("Mesh Id"))
370 meshid:depends({mode="mesh"})
371
372 meshfwd = s:taboption("advanced", Flag, "mesh_fwding", translate("Forward mesh peer traffic"))
373 meshfwd.rmempty = false
374 meshfwd.default = "1"
375 meshfwd:depends({mode="mesh"})
376
377 ssid = s:taboption("general", Value, "ssid", translate("<abbr title=\"Extended Service Set Identifier\">ESSID</abbr>"))
378 ssid.datatype = "maxlength(32)"
379 ssid:depends({mode="ap"})
380 ssid:depends({mode="sta"})
381 ssid:depends({mode="adhoc"})
382 ssid:depends({mode="ahdemo"})
383 ssid:depends({mode="monitor"})
384 ssid:depends({mode="ap-wds"})
385 ssid:depends({mode="sta-wds"})
386 ssid:depends({mode="wds"})
387
388 bssid = s:taboption("general", Value, "bssid", translate("<abbr title=\"Basic Service Set Identifier\">BSSID</abbr>"))
389
390 network = s:taboption("general", Value, "network", translate("Network"),
391         translate("Choose the network(s) you want to attach to this wireless interface or " ..
392                 "fill out the <em>create</em> field to define a new network."))
393
394 network.rmempty = true
395 network.template = "cbi/network_netlist"
396 network.widget = "checkbox"
397 network.novirtual = true
398
399 function network.write(self, section, value)
400         local i = nw:get_interface(section)
401         if i then
402                 local _, net, old, new = nil, nil, {}, {}
403
404                 for _, net in ipairs(i:get_networks()) do
405                         old[net:name()] = true
406                 end
407
408                 for net in ut.imatch(value) do
409                         new[net] = true
410                         if not old[net] then
411                                 local n = nw:get_network(net) or nw:add_network(net, { proto = "none" })
412                                 if n then
413                                         if not n:is_empty() then
414                                                 n:set("type", "bridge")
415                                         end
416                                         n:add_interface(i)
417                                 end
418                         end
419                 end
420
421                 for net, _ in pairs(old) do
422                         if not new[net] then
423                                 local n = nw:get_network(net)
424                                 if n then
425                                         n:del_interface(i)
426                                 end
427                         end
428                 end
429         end
430 end
431
432 -------------------- MAC80211 Interface ----------------------
433
434 if hwtype == "mac80211" then
435         if fs.access("/usr/sbin/iw") then
436                 mode:value("mesh", "802.11s")
437         end
438
439         mode:value("ahdemo", translate("Pseudo Ad-Hoc (ahdemo)"))
440         mode:value("monitor", translate("Monitor"))
441         bssid:depends({mode="adhoc"})
442         bssid:depends({mode="sta"})
443         bssid:depends({mode="sta-wds"})
444
445         mp = s:taboption("macfilter", ListValue, "macfilter", translate("MAC-Address Filter"))
446         mp:depends({mode="ap"})
447         mp:depends({mode="ap-wds"})
448         mp:value("", translate("disable"))
449         mp:value("allow", translate("Allow listed only"))
450         mp:value("deny", translate("Allow all except listed"))
451
452         ml = s:taboption("macfilter", DynamicList, "maclist", translate("MAC-List"))
453         ml.datatype = "macaddr"
454         ml:depends({macfilter="allow"})
455         ml:depends({macfilter="deny"})
456         nt.mac_hints(function(mac, name) ml:value(mac, "%s (%s)" %{ mac, name }) end)
457
458         mode:value("ap-wds", "%s (%s)" % {translate("Access Point"), translate("WDS")})
459         mode:value("sta-wds", "%s (%s)" % {translate("Client"), translate("WDS")})
460
461         function mode.write(self, section, value)
462                 if value == "ap-wds" then
463                         ListValue.write(self, section, "ap")
464                         m.uci:set("wireless", section, "wds", 1)
465                 elseif value == "sta-wds" then
466                         ListValue.write(self, section, "sta")
467                         m.uci:set("wireless", section, "wds", 1)
468                 else
469                         ListValue.write(self, section, value)
470                         m.uci:delete("wireless", section, "wds")
471                 end
472         end
473
474         function mode.cfgvalue(self, section)
475                 local mode = ListValue.cfgvalue(self, section)
476                 local wds  = m.uci:get("wireless", section, "wds") == "1"
477
478                 if mode == "ap" and wds then
479                         return "ap-wds"
480                 elseif mode == "sta" and wds then
481                         return "sta-wds"
482                 else
483                         return mode
484                 end
485         end
486
487         hidden = s:taboption("general", Flag, "hidden", translate("Hide <abbr title=\"Extended Service Set Identifier\">ESSID</abbr>"))
488         hidden:depends({mode="ap"})
489         hidden:depends({mode="ap-wds"})
490
491         wmm = s:taboption("general", Flag, "wmm", translate("WMM Mode"))
492         wmm:depends({mode="ap"})
493         wmm:depends({mode="ap-wds"})
494         wmm.default = wmm.enabled
495
496         isolate = s:taboption("advanced", Flag, "isolate", translate("Isolate Clients"),
497          translate("Prevents client-to-client communication"))
498         isolate:depends({mode="ap"})
499         isolate:depends({mode="ap-wds"})
500
501         ifname = s:taboption("advanced", Value, "ifname", translate("Interface name"), translate("Override default interface name"))
502         ifname.optional = true
503
504         short_preamble = s:taboption("advanced", Flag, "short_preamble", translate("Short Preamble"))
505         short_preamble.default = short_preamble.enabled
506
507         dtim_period = s:taboption("advanced", Value, "dtim_period", translate("DTIM Interval"), translate("Delivery Traffic Indication Message Interval"))
508         dtim_period.optional = true
509         dtim_period.placeholder = 2
510         dtim_period.datatype = "range(1,255)"
511         
512         
513         wparekey = s:taboption("advanced", Value, "wpa_group_rekey", translate("Time interval for rekeying GTK"), translate("sec"))
514         wparekey.optional    = true
515         wparekey.placeholder = 600
516         wparekey.datatype    = "uinteger"
517         
518         inactivitypool = s:taboption("advanced", Flag , "skip_inactivity_poll", translate("Disable Inactivity Polling"))
519         inactivitypool.optional    = true
520         inactivitypool.datatype    = "uinteger"
521         
522         maxinactivity = s:taboption("advanced", Value, "max_inactivity", translate("Station inactivity limit"), translate("sec"))
523         maxinactivity.optional    = true
524         maxinactivity.placeholder = 300
525         maxinactivity.datatype    = "uinteger"
526         
527         listeninterval = s:taboption("advanced", Value, "max_listen_interval", translate("Maximum allowed Listen Interval"))
528         listeninterval.optional    = true
529         listeninterval.placeholder = 65535
530         listeninterval.datatype    = "uinteger"
531
532         disassoc_low_ack = s:taboption("advanced", Flag, "disassoc_low_ack", translate("Disassociate On Low Acknowledgement"),
533                 translate("Allow AP mode to disconnect STAs based on low ACK condition"))
534         disassoc_low_ack.default = disassoc_low_ack.enabled
535 end
536
537
538 -------------------- Broadcom Interface ----------------------
539
540 if hwtype == "broadcom" then
541         mode:value("wds", translate("WDS"))
542         mode:value("monitor", translate("Monitor"))
543
544         hidden = s:taboption("general", Flag, "hidden", translate("Hide <abbr title=\"Extended Service Set Identifier\">ESSID</abbr>"))
545         hidden:depends({mode="ap"})
546         hidden:depends({mode="adhoc"})
547         hidden:depends({mode="wds"})
548
549         isolate = s:taboption("advanced", Flag, "isolate", translate("Separate Clients"),
550          translate("Prevents client-to-client communication"))
551         isolate:depends({mode="ap"})
552
553         s:taboption("advanced", Flag, "doth", "802.11h")
554         s:taboption("advanced", Flag, "wmm", translate("WMM Mode"))
555
556         bssid:depends({mode="wds"})
557         bssid:depends({mode="adhoc"})
558 end
559
560
561 ----------------------- HostAP Interface ---------------------
562
563 if hwtype == "prism2" then
564         mode:value("wds", translate("WDS"))
565         mode:value("monitor", translate("Monitor"))
566
567         hidden = s:taboption("general", Flag, "hidden", translate("Hide <abbr title=\"Extended Service Set Identifier\">ESSID</abbr>"))
568         hidden:depends({mode="ap"})
569         hidden:depends({mode="adhoc"})
570         hidden:depends({mode="wds"})
571
572         bssid:depends({mode="sta"})
573
574         mp = s:taboption("macfilter", ListValue, "macpolicy", translate("MAC-Address Filter"))
575         mp:value("", translate("disable"))
576         mp:value("allow", translate("Allow listed only"))
577         mp:value("deny", translate("Allow all except listed"))
578         ml = s:taboption("macfilter", DynamicList, "maclist", translate("MAC-List"))
579         ml:depends({macpolicy="allow"})
580         ml:depends({macpolicy="deny"})
581         nt.mac_hints(function(mac, name) ml:value(mac, "%s (%s)" %{ mac, name }) end)
582
583         s:taboption("advanced", Value, "rate", translate("Transmission Rate"))
584         s:taboption("advanced", Value, "frag", translate("Fragmentation Threshold"))
585         s:taboption("advanced", Value, "rts", translate("RTS/CTS Threshold"))
586 end
587
588
589 ------------------- WiFI-Encryption -------------------
590
591 encr = s:taboption("encryption", ListValue, "encryption", translate("Encryption"))
592 encr.override_values = true
593 encr.override_depends = true
594 encr:depends({mode="ap"})
595 encr:depends({mode="sta"})
596 encr:depends({mode="adhoc"})
597 encr:depends({mode="ahdemo"})
598 encr:depends({mode="ap-wds"})
599 encr:depends({mode="sta-wds"})
600 encr:depends({mode="mesh"})
601
602 cipher = s:taboption("encryption", ListValue, "cipher", translate("Cipher"))
603 cipher:depends({encryption="wpa"})
604 cipher:depends({encryption="wpa2"})
605 cipher:depends({encryption="psk"})
606 cipher:depends({encryption="psk2"})
607 cipher:depends({encryption="wpa-mixed"})
608 cipher:depends({encryption="psk-mixed"})
609 cipher:value("auto", translate("auto"))
610 cipher:value("ccmp", translate("Force CCMP (AES)"))
611 cipher:value("tkip", translate("Force TKIP"))
612 cipher:value("tkip+ccmp", translate("Force TKIP and CCMP (AES)"))
613
614 function encr.cfgvalue(self, section)
615         local v = tostring(ListValue.cfgvalue(self, section))
616         if v == "wep" then
617                 return "wep-open"
618         elseif v and v:match("%+") then
619                 return (v:gsub("%+.+$", ""))
620         end
621         return v
622 end
623
624 function encr.write(self, section, value)
625         local e = tostring(encr:formvalue(section))
626         local c = tostring(cipher:formvalue(section))
627         if value == "wpa" or value == "wpa2"  then
628                 self.map.uci:delete("wireless", section, "key")
629         end
630         if e and (c == "tkip" or c == "ccmp" or c == "tkip+ccmp") then
631                 e = e .. "+" .. c
632         end
633         self.map:set(section, "encryption", e)
634 end
635
636 function cipher.cfgvalue(self, section)
637         local v = tostring(ListValue.cfgvalue(encr, section))
638         if v and v:match("%+") then
639                 v = v:gsub("^[^%+]+%+", "")
640                 if v == "aes" then v = "ccmp"
641                 elseif v == "tkip+aes" then v = "tkip+ccmp"
642                 elseif v == "aes+tkip" then v = "tkip+ccmp"
643                 elseif v == "ccmp+tkip" then v = "tkip+ccmp"
644                 end
645         end
646         return v
647 end
648
649 function cipher.write(self, section)
650         return encr:write(section)
651 end
652
653
654 encr:value("none", "No Encryption")
655 encr:value("wep-open",   translate("WEP Open System"), {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"}, {mode="adhoc"}, {mode="ahdemo"}, {mode="wds"})
656 encr:value("wep-shared", translate("WEP Shared Key"),  {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"}, {mode="adhoc"}, {mode="ahdemo"}, {mode="wds"})
657
658 if hwtype == "mac80211" or hwtype == "prism2" then
659         local supplicant = fs.access("/usr/sbin/wpa_supplicant")
660         local hostapd = fs.access("/usr/sbin/hostapd")
661
662         -- Probe EAP support
663         local has_ap_eap  = (os.execute("hostapd -veap >/dev/null 2>/dev/null") == 0)
664         local has_sta_eap = (os.execute("wpa_supplicant -veap >/dev/null 2>/dev/null") == 0)
665
666         if hostapd and supplicant then
667                 encr:value("psk", "WPA-PSK", {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"}, {mode="adhoc"})
668                 encr:value("psk2", "WPA2-PSK", {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"}, {mode="adhoc"})
669                 encr:value("psk-mixed", "WPA-PSK/WPA2-PSK Mixed Mode", {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"}, {mode="adhoc"})
670                 if has_ap_eap and has_sta_eap then
671                         encr:value("wpa", "WPA-EAP", {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"})
672                         encr:value("wpa2", "WPA2-EAP", {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"})
673                 end
674         elseif hostapd and not supplicant then
675                 encr:value("psk", "WPA-PSK", {mode="ap"}, {mode="ap-wds"})
676                 encr:value("psk2", "WPA2-PSK", {mode="ap"}, {mode="ap-wds"})
677                 encr:value("psk-mixed", "WPA-PSK/WPA2-PSK Mixed Mode", {mode="ap"}, {mode="ap-wds"})
678                 if has_ap_eap then
679                         encr:value("wpa", "WPA-EAP", {mode="ap"}, {mode="ap-wds"})
680                         encr:value("wpa2", "WPA2-EAP", {mode="ap"}, {mode="ap-wds"})
681                 end
682                 encr.description = translate(
683                         "WPA-Encryption requires wpa_supplicant (for client mode) or hostapd (for AP " ..
684                         "and ad-hoc mode) to be installed."
685                 )
686         elseif not hostapd and supplicant then
687                 encr:value("psk", "WPA-PSK", {mode="sta"}, {mode="sta-wds"}, {mode="adhoc"})
688                 encr:value("psk2", "WPA2-PSK", {mode="sta"}, {mode="sta-wds"}, {mode="adhoc"})
689                 encr:value("psk-mixed", "WPA-PSK/WPA2-PSK Mixed Mode", {mode="sta"}, {mode="sta-wds"}, {mode="adhoc"})
690                 if has_sta_eap then
691                         encr:value("wpa", "WPA-EAP", {mode="sta"}, {mode="sta-wds"})
692                         encr:value("wpa2", "WPA2-EAP", {mode="sta"}, {mode="sta-wds"})
693                 end
694                 encr.description = translate(
695                         "WPA-Encryption requires wpa_supplicant (for client mode) or hostapd (for AP " ..
696                         "and ad-hoc mode) to be installed."
697                 )
698         else
699                 encr.description = translate(
700                         "WPA-Encryption requires wpa_supplicant (for client mode) or hostapd (for AP " ..
701                         "and ad-hoc mode) to be installed."
702                 )
703         end
704 elseif hwtype == "broadcom" then
705         encr:value("psk", "WPA-PSK")
706         encr:value("psk2", "WPA2-PSK")
707         encr:value("psk+psk2", "WPA-PSK/WPA2-PSK Mixed Mode")
708 end
709
710 auth_server = s:taboption("encryption", Value, "auth_server", translate("Radius-Authentication-Server"))
711 auth_server:depends({mode="ap", encryption="wpa"})
712 auth_server:depends({mode="ap", encryption="wpa2"})
713 auth_server:depends({mode="ap-wds", encryption="wpa"})
714 auth_server:depends({mode="ap-wds", encryption="wpa2"})
715 auth_server.rmempty = true
716 auth_server.datatype = "host(0)"
717
718 auth_port = s:taboption("encryption", Value, "auth_port", translate("Radius-Authentication-Port"), translatef("Default %d", 1812))
719 auth_port:depends({mode="ap", encryption="wpa"})
720 auth_port:depends({mode="ap", encryption="wpa2"})
721 auth_port:depends({mode="ap-wds", encryption="wpa"})
722 auth_port:depends({mode="ap-wds", encryption="wpa2"})
723 auth_port.rmempty = true
724 auth_port.datatype = "port"
725
726 auth_secret = s:taboption("encryption", Value, "auth_secret", translate("Radius-Authentication-Secret"))
727 auth_secret:depends({mode="ap", encryption="wpa"})
728 auth_secret:depends({mode="ap", encryption="wpa2"})
729 auth_secret:depends({mode="ap-wds", encryption="wpa"})
730 auth_secret:depends({mode="ap-wds", encryption="wpa2"})
731 auth_secret.rmempty = true
732 auth_secret.password = true
733
734 acct_server = s:taboption("encryption", Value, "acct_server", translate("Radius-Accounting-Server"))
735 acct_server:depends({mode="ap", encryption="wpa"})
736 acct_server:depends({mode="ap", encryption="wpa2"})
737 acct_server:depends({mode="ap-wds", encryption="wpa"})
738 acct_server:depends({mode="ap-wds", encryption="wpa2"})
739 acct_server.rmempty = true
740 acct_server.datatype = "host(0)"
741
742 acct_port = s:taboption("encryption", Value, "acct_port", translate("Radius-Accounting-Port"), translatef("Default %d", 1813))
743 acct_port:depends({mode="ap", encryption="wpa"})
744 acct_port:depends({mode="ap", encryption="wpa2"})
745 acct_port:depends({mode="ap-wds", encryption="wpa"})
746 acct_port:depends({mode="ap-wds", encryption="wpa2"})
747 acct_port.rmempty = true
748 acct_port.datatype = "port"
749
750 acct_secret = s:taboption("encryption", Value, "acct_secret", translate("Radius-Accounting-Secret"))
751 acct_secret:depends({mode="ap", encryption="wpa"})
752 acct_secret:depends({mode="ap", encryption="wpa2"})
753 acct_secret:depends({mode="ap-wds", encryption="wpa"})
754 acct_secret:depends({mode="ap-wds", encryption="wpa2"})
755 acct_secret.rmempty = true
756 acct_secret.password = true
757
758 wpakey = s:taboption("encryption", Value, "_wpa_key", translate("Key"))
759 wpakey:depends("encryption", "psk")
760 wpakey:depends("encryption", "psk2")
761 wpakey:depends("encryption", "psk+psk2")
762 wpakey:depends("encryption", "psk-mixed")
763 wpakey.datatype = "wpakey"
764 wpakey.rmempty = true
765 wpakey.password = true
766
767 wpakey.cfgvalue = function(self, section, value)
768         local key = m.uci:get("wireless", section, "key")
769         if key == "1" or key == "2" or key == "3" or key == "4" then
770                 return nil
771         end
772         return key
773 end
774
775 wpakey.write = function(self, section, value)
776         self.map.uci:set("wireless", section, "key", value)
777         self.map.uci:delete("wireless", section, "key1")
778 end
779
780
781 wepslot = s:taboption("encryption", ListValue, "_wep_key", translate("Used Key Slot"))
782 wepslot:depends("encryption", "wep-open")
783 wepslot:depends("encryption", "wep-shared")
784 wepslot:value("1", translatef("Key #%d", 1))
785 wepslot:value("2", translatef("Key #%d", 2))
786 wepslot:value("3", translatef("Key #%d", 3))
787 wepslot:value("4", translatef("Key #%d", 4))
788
789 wepslot.cfgvalue = function(self, section)
790         local slot = tonumber(m.uci:get("wireless", section, "key"))
791         if not slot or slot < 1 or slot > 4 then
792                 return 1
793         end
794         return slot
795 end
796
797 wepslot.write = function(self, section, value)
798         self.map.uci:set("wireless", section, "key", value)
799 end
800
801 local slot
802 for slot=1,4 do
803         wepkey = s:taboption("encryption", Value, "key" .. slot, translatef("Key #%d", slot))
804         wepkey:depends("encryption", "wep-open")
805         wepkey:depends("encryption", "wep-shared")
806         wepkey.datatype = "wepkey"
807         wepkey.rmempty = true
808         wepkey.password = true
809
810         function wepkey.write(self, section, value)
811                 if value and (#value == 5 or #value == 13) then
812                         value = "s:" .. value
813                 end
814                 return Value.write(self, section, value)
815         end
816 end
817
818 if hwtype == "mac80211" or hwtype == "prism2" then
819
820         -- Probe 802.11r support (and EAP support as a proxy for Openwrt)
821         local has_80211r = (os.execute("hostapd -v11r 2>/dev/null || hostapd -veap 2>/dev/null") == 0)
822
823         ieee80211r = s:taboption("encryption", Flag, "ieee80211r",
824                 translate("802.11r Fast Transition"),
825                 translate("Enables fast roaming among access points that belong " ..
826                         "to the same Mobility Domain"))
827         ieee80211r:depends({mode="ap", encryption="wpa"})
828         ieee80211r:depends({mode="ap", encryption="wpa2"})
829         ieee80211r:depends({mode="ap-wds", encryption="wpa"})
830         ieee80211r:depends({mode="ap-wds", encryption="wpa2"})
831         if has_80211r then
832                 ieee80211r:depends({mode="ap", encryption="psk"})
833                 ieee80211r:depends({mode="ap", encryption="psk2"})
834                 ieee80211r:depends({mode="ap", encryption="psk-mixed"})
835                 ieee80211r:depends({mode="ap-wds", encryption="psk"})
836                 ieee80211r:depends({mode="ap-wds", encryption="psk2"})
837                 ieee80211r:depends({mode="ap-wds", encryption="psk-mixed"})
838         end
839         ieee80211r.rmempty = true
840
841         nasid = s:taboption("encryption", Value, "nasid", translate("NAS ID"),
842                 translate("Used for two different purposes: RADIUS NAS ID and " ..
843                         "802.11r R0KH-ID. Not needed with normal WPA(2)-PSK."))
844         nasid:depends({mode="ap", encryption="wpa"})
845         nasid:depends({mode="ap", encryption="wpa2"})
846         nasid:depends({mode="ap-wds", encryption="wpa"})
847         nasid:depends({mode="ap-wds", encryption="wpa2"})
848         nasid:depends({ieee80211r="1"})
849         nasid.rmempty = true
850
851         mobility_domain = s:taboption("encryption", Value, "mobility_domain",
852                         translate("Mobility Domain"),
853                         translate("4-character hexadecimal ID"))
854         mobility_domain:depends({ieee80211r="1"})
855         mobility_domain.placeholder = "4f57"
856         mobility_domain.datatype = "and(hexstring,rangelength(4,4))"
857         mobility_domain.rmempty = true
858
859         reassociation_deadline = s:taboption("encryption", Value, "reassociation_deadline",
860                 translate("Reassociation Deadline"),
861                 translate("time units (TUs / 1.024 ms) [1000-65535]"))
862         reassociation_deadline:depends({ieee80211r="1"})
863         reassociation_deadline.placeholder = "1000"
864         reassociation_deadline.datatype = "range(1000,65535)"
865         reassociation_deadline.rmempty = true
866
867         ft_protocol = s:taboption("encryption", ListValue, "ft_over_ds", translate("FT protocol"))
868         ft_protocol:depends({ieee80211r="1"})
869         ft_protocol:value("1", translatef("FT over DS"))
870         ft_protocol:value("0", translatef("FT over the Air"))
871         ft_protocol.rmempty = true
872
873         ft_psk_generate_local = s:taboption("encryption", Flag, "ft_psk_generate_local",
874                 translate("Generate PMK locally"),
875                 translate("When using a PSK, the PMK can be generated locally without inter AP communications"))
876         ft_psk_generate_local:depends({ieee80211r="1"})
877
878         r0_key_lifetime = s:taboption("encryption", Value, "r0_key_lifetime",
879                         translate("R0 Key Lifetime"), translate("minutes"))
880         r0_key_lifetime:depends({ieee80211r="1", ft_psk_generate_local=""})
881         r0_key_lifetime.placeholder = "10000"
882         r0_key_lifetime.datatype = "uinteger"
883         r0_key_lifetime.rmempty = true
884
885         r1_key_holder = s:taboption("encryption", Value, "r1_key_holder",
886                         translate("R1 Key Holder"),
887                         translate("6-octet identifier as a hex string - no colons"))
888         r1_key_holder:depends({ieee80211r="1", ft_psk_generate_local=""})
889         r1_key_holder.placeholder = "00004f577274"
890         r1_key_holder.datatype = "and(hexstring,rangelength(12,12))"
891         r1_key_holder.rmempty = true
892
893         pmk_r1_push = s:taboption("encryption", Flag, "pmk_r1_push", translate("PMK R1 Push"))
894         pmk_r1_push:depends({ieee80211r="1", ft_psk_generate_local=""})
895         pmk_r1_push.placeholder = "0"
896         pmk_r1_push.rmempty = true
897
898         r0kh = s:taboption("encryption", DynamicList, "r0kh", translate("External R0 Key Holder List"),
899                 translate("List of R0KHs in the same Mobility Domain. " ..
900                         "<br />Format: MAC-address,NAS-Identifier,128-bit key as hex string. " ..
901                         "<br />This list is used to map R0KH-ID (NAS Identifier) to a destination " ..
902                         "MAC address when requesting PMK-R1 key from the R0KH that the STA " ..
903                         "used during the Initial Mobility Domain Association."))
904         r0kh:depends({ieee80211r="1", ft_psk_generate_local=""})
905         r0kh.rmempty = true
906
907         r1kh = s:taboption("encryption", DynamicList, "r1kh", translate("External R1 Key Holder List"),
908                 translate ("List of R1KHs in the same Mobility Domain. "..
909                         "<br />Format: MAC-address,R1KH-ID as 6 octets with colons,128-bit key as hex string. "..
910                         "<br />This list is used to map R1KH-ID to a destination MAC address " ..
911                         "when sending PMK-R1 key from the R0KH. This is also the " ..
912                         "list of authorized R1KHs in the MD that can request PMK-R1 keys."))
913         r1kh:depends({ieee80211r="1", ft_psk_generate_local=""})
914         r1kh.rmempty = true
915         -- End of 802.11r options
916
917         eaptype = s:taboption("encryption", ListValue, "eap_type", translate("EAP-Method"))
918         eaptype:value("tls",  "TLS")
919         eaptype:value("ttls", "TTLS")
920         eaptype:value("peap", "PEAP")
921         eaptype:value("fast", "FAST")
922         eaptype:depends({mode="sta", encryption="wpa"})
923         eaptype:depends({mode="sta", encryption="wpa2"})
924         eaptype:depends({mode="sta-wds", encryption="wpa"})
925         eaptype:depends({mode="sta-wds", encryption="wpa2"})
926
927         cacert = s:taboption("encryption", FileUpload, "ca_cert", translate("Path to CA-Certificate"))
928         cacert:depends({mode="sta", encryption="wpa"})
929         cacert:depends({mode="sta", encryption="wpa2"})
930         cacert:depends({mode="sta-wds", encryption="wpa"})
931         cacert:depends({mode="sta-wds", encryption="wpa2"})
932         cacert.rmempty = true
933
934         clientcert = s:taboption("encryption", FileUpload, "client_cert", translate("Path to Client-Certificate"))
935         clientcert:depends({mode="sta", eap_type="tls", encryption="wpa"})
936         clientcert:depends({mode="sta", eap_type="tls", encryption="wpa2"})
937         clientcert:depends({mode="sta-wds", eap_type="tls", encryption="wpa"})
938         clientcert:depends({mode="sta-wds", eap_type="tls", encryption="wpa2"})
939
940         privkey = s:taboption("encryption", FileUpload, "priv_key", translate("Path to Private Key"))
941         privkey:depends({mode="sta", eap_type="tls", encryption="wpa2"})
942         privkey:depends({mode="sta", eap_type="tls", encryption="wpa"})
943         privkey:depends({mode="sta-wds", eap_type="tls", encryption="wpa2"})
944         privkey:depends({mode="sta-wds", eap_type="tls", encryption="wpa"})
945
946         privkeypwd = s:taboption("encryption", Value, "priv_key_pwd", translate("Password of Private Key"))
947         privkeypwd:depends({mode="sta", eap_type="tls", encryption="wpa2"})
948         privkeypwd:depends({mode="sta", eap_type="tls", encryption="wpa"})
949         privkeypwd:depends({mode="sta-wds", eap_type="tls", encryption="wpa2"})
950         privkeypwd:depends({mode="sta-wds", eap_type="tls", encryption="wpa"})
951         privkeypwd.rmempty = true
952         privkeypwd.password = true
953
954         auth = s:taboption("encryption", ListValue, "auth", translate("Authentication"))
955         auth:value("PAP", "PAP", {eap_type="ttls"})
956         auth:value("CHAP", "CHAP", {eap_type="ttls"})
957         auth:value("MSCHAP", "MSCHAP", {eap_type="ttls"})
958         auth:value("MSCHAPV2", "MSCHAPv2", {eap_type="ttls"})
959         auth:value("EAP-GTC")
960         auth:value("EAP-MD5")
961         auth:value("EAP-MSCHAPV2")
962         auth:value("EAP-TLS")
963         auth:depends({mode="sta", eap_type="fast", encryption="wpa2"})
964         auth:depends({mode="sta", eap_type="fast", encryption="wpa"})
965         auth:depends({mode="sta", eap_type="peap", encryption="wpa2"})
966         auth:depends({mode="sta", eap_type="peap", encryption="wpa"})
967         auth:depends({mode="sta", eap_type="ttls", encryption="wpa2"})
968         auth:depends({mode="sta", eap_type="ttls", encryption="wpa"})
969         auth:depends({mode="sta-wds", eap_type="fast", encryption="wpa2"})
970         auth:depends({mode="sta-wds", eap_type="fast", encryption="wpa"})
971         auth:depends({mode="sta-wds", eap_type="peap", encryption="wpa2"})
972         auth:depends({mode="sta-wds", eap_type="peap", encryption="wpa"})
973         auth:depends({mode="sta-wds", eap_type="ttls", encryption="wpa2"})
974         auth:depends({mode="sta-wds", eap_type="ttls", encryption="wpa"})
975
976         cacert2 = s:taboption("encryption", FileUpload, "ca_cert2", translate("Path to inner CA-Certificate"))
977         cacert2:depends({mode="sta", auth="EAP-TLS", encryption="wpa"})
978         cacert2:depends({mode="sta", auth="EAP-TLS", encryption="wpa2"})
979         cacert2:depends({mode="sta-wds", auth="EAP-TLS", encryption="wpa"})
980         cacert2:depends({mode="sta-wds", auth="EAP-TLS", encryption="wpa2"})
981
982         clientcert2 = s:taboption("encryption", FileUpload, "client_cert2", translate("Path to inner Client-Certificate"))
983         clientcert2:depends({mode="sta", auth="EAP-TLS", encryption="wpa"})
984         clientcert2:depends({mode="sta", auth="EAP-TLS", encryption="wpa2"})
985         clientcert2:depends({mode="sta-wds", auth="EAP-TLS", encryption="wpa"})
986         clientcert2:depends({mode="sta-wds", auth="EAP-TLS", encryption="wpa2"})
987
988         privkey2 = s:taboption("encryption", FileUpload, "priv_key2", translate("Path to inner Private Key"))
989         privkey2:depends({mode="sta", auth="EAP-TLS", encryption="wpa"})
990         privkey2:depends({mode="sta", auth="EAP-TLS", encryption="wpa2"})
991         privkey2:depends({mode="sta-wds", auth="EAP-TLS", encryption="wpa"})
992         privkey2:depends({mode="sta-wds", auth="EAP-TLS", encryption="wpa2"})
993
994         privkeypwd2 = s:taboption("encryption", Value, "priv_key2_pwd", translate("Password of inner Private Key"))
995         privkeypwd2:depends({mode="sta", auth="EAP-TLS", encryption="wpa"})
996         privkeypwd2:depends({mode="sta", auth="EAP-TLS", encryption="wpa2"})
997         privkeypwd2:depends({mode="sta-wds", auth="EAP-TLS", encryption="wpa"})
998         privkeypwd2:depends({mode="sta-wds", auth="EAP-TLS", encryption="wpa2"})
999         privkeypwd2.rmempty = true
1000         privkeypwd2.password = true
1001
1002         identity = s:taboption("encryption", Value, "identity", translate("Identity"))
1003         identity:depends({mode="sta", eap_type="fast", encryption="wpa2"})
1004         identity:depends({mode="sta", eap_type="fast", encryption="wpa"})
1005         identity:depends({mode="sta", eap_type="peap", encryption="wpa2"})
1006         identity:depends({mode="sta", eap_type="peap", encryption="wpa"})
1007         identity:depends({mode="sta", eap_type="ttls", encryption="wpa2"})
1008         identity:depends({mode="sta", eap_type="ttls", encryption="wpa"})
1009         identity:depends({mode="sta-wds", eap_type="fast", encryption="wpa2"})
1010         identity:depends({mode="sta-wds", eap_type="fast", encryption="wpa"})
1011         identity:depends({mode="sta-wds", eap_type="peap", encryption="wpa2"})
1012         identity:depends({mode="sta-wds", eap_type="peap", encryption="wpa"})
1013         identity:depends({mode="sta-wds", eap_type="ttls", encryption="wpa2"})
1014         identity:depends({mode="sta-wds", eap_type="ttls", encryption="wpa"})
1015         identity:depends({mode="sta", eap_type="tls", encryption="wpa2"})
1016         identity:depends({mode="sta", eap_type="tls", encryption="wpa"})
1017         identity:depends({mode="sta-wds", eap_type="tls", encryption="wpa2"})
1018         identity:depends({mode="sta-wds", eap_type="tls", encryption="wpa"})
1019
1020         anonymous_identity = s:taboption("encryption", Value, "anonymous_identity", translate("Anonymous Identity"))
1021         anonymous_identity:depends({mode="sta", eap_type="fast", encryption="wpa2"})
1022         anonymous_identity:depends({mode="sta", eap_type="fast", encryption="wpa"})
1023         anonymous_identity:depends({mode="sta", eap_type="peap", encryption="wpa2"})
1024         anonymous_identity:depends({mode="sta", eap_type="peap", encryption="wpa"})
1025         anonymous_identity:depends({mode="sta", eap_type="ttls", encryption="wpa2"})
1026         anonymous_identity:depends({mode="sta", eap_type="ttls", encryption="wpa"})
1027         anonymous_identity:depends({mode="sta-wds", eap_type="fast", encryption="wpa2"})
1028         anonymous_identity:depends({mode="sta-wds", eap_type="fast", encryption="wpa"})
1029         anonymous_identity:depends({mode="sta-wds", eap_type="peap", encryption="wpa2"})
1030         anonymous_identity:depends({mode="sta-wds", eap_type="peap", encryption="wpa"})
1031         anonymous_identity:depends({mode="sta-wds", eap_type="ttls", encryption="wpa2"})
1032         anonymous_identity:depends({mode="sta-wds", eap_type="ttls", encryption="wpa"})
1033         anonymous_identity:depends({mode="sta", eap_type="tls", encryption="wpa2"})
1034         anonymous_identity:depends({mode="sta", eap_type="tls", encryption="wpa"})
1035         anonymous_identity:depends({mode="sta-wds", eap_type="tls", encryption="wpa2"})
1036         anonymous_identity:depends({mode="sta-wds", eap_type="tls", encryption="wpa"})
1037
1038         password = s:taboption("encryption", Value, "password", translate("Password"))
1039         password:depends({mode="sta", eap_type="fast", encryption="wpa2"})
1040         password:depends({mode="sta", eap_type="fast", encryption="wpa"})
1041         password:depends({mode="sta", eap_type="peap", encryption="wpa2"})
1042         password:depends({mode="sta", eap_type="peap", encryption="wpa"})
1043         password:depends({mode="sta", eap_type="ttls", encryption="wpa2"})
1044         password:depends({mode="sta", eap_type="ttls", encryption="wpa"})
1045         password:depends({mode="sta-wds", eap_type="fast", encryption="wpa2"})
1046         password:depends({mode="sta-wds", eap_type="fast", encryption="wpa"})
1047         password:depends({mode="sta-wds", eap_type="peap", encryption="wpa2"})
1048         password:depends({mode="sta-wds", eap_type="peap", encryption="wpa"})
1049         password:depends({mode="sta-wds", eap_type="ttls", encryption="wpa2"})
1050         password:depends({mode="sta-wds", eap_type="ttls", encryption="wpa"})
1051         password.rmempty = true
1052         password.password = true
1053 end
1054
1055 -- ieee802.11w options
1056 if hwtype == "mac80211" then
1057         local has_80211w = (os.execute("hostapd -v11w 2>/dev/null || hostapd -veap 2>/dev/null") == 0)
1058         if has_80211w then
1059                 ieee80211w = s:taboption("encryption", ListValue, "ieee80211w",
1060                         translate("802.11w Management Frame Protection"),
1061                         translate("Requires the 'full' version of wpad/hostapd " ..
1062                                 "and support from the wifi driver <br />(as of Feb 2017: " ..
1063                                 "ath9k and ath10k, in LEDE also mwlwifi and mt76)"))
1064                 ieee80211w.default = ""
1065                 ieee80211w.rmempty = true
1066                 ieee80211w:value("", translate("Disabled (default)"))
1067                 ieee80211w:value("1", translate("Optional"))
1068                 ieee80211w:value("2", translate("Required"))
1069                 ieee80211w:depends({mode="ap", encryption="wpa2"})
1070                 ieee80211w:depends({mode="ap-wds", encryption="wpa2"})
1071                 ieee80211w:depends({mode="ap", encryption="psk2"})
1072                 ieee80211w:depends({mode="ap", encryption="psk-mixed"})
1073                 ieee80211w:depends({mode="ap-wds", encryption="psk2"})
1074                 ieee80211w:depends({mode="ap-wds", encryption="psk-mixed"})
1075
1076                 max_timeout = s:taboption("encryption", Value, "ieee80211w_max_timeout",
1077                                 translate("802.11w maximum timeout"),
1078                                 translate("802.11w Association SA Query maximum timeout"))
1079                 max_timeout:depends({ieee80211w="1"})
1080                 max_timeout:depends({ieee80211w="2"})
1081                 max_timeout.datatype = "uinteger"
1082                 max_timeout.placeholder = "1000"
1083                 max_timeout.rmempty = true
1084
1085                 retry_timeout = s:taboption("encryption", Value, "ieee80211w_retry_timeout",
1086                                 translate("802.11w retry timeout"),
1087                                 translate("802.11w Association SA Query retry timeout"))
1088                 retry_timeout:depends({ieee80211w="1"})
1089                 retry_timeout:depends({ieee80211w="2"})
1090                 retry_timeout.datatype = "uinteger"
1091                 retry_timeout.placeholder = "201"
1092                 retry_timeout.rmempty = true
1093         end
1094
1095         key_retries = s:taboption("encryption", Flag, "wpa_disable_eapol_key_retries",
1096                 translate("Enable key reinstallation (KRACK) countermeasures"),
1097                 translate("Complicates key reinstallation attacks on the client side by disabling retransmission of EAPOL-Key frames that are used to install keys. This workaround might cause interoperability issues and reduced robustness of key negotiation especially in environments with heavy traffic load."))
1098
1099         key_retries:depends({mode="ap", encryption="wpa2"})
1100         key_retries:depends({mode="ap", encryption="psk2"})
1101         key_retries:depends({mode="ap", encryption="psk-mixed"})
1102         key_retries:depends({mode="ap-wds", encryption="wpa2"})
1103         key_retries:depends({mode="ap-wds", encryption="psk2"})
1104         key_retries:depends({mode="ap-wds", encryption="psk-mixed"})
1105 end
1106
1107 if hwtype == "mac80211" or hwtype == "prism2" then
1108         local wpasupplicant = fs.access("/usr/sbin/wpa_supplicant")
1109         local hostcli = fs.access("/usr/sbin/hostapd_cli")
1110         if hostcli and wpasupplicant then
1111                 wps = s:taboption("encryption", Flag, "wps_pushbutton", translate("Enable WPS pushbutton, requires WPA(2)-PSK"))
1112                 wps.enabled = "1"
1113                 wps.disabled = "0"
1114                 wps.rmempty = false
1115                 wps:depends("encryption", "psk")
1116                 wps:depends("encryption", "psk2")
1117                 wps:depends("encryption", "psk-mixed")
1118         end
1119 end
1120
1121 return m