1# Python class for controlling hostapd
2# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
3#
4# This software may be distributed under the terms of the BSD license.
5# See README for more details.
6
7import os
8import re
9import time
10import logging
11import binascii
12import struct
13import tempfile
14import wpaspy
15import remotehost
16import utils
17import subprocess
18
19logger = logging.getLogger()
20hapd_ctrl = '/var/run/hostapd'
21hapd_global = '/var/run/hostapd-global'
22
23def mac2tuple(mac):
24    return struct.unpack('6B', binascii.unhexlify(mac.replace(':', '')))
25
26class HostapdGlobal:
27    def __init__(self, apdev=None, global_ctrl_override=None):
28        try:
29            hostname = apdev['hostname']
30            port = apdev['port']
31        except:
32            hostname = None
33            port = 8878
34        self.host = remotehost.Host(hostname)
35        self.hostname = hostname
36        self.port = port
37        if hostname is None:
38            global_ctrl = hapd_global
39            if global_ctrl_override:
40                global_ctrl = global_ctrl_override
41            self.ctrl = wpaspy.Ctrl(global_ctrl)
42            self.mon = wpaspy.Ctrl(global_ctrl)
43            self.dbg = ""
44        else:
45            self.ctrl = wpaspy.Ctrl(hostname, port)
46            self.mon = wpaspy.Ctrl(hostname, port)
47            self.dbg = hostname + "/" + str(port)
48        self.mon.attach()
49
50    def cmd_execute(self, cmd_array, shell=False):
51        if self.hostname is None:
52            if shell:
53                cmd = ' '.join(cmd_array)
54            else:
55                cmd = cmd_array
56            proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT,
57                                    stdout=subprocess.PIPE, shell=shell)
58            out = proc.communicate()[0]
59            ret = proc.returncode
60            return ret, out.decode()
61        else:
62            return self.host.execute(cmd_array)
63
64    def request(self, cmd, timeout=10):
65        logger.debug(self.dbg + ": CTRL(global): " + cmd)
66        return self.ctrl.request(cmd, timeout)
67
68    def wait_event(self, events, timeout):
69        start = os.times()[4]
70        while True:
71            while self.mon.pending():
72                ev = self.mon.recv()
73                logger.debug(self.dbg + "(global): " + ev)
74                for event in events:
75                    if event in ev:
76                        return ev
77            now = os.times()[4]
78            remaining = start + timeout - now
79            if remaining <= 0:
80                break
81            if not self.mon.pending(timeout=remaining):
82                break
83        return None
84
85    def add(self, ifname, driver=None):
86        cmd = "ADD " + ifname + " " + hapd_ctrl
87        if driver:
88            cmd += " " + driver
89        res = self.request(cmd)
90        if "OK" not in res:
91            raise Exception("Could not add hostapd interface " + ifname)
92
93    def add_iface(self, ifname, confname):
94        res = self.request("ADD " + ifname + " config=" + confname)
95        if "OK" not in res:
96            raise Exception("Could not add hostapd interface")
97
98    def add_bss(self, phy, confname, ignore_error=False):
99        res = self.request("ADD bss_config=" + phy + ":" + confname)
100        if "OK" not in res:
101            if not ignore_error:
102                raise Exception("Could not add hostapd BSS")
103
104    def remove(self, ifname):
105        self.request("REMOVE " + ifname, timeout=30)
106
107    def relog(self):
108        self.request("RELOG")
109
110    def flush(self):
111        self.request("FLUSH")
112
113    def get_ctrl_iface_port(self, ifname):
114        if self.hostname is None:
115            return None
116
117        res = self.request("INTERFACES ctrl")
118        lines = res.splitlines()
119        found = False
120        for line in lines:
121            words = line.split()
122            if words[0] == ifname:
123                found = True
124                break
125        if not found:
126            raise Exception("Could not find UDP port for " + ifname)
127        res = line.find("ctrl_iface=udp:")
128        if res == -1:
129            raise Exception("Wrong ctrl_interface format")
130        words = line.split(":")
131        return int(words[1])
132
133    def terminate(self):
134        self.mon.detach()
135        self.mon.close()
136        self.mon = None
137        self.ctrl.terminate()
138        self.ctrl = None
139
140    def send_file(self, src, dst):
141        self.host.send_file(src, dst)
142
143class Hostapd:
144    def __init__(self, ifname, bssidx=0, hostname=None, port=8877):
145        self.hostname = hostname
146        self.host = remotehost.Host(hostname, ifname)
147        self.ifname = ifname
148        if hostname is None:
149            self.ctrl = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
150            self.mon = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
151            self.dbg = ifname
152        else:
153            self.ctrl = wpaspy.Ctrl(hostname, port)
154            self.mon = wpaspy.Ctrl(hostname, port)
155            self.dbg = hostname + "/" + ifname
156        self.mon.attach()
157        self.bssid = None
158        self.bssidx = bssidx
159
160    def cmd_execute(self, cmd_array, shell=False):
161        if self.hostname is None:
162            if shell:
163                cmd = ' '.join(cmd_array)
164            else:
165                cmd = cmd_array
166            proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT,
167                                    stdout=subprocess.PIPE, shell=shell)
168            out = proc.communicate()[0]
169            ret = proc.returncode
170            return ret, out.decode()
171        else:
172            return self.host.execute(cmd_array)
173
174    def close_ctrl(self):
175        if self.mon is not None:
176            self.mon.detach()
177            self.mon.close()
178            self.mon = None
179            self.ctrl.close()
180            self.ctrl = None
181
182    def own_addr(self):
183        if self.bssid is None:
184            self.bssid = self.get_status_field('bssid[%d]' % self.bssidx)
185        return self.bssid
186
187    def get_addr(self, group=False):
188        return self.own_addr()
189
190    def request(self, cmd):
191        logger.debug(self.dbg + ": CTRL: " + cmd)
192        return self.ctrl.request(cmd)
193
194    def ping(self):
195        return "PONG" in self.request("PING")
196
197    def set(self, field, value):
198        if "OK" not in self.request("SET " + field + " " + value):
199            if "TKIP" in value and (field == "wpa_pairwise" or \
200                                    field == "rsn_pairwise"):
201                raise utils.HwsimSkip("Cipher TKIP not supported")
202            raise Exception("Failed to set hostapd parameter " + field)
203
204    def set_defaults(self):
205        self.set("driver", "nl80211")
206        self.set("hw_mode", "g")
207        self.set("channel", "1")
208        self.set("ieee80211n", "1")
209        self.set("logger_stdout", "-1")
210        self.set("logger_stdout_level", "0")
211
212    def set_open(self, ssid):
213        self.set_defaults()
214        self.set("ssid", ssid)
215
216    def set_wpa2_psk(self, ssid, passphrase):
217        self.set_defaults()
218        self.set("ssid", ssid)
219        self.set("wpa_passphrase", passphrase)
220        self.set("wpa", "2")
221        self.set("wpa_key_mgmt", "WPA-PSK")
222        self.set("rsn_pairwise", "CCMP")
223
224    def set_wpa_psk(self, ssid, passphrase):
225        self.set_defaults()
226        self.set("ssid", ssid)
227        self.set("wpa_passphrase", passphrase)
228        self.set("wpa", "1")
229        self.set("wpa_key_mgmt", "WPA-PSK")
230        self.set("wpa_pairwise", "TKIP")
231
232    def set_wpa_psk_mixed(self, ssid, passphrase):
233        self.set_defaults()
234        self.set("ssid", ssid)
235        self.set("wpa_passphrase", passphrase)
236        self.set("wpa", "3")
237        self.set("wpa_key_mgmt", "WPA-PSK")
238        self.set("wpa_pairwise", "TKIP")
239        self.set("rsn_pairwise", "CCMP")
240
241    def set_wep(self, ssid, key):
242        self.set_defaults()
243        self.set("ssid", ssid)
244        self.set("wep_key0", key)
245
246    def enable(self):
247        if "OK" not in self.request("ENABLE"):
248            raise Exception("Failed to enable hostapd interface " + self.ifname)
249
250    def disable(self):
251        if "OK" not in self.request("DISABLE"):
252            raise Exception("Failed to disable hostapd interface " + self.ifname)
253
254    def dump_monitor(self):
255        while self.mon.pending():
256            ev = self.mon.recv()
257            logger.debug(self.dbg + ": " + ev)
258
259    def wait_event(self, events, timeout):
260        if not isinstance(events, list):
261            raise Exception("Hostapd.wait_event() called with incorrect events argument type")
262        start = os.times()[4]
263        while True:
264            while self.mon.pending():
265                ev = self.mon.recv()
266                logger.debug(self.dbg + ": " + ev)
267                for event in events:
268                    if event in ev:
269                        return ev
270            now = os.times()[4]
271            remaining = start + timeout - now
272            if remaining <= 0:
273                break
274            if not self.mon.pending(timeout=remaining):
275                break
276        return None
277
278    def wait_sta(self, addr=None, timeout=2):
279        ev = self.wait_event(["AP-STA-CONNECT"], timeout=timeout)
280        if ev is None:
281            raise Exception("AP did not report STA connection")
282        if addr and addr not in ev:
283            raise Exception("Unexpected STA address in connection event: " + ev)
284
285    def wait_ptkinitdone(self, addr, timeout=2):
286        while timeout > 0:
287            sta = self.get_sta(addr)
288            if 'hostapdWPAPTKState' not in sta:
289                raise Exception("GET_STA did not return hostapdWPAPTKState")
290            state = sta['hostapdWPAPTKState']
291            if state == "11":
292                return
293            time.sleep(0.1)
294            timeout -= 0.1
295        raise Exception("Timeout while waiting for PTKINITDONE")
296
297    def get_status(self):
298        res = self.request("STATUS")
299        lines = res.splitlines()
300        vals = dict()
301        for l in lines:
302            [name, value] = l.split('=', 1)
303            vals[name] = value
304        return vals
305
306    def get_status_field(self, field):
307        vals = self.get_status()
308        if field in vals:
309            return vals[field]
310        return None
311
312    def get_driver_status(self):
313        res = self.request("STATUS-DRIVER")
314        lines = res.splitlines()
315        vals = dict()
316        for l in lines:
317            [name, value] = l.split('=', 1)
318            vals[name] = value
319        return vals
320
321    def get_driver_status_field(self, field):
322        vals = self.get_driver_status()
323        if field in vals:
324            return vals[field]
325        return None
326
327    def get_config(self):
328        res = self.request("GET_CONFIG")
329        lines = res.splitlines()
330        vals = dict()
331        for l in lines:
332            [name, value] = l.split('=', 1)
333            vals[name] = value
334        return vals
335
336    def mgmt_rx(self, timeout=5):
337        ev = self.wait_event(["MGMT-RX"], timeout=timeout)
338        if ev is None:
339            return None
340        msg = {}
341        frame = binascii.unhexlify(ev.split(' ')[1])
342        msg['frame'] = frame
343
344        hdr = struct.unpack('<HH6B6B6BH', frame[0:24])
345        msg['fc'] = hdr[0]
346        msg['subtype'] = (hdr[0] >> 4) & 0xf
347        hdr = hdr[1:]
348        msg['duration'] = hdr[0]
349        hdr = hdr[1:]
350        msg['da'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
351        hdr = hdr[6:]
352        msg['sa'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
353        hdr = hdr[6:]
354        msg['bssid'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
355        hdr = hdr[6:]
356        msg['seq_ctrl'] = hdr[0]
357        msg['payload'] = frame[24:]
358
359        return msg
360
361    def mgmt_tx(self, msg):
362        t = (msg['fc'], 0) + mac2tuple(msg['da']) + mac2tuple(msg['sa']) + mac2tuple(msg['bssid']) + (0,)
363        hdr = struct.pack('<HH6B6B6BH', *t)
364        res = self.request("MGMT_TX " + binascii.hexlify(hdr + msg['payload']).decode())
365        if "OK" not in res:
366            raise Exception("MGMT_TX command to hostapd failed")
367
368    def get_sta(self, addr, info=None, next=False):
369        cmd = "STA-NEXT " if next else "STA "
370        if addr is None:
371            res = self.request("STA-FIRST")
372        elif info:
373            res = self.request(cmd + addr + " " + info)
374        else:
375            res = self.request(cmd + addr)
376        lines = res.splitlines()
377        vals = dict()
378        first = True
379        for l in lines:
380            if first and '=' not in l:
381                vals['addr'] = l
382                first = False
383            else:
384                [name, value] = l.split('=', 1)
385                vals[name] = value
386        return vals
387
388    def get_mib(self, param=None):
389        if param:
390            res = self.request("MIB " + param)
391        else:
392            res = self.request("MIB")
393        lines = res.splitlines()
394        vals = dict()
395        for l in lines:
396            name_val = l.split('=', 1)
397            if len(name_val) > 1:
398                vals[name_val[0]] = name_val[1]
399        return vals
400
401    def get_pmksa(self, addr):
402        res = self.request("PMKSA")
403        lines = res.splitlines()
404        for l in lines:
405            if addr not in l:
406                continue
407            vals = dict()
408            [index, aa, pmkid, expiration, opportunistic] = l.split(' ')
409            vals['index'] = index
410            vals['pmkid'] = pmkid
411            vals['expiration'] = expiration
412            vals['opportunistic'] = opportunistic
413            return vals
414        return None
415
416    def dpp_qr_code(self, uri):
417        res = self.request("DPP_QR_CODE " + uri)
418        if "FAIL" in res:
419            raise Exception("Failed to parse QR Code URI")
420        return int(res)
421
422    def dpp_nfc_uri(self, uri):
423        res = self.request("DPP_NFC_URI " + uri)
424        if "FAIL" in res:
425            raise Exception("Failed to parse NFC URI")
426        return int(res)
427
428    def dpp_bootstrap_gen(self, type="qrcode", chan=None, mac=None, info=None,
429                          curve=None, key=None):
430        cmd = "DPP_BOOTSTRAP_GEN type=" + type
431        if chan:
432            cmd += " chan=" + chan
433        if mac:
434            if mac is True:
435                mac = self.own_addr()
436            cmd += " mac=" + mac.replace(':', '')
437        if info:
438            cmd += " info=" + info
439        if curve:
440            cmd += " curve=" + curve
441        if key:
442            cmd += " key=" + key
443        res = self.request(cmd)
444        if "FAIL" in res:
445            raise Exception("Failed to generate bootstrapping info")
446        return int(res)
447
448    def dpp_bootstrap_set(self, id, conf=None, configurator=None, ssid=None,
449                          extra=None):
450        cmd = "DPP_BOOTSTRAP_SET %d" % id
451        if ssid:
452            cmd += " ssid=" + binascii.hexlify(ssid.encode()).decode()
453        if extra:
454            cmd += " " + extra
455        if conf:
456            cmd += " conf=" + conf
457        if configurator is not None:
458            cmd += " configurator=%d" % configurator
459        if "OK" not in self.request(cmd):
460            raise Exception("Failed to set bootstrapping parameters")
461
462    def dpp_listen(self, freq, netrole=None, qr=None, role=None):
463        cmd = "DPP_LISTEN " + str(freq)
464        if netrole:
465            cmd += " netrole=" + netrole
466        if qr:
467            cmd += " qr=" + qr
468        if role:
469            cmd += " role=" + role
470        if "OK" not in self.request(cmd):
471            raise Exception("Failed to start listen operation")
472
473    def dpp_auth_init(self, peer=None, uri=None, conf=None, configurator=None,
474                      extra=None, own=None, role=None, neg_freq=None,
475                      ssid=None, passphrase=None, expect_fail=False,
476                      conn_status=False, nfc_uri=None):
477        cmd = "DPP_AUTH_INIT"
478        if peer is None:
479            if nfc_uri:
480                peer = self.dpp_nfc_uri(nfc_uri)
481            else:
482                peer = self.dpp_qr_code(uri)
483        cmd += " peer=%d" % peer
484        if own is not None:
485            cmd += " own=%d" % own
486        if role:
487            cmd += " role=" + role
488        if extra:
489            cmd += " " + extra
490        if conf:
491            cmd += " conf=" + conf
492        if configurator is not None:
493            cmd += " configurator=%d" % configurator
494        if neg_freq:
495            cmd += " neg_freq=%d" % neg_freq
496        if ssid:
497            cmd += " ssid=" + binascii.hexlify(ssid.encode()).decode()
498        if passphrase:
499            cmd += " pass=" + binascii.hexlify(passphrase.encode()).decode()
500        if conn_status:
501            cmd += " conn_status=1"
502        res = self.request(cmd)
503        if expect_fail:
504            if "FAIL" not in res:
505                raise Exception("DPP authentication started unexpectedly")
506            return
507        if "OK" not in res:
508            raise Exception("Failed to initiate DPP Authentication")
509
510    def dpp_pkex_init(self, identifier, code, role=None, key=None, curve=None,
511                      extra=None, use_id=None):
512        if use_id is None:
513            id1 = self.dpp_bootstrap_gen(type="pkex", key=key, curve=curve)
514        else:
515            id1 = use_id
516        cmd = "own=%d " % id1
517        if identifier:
518            cmd += "identifier=%s " % identifier
519        cmd += "init=1 "
520        if role:
521            cmd += "role=%s " % role
522        if extra:
523            cmd += extra + " "
524        cmd += "code=%s" % code
525        res = self.request("DPP_PKEX_ADD " + cmd)
526        if "FAIL" in res:
527            raise Exception("Failed to set PKEX data (initiator)")
528        return id1
529
530    def dpp_pkex_resp(self, freq, identifier, code, key=None, curve=None,
531                      listen_role=None):
532        id0 = self.dpp_bootstrap_gen(type="pkex", key=key, curve=curve)
533        cmd = "own=%d " % id0
534        if identifier:
535            cmd += "identifier=%s " % identifier
536        cmd += "code=%s" % code
537        res = self.request("DPP_PKEX_ADD " + cmd)
538        if "FAIL" in res:
539            raise Exception("Failed to set PKEX data (responder)")
540        self.dpp_listen(freq, role=listen_role)
541
542    def dpp_configurator_add(self, curve=None, key=None):
543        cmd = "DPP_CONFIGURATOR_ADD"
544        if curve:
545            cmd += " curve=" + curve
546        if key:
547            cmd += " key=" + key
548        res = self.request(cmd)
549        if "FAIL" in res:
550            raise Exception("Failed to add configurator")
551        return int(res)
552
553    def dpp_configurator_remove(self, conf_id):
554        res = self.request("DPP_CONFIGURATOR_REMOVE %d" % conf_id)
555        if "OK" not in res:
556            raise Exception("DPP_CONFIGURATOR_REMOVE failed")
557
558    def note(self, txt):
559        self.request("NOTE " + txt)
560
561    def send_file(self, src, dst):
562        self.host.send_file(src, dst)
563
564    def get_ptksa(self, bssid, cipher):
565        res = self.request("PTKSA_CACHE_LIST")
566        lines = res.splitlines()
567        for l in lines:
568            if bssid not in l or cipher not in l:
569                continue
570            vals = dict()
571            [index, addr, cipher, expiration, tk, kdk] = l.split(' ', 5)
572            vals['index'] = index
573            vals['addr'] = addr
574            vals['cipher'] = cipher
575            vals['expiration'] = expiration
576            vals['tk'] = tk
577            vals['kdk'] = kdk
578            return vals
579        return None
580
581def add_ap(apdev, params, wait_enabled=True, no_enable=False, timeout=30,
582           global_ctrl_override=None, driver=False):
583        if isinstance(apdev, dict):
584            ifname = apdev['ifname']
585            try:
586                hostname = apdev['hostname']
587                port = apdev['port']
588                logger.info("Starting AP " + hostname + "/" + port + " " + ifname)
589            except:
590                logger.info("Starting AP " + ifname)
591                hostname = None
592                port = 8878
593        else:
594            ifname = apdev
595            logger.info("Starting AP " + ifname + " (old add_ap argument type)")
596            hostname = None
597            port = 8878
598        hapd_global = HostapdGlobal(apdev,
599                                    global_ctrl_override=global_ctrl_override)
600        hapd_global.remove(ifname)
601        hapd_global.add(ifname, driver=driver)
602        port = hapd_global.get_ctrl_iface_port(ifname)
603        hapd = Hostapd(ifname, hostname=hostname, port=port)
604        if not hapd.ping():
605            raise Exception("Could not ping hostapd")
606        hapd.set_defaults()
607        fields = ["ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt",
608                  "wpa", "wpa_deny_ptk0_rekey",
609                  "wpa_pairwise", "rsn_pairwise", "auth_server_addr",
610                  "acct_server_addr", "osu_server_uri"]
611        for field in fields:
612            if field in params:
613                hapd.set(field, params[field])
614        for f, v in list(params.items()):
615            if f in fields:
616                continue
617            if isinstance(v, list):
618                for val in v:
619                    hapd.set(f, val)
620            else:
621                hapd.set(f, v)
622        if no_enable:
623            return hapd
624        hapd.enable()
625        if wait_enabled:
626            ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=timeout)
627            if ev is None:
628                raise Exception("AP startup timed out")
629            if "AP-ENABLED" not in ev:
630                raise Exception("AP startup failed")
631        return hapd
632
633def add_bss(apdev, ifname, confname, ignore_error=False):
634    phy = utils.get_phy(apdev)
635    try:
636        hostname = apdev['hostname']
637        port = apdev['port']
638        logger.info("Starting BSS " + hostname + "/" + port + " phy=" + phy + " ifname=" + ifname)
639    except:
640        logger.info("Starting BSS phy=" + phy + " ifname=" + ifname)
641        hostname = None
642        port = 8878
643    hapd_global = HostapdGlobal(apdev)
644    confname = cfg_file(apdev, confname, ifname)
645    hapd_global.send_file(confname, confname)
646    hapd_global.add_bss(phy, confname, ignore_error)
647    port = hapd_global.get_ctrl_iface_port(ifname)
648    hapd = Hostapd(ifname, hostname=hostname, port=port)
649    if not hapd.ping():
650        raise Exception("Could not ping hostapd")
651    return hapd
652
653def add_iface(apdev, confname):
654    ifname = apdev['ifname']
655    try:
656        hostname = apdev['hostname']
657        port = apdev['port']
658        logger.info("Starting interface " + hostname + "/" + port + " " + ifname)
659    except:
660        logger.info("Starting interface " + ifname)
661        hostname = None
662        port = 8878
663    hapd_global = HostapdGlobal(apdev)
664    confname = cfg_file(apdev, confname, ifname)
665    hapd_global.send_file(confname, confname)
666    hapd_global.add_iface(ifname, confname)
667    port = hapd_global.get_ctrl_iface_port(ifname)
668    hapd = Hostapd(ifname, hostname=hostname, port=port)
669    if not hapd.ping():
670        raise Exception("Could not ping hostapd")
671    return hapd
672
673def remove_bss(apdev, ifname=None):
674    if ifname == None:
675        ifname = apdev['ifname']
676    try:
677        hostname = apdev['hostname']
678        port = apdev['port']
679        logger.info("Removing BSS " + hostname + "/" + port + " " + ifname)
680    except:
681        logger.info("Removing BSS " + ifname)
682    hapd_global = HostapdGlobal(apdev)
683    hapd_global.remove(ifname)
684
685def terminate(apdev):
686    try:
687        hostname = apdev['hostname']
688        port = apdev['port']
689        logger.info("Terminating hostapd " + hostname + "/" + port)
690    except:
691        logger.info("Terminating hostapd")
692    hapd_global = HostapdGlobal(apdev)
693    hapd_global.terminate()
694
695def wpa2_params(ssid=None, passphrase=None, wpa_key_mgmt="WPA-PSK",
696                ieee80211w=None):
697    params = {"wpa": "2",
698              "wpa_key_mgmt": wpa_key_mgmt,
699              "rsn_pairwise": "CCMP"}
700    if ssid:
701        params["ssid"] = ssid
702    if passphrase:
703        params["wpa_passphrase"] = passphrase
704    if ieee80211w is not None:
705        params["ieee80211w"] = ieee80211w
706    return params
707
708def wpa_params(ssid=None, passphrase=None):
709    params = {"wpa": "1",
710              "wpa_key_mgmt": "WPA-PSK",
711              "wpa_pairwise": "TKIP"}
712    if ssid:
713        params["ssid"] = ssid
714    if passphrase:
715        params["wpa_passphrase"] = passphrase
716    return params
717
718def wpa_mixed_params(ssid=None, passphrase=None):
719    params = {"wpa": "3",
720              "wpa_key_mgmt": "WPA-PSK",
721              "wpa_pairwise": "TKIP",
722              "rsn_pairwise": "CCMP"}
723    if ssid:
724        params["ssid"] = ssid
725    if passphrase:
726        params["wpa_passphrase"] = passphrase
727    return params
728
729def radius_params():
730    params = {"auth_server_addr": "127.0.0.1",
731              "auth_server_port": "1812",
732              "auth_server_shared_secret": "radius",
733              "nas_identifier": "nas.w1.fi"}
734    return params
735
736def wpa_eap_params(ssid=None):
737    params = radius_params()
738    params["wpa"] = "1"
739    params["wpa_key_mgmt"] = "WPA-EAP"
740    params["wpa_pairwise"] = "TKIP"
741    params["ieee8021x"] = "1"
742    if ssid:
743        params["ssid"] = ssid
744    return params
745
746def wpa2_eap_params(ssid=None):
747    params = radius_params()
748    params["wpa"] = "2"
749    params["wpa_key_mgmt"] = "WPA-EAP"
750    params["rsn_pairwise"] = "CCMP"
751    params["ieee8021x"] = "1"
752    if ssid:
753        params["ssid"] = ssid
754    return params
755
756def b_only_params(channel="1", ssid=None, country=None):
757    params = {"hw_mode": "b",
758              "channel": channel}
759    if ssid:
760        params["ssid"] = ssid
761    if country:
762        params["country_code"] = country
763    return params
764
765def g_only_params(channel="1", ssid=None, country=None):
766    params = {"hw_mode": "g",
767              "channel": channel}
768    if ssid:
769        params["ssid"] = ssid
770    if country:
771        params["country_code"] = country
772    return params
773
774def a_only_params(channel="36", ssid=None, country=None):
775    params = {"hw_mode": "a",
776              "channel": channel}
777    if ssid:
778        params["ssid"] = ssid
779    if country:
780        params["country_code"] = country
781    return params
782
783def ht20_params(channel="1", ssid=None, country=None):
784    params = {"ieee80211n": "1",
785              "channel": channel,
786              "hw_mode": "g"}
787    if int(channel) > 14:
788        params["hw_mode"] = "a"
789    if ssid:
790        params["ssid"] = ssid
791    if country:
792        params["country_code"] = country
793    return params
794
795def ht40_plus_params(channel="1", ssid=None, country=None):
796    params = ht20_params(channel, ssid, country)
797    params['ht_capab'] = "[HT40+]"
798    return params
799
800def ht40_minus_params(channel="1", ssid=None, country=None):
801    params = ht20_params(channel, ssid, country)
802    params['ht_capab'] = "[HT40-]"
803    return params
804
805def cmd_execute(apdev, cmd, shell=False):
806    hapd_global = HostapdGlobal(apdev)
807    return hapd_global.cmd_execute(cmd, shell=shell)
808
809def send_file(apdev, src, dst):
810    hapd_global = HostapdGlobal(apdev)
811    return hapd_global.send_file(src, dst)
812
813def acl_file(dev, apdev, conf):
814    fd, filename = tempfile.mkstemp(dir='/tmp', prefix=conf + '-')
815    f = os.fdopen(fd, 'w')
816
817    if conf == 'hostapd.macaddr':
818        mac0 = dev[0].get_status_field("address")
819        f.write(mac0 + '\n')
820        f.write("02:00:00:00:00:12\n")
821        f.write("02:00:00:00:00:34\n")
822        f.write("-02:00:00:00:00:12\n")
823        f.write("-02:00:00:00:00:34\n")
824        f.write("01:01:01:01:01:01\n")
825        f.write("03:01:01:01:01:03\n")
826    elif conf == 'hostapd.accept':
827        mac0 = dev[0].get_status_field("address")
828        mac1 = dev[1].get_status_field("address")
829        f.write(mac0 + "    1\n")
830        f.write(mac1 + "    2\n")
831    elif conf == 'hostapd.accept2':
832        mac0 = dev[0].get_status_field("address")
833        mac1 = dev[1].get_status_field("address")
834        mac2 = dev[2].get_status_field("address")
835        f.write(mac0 + "    1\n")
836        f.write(mac1 + "    2\n")
837        f.write(mac2 + "    3\n")
838    else:
839        f.close()
840        os.unlink(filename)
841        return conf
842
843    return filename
844
845def bssid_inc(apdev, inc=1):
846    parts = apdev['bssid'].split(':')
847    parts[5] = '%02x' % (int(parts[5], 16) + int(inc))
848    bssid = '%s:%s:%s:%s:%s:%s' % (parts[0], parts[1], parts[2],
849                                   parts[3], parts[4], parts[5])
850    return bssid
851
852def cfg_file(apdev, conf, ifname=None):
853    match = re.search(r'^bss-.+', conf)
854    if match:
855        # put cfg file in /tmp directory
856        fd, fname = tempfile.mkstemp(dir='/tmp', prefix=conf + '-')
857        f = os.fdopen(fd, 'w')
858        idx = ''.join(filter(str.isdigit, conf.split('-')[-1]))
859        if ifname is None:
860            ifname = apdev['ifname']
861            if idx != '1':
862                ifname = ifname + '-' + idx
863
864        f.write("driver=nl80211\n")
865        f.write("ctrl_interface=/var/run/hostapd\n")
866        f.write("hw_mode=g\n")
867        f.write("channel=1\n")
868        f.write("ieee80211n=1\n")
869        if conf.startswith('bss-ht40-'):
870            f.write("ht_capab=[HT40+]\n")
871        f.write("interface=%s\n" % ifname)
872
873        f.write("ssid=bss-%s\n" % idx)
874        if conf == 'bss-2-dup.conf':
875            bssid = apdev['bssid']
876        else:
877            bssid = bssid_inc(apdev, int(idx) - 1)
878        f.write("bssid=%s\n" % bssid)
879
880        return fname
881
882    return conf
883