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, ver=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 ver is not None:
521            cmd += "ver=" + str(ver) + " "
522        if role:
523            cmd += "role=%s " % role
524        if extra:
525            cmd += extra + " "
526        cmd += "code=%s" % code
527        res = self.request("DPP_PKEX_ADD " + cmd)
528        if "FAIL" in res:
529            raise Exception("Failed to set PKEX data (initiator)")
530        return id1
531
532    def dpp_pkex_resp(self, freq, identifier, code, key=None, curve=None,
533                      listen_role=None):
534        id0 = self.dpp_bootstrap_gen(type="pkex", key=key, curve=curve)
535        cmd = "own=%d " % id0
536        if identifier:
537            cmd += "identifier=%s " % identifier
538        cmd += "code=%s" % code
539        res = self.request("DPP_PKEX_ADD " + cmd)
540        if "FAIL" in res:
541            raise Exception("Failed to set PKEX data (responder)")
542        self.dpp_listen(freq, role=listen_role)
543
544    def dpp_configurator_add(self, curve=None, key=None,
545                             net_access_key_curve=None):
546        cmd = "DPP_CONFIGURATOR_ADD"
547        if curve:
548            cmd += " curve=" + curve
549        if net_access_key_curve:
550            cmd += " net_access_key_curve=" + curve
551        if key:
552            cmd += " key=" + key
553        res = self.request(cmd)
554        if "FAIL" in res:
555            raise Exception("Failed to add configurator")
556        return int(res)
557
558    def dpp_configurator_remove(self, conf_id):
559        res = self.request("DPP_CONFIGURATOR_REMOVE %d" % conf_id)
560        if "OK" not in res:
561            raise Exception("DPP_CONFIGURATOR_REMOVE failed")
562
563    def note(self, txt):
564        self.request("NOTE " + txt)
565
566    def send_file(self, src, dst):
567        self.host.send_file(src, dst)
568
569    def get_ptksa(self, bssid, cipher):
570        res = self.request("PTKSA_CACHE_LIST")
571        lines = res.splitlines()
572        for l in lines:
573            if bssid not in l or cipher not in l:
574                continue
575            vals = dict()
576            [index, addr, cipher, expiration, tk, kdk] = l.split(' ', 5)
577            vals['index'] = index
578            vals['addr'] = addr
579            vals['cipher'] = cipher
580            vals['expiration'] = expiration
581            vals['tk'] = tk
582            vals['kdk'] = kdk
583            return vals
584        return None
585
586def add_ap(apdev, params, wait_enabled=True, no_enable=False, timeout=30,
587           global_ctrl_override=None, driver=False):
588        if isinstance(apdev, dict):
589            ifname = apdev['ifname']
590            try:
591                hostname = apdev['hostname']
592                port = apdev['port']
593                logger.info("Starting AP " + hostname + "/" + port + " " + ifname)
594            except:
595                logger.info("Starting AP " + ifname)
596                hostname = None
597                port = 8878
598        else:
599            ifname = apdev
600            logger.info("Starting AP " + ifname + " (old add_ap argument type)")
601            hostname = None
602            port = 8878
603        hapd_global = HostapdGlobal(apdev,
604                                    global_ctrl_override=global_ctrl_override)
605        hapd_global.remove(ifname)
606        hapd_global.add(ifname, driver=driver)
607        port = hapd_global.get_ctrl_iface_port(ifname)
608        hapd = Hostapd(ifname, hostname=hostname, port=port)
609        if not hapd.ping():
610            raise Exception("Could not ping hostapd")
611        hapd.set_defaults()
612        fields = ["ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt",
613                  "wpa", "wpa_deny_ptk0_rekey",
614                  "wpa_pairwise", "rsn_pairwise", "auth_server_addr",
615                  "acct_server_addr", "osu_server_uri"]
616        for field in fields:
617            if field in params:
618                hapd.set(field, params[field])
619        for f, v in list(params.items()):
620            if f in fields:
621                continue
622            if isinstance(v, list):
623                for val in v:
624                    hapd.set(f, val)
625            else:
626                hapd.set(f, v)
627        if no_enable:
628            return hapd
629        hapd.enable()
630        if wait_enabled:
631            ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=timeout)
632            if ev is None:
633                raise Exception("AP startup timed out")
634            if "AP-ENABLED" not in ev:
635                raise Exception("AP startup failed")
636        return hapd
637
638def add_bss(apdev, ifname, confname, ignore_error=False):
639    phy = utils.get_phy(apdev)
640    try:
641        hostname = apdev['hostname']
642        port = apdev['port']
643        logger.info("Starting BSS " + hostname + "/" + port + " phy=" + phy + " ifname=" + ifname)
644    except:
645        logger.info("Starting BSS phy=" + phy + " ifname=" + ifname)
646        hostname = None
647        port = 8878
648    hapd_global = HostapdGlobal(apdev)
649    confname = cfg_file(apdev, confname, ifname)
650    hapd_global.send_file(confname, confname)
651    hapd_global.add_bss(phy, confname, ignore_error)
652    port = hapd_global.get_ctrl_iface_port(ifname)
653    hapd = Hostapd(ifname, hostname=hostname, port=port)
654    if not hapd.ping():
655        raise Exception("Could not ping hostapd")
656    return hapd
657
658def add_iface(apdev, confname):
659    ifname = apdev['ifname']
660    try:
661        hostname = apdev['hostname']
662        port = apdev['port']
663        logger.info("Starting interface " + hostname + "/" + port + " " + ifname)
664    except:
665        logger.info("Starting interface " + ifname)
666        hostname = None
667        port = 8878
668    hapd_global = HostapdGlobal(apdev)
669    confname = cfg_file(apdev, confname, ifname)
670    hapd_global.send_file(confname, confname)
671    hapd_global.add_iface(ifname, confname)
672    port = hapd_global.get_ctrl_iface_port(ifname)
673    hapd = Hostapd(ifname, hostname=hostname, port=port)
674    if not hapd.ping():
675        raise Exception("Could not ping hostapd")
676    return hapd
677
678def remove_bss(apdev, ifname=None):
679    if ifname == None:
680        ifname = apdev['ifname']
681    try:
682        hostname = apdev['hostname']
683        port = apdev['port']
684        logger.info("Removing BSS " + hostname + "/" + port + " " + ifname)
685    except:
686        logger.info("Removing BSS " + ifname)
687    hapd_global = HostapdGlobal(apdev)
688    hapd_global.remove(ifname)
689
690def terminate(apdev):
691    try:
692        hostname = apdev['hostname']
693        port = apdev['port']
694        logger.info("Terminating hostapd " + hostname + "/" + port)
695    except:
696        logger.info("Terminating hostapd")
697    hapd_global = HostapdGlobal(apdev)
698    hapd_global.terminate()
699
700def wpa2_params(ssid=None, passphrase=None, wpa_key_mgmt="WPA-PSK",
701                ieee80211w=None):
702    params = {"wpa": "2",
703              "wpa_key_mgmt": wpa_key_mgmt,
704              "rsn_pairwise": "CCMP"}
705    if ssid:
706        params["ssid"] = ssid
707    if passphrase:
708        params["wpa_passphrase"] = passphrase
709    if ieee80211w is not None:
710        params["ieee80211w"] = ieee80211w
711    return params
712
713def wpa_params(ssid=None, passphrase=None):
714    params = {"wpa": "1",
715              "wpa_key_mgmt": "WPA-PSK",
716              "wpa_pairwise": "TKIP"}
717    if ssid:
718        params["ssid"] = ssid
719    if passphrase:
720        params["wpa_passphrase"] = passphrase
721    return params
722
723def wpa_mixed_params(ssid=None, passphrase=None):
724    params = {"wpa": "3",
725              "wpa_key_mgmt": "WPA-PSK",
726              "wpa_pairwise": "TKIP",
727              "rsn_pairwise": "CCMP"}
728    if ssid:
729        params["ssid"] = ssid
730    if passphrase:
731        params["wpa_passphrase"] = passphrase
732    return params
733
734def radius_params():
735    params = {"auth_server_addr": "127.0.0.1",
736              "auth_server_port": "1812",
737              "auth_server_shared_secret": "radius",
738              "nas_identifier": "nas.w1.fi"}
739    return params
740
741def wpa_eap_params(ssid=None):
742    params = radius_params()
743    params["wpa"] = "1"
744    params["wpa_key_mgmt"] = "WPA-EAP"
745    params["wpa_pairwise"] = "TKIP"
746    params["ieee8021x"] = "1"
747    if ssid:
748        params["ssid"] = ssid
749    return params
750
751def wpa2_eap_params(ssid=None):
752    params = radius_params()
753    params["wpa"] = "2"
754    params["wpa_key_mgmt"] = "WPA-EAP"
755    params["rsn_pairwise"] = "CCMP"
756    params["ieee8021x"] = "1"
757    if ssid:
758        params["ssid"] = ssid
759    return params
760
761def b_only_params(channel="1", ssid=None, country=None):
762    params = {"hw_mode": "b",
763              "channel": channel}
764    if ssid:
765        params["ssid"] = ssid
766    if country:
767        params["country_code"] = country
768    return params
769
770def g_only_params(channel="1", ssid=None, country=None):
771    params = {"hw_mode": "g",
772              "channel": channel}
773    if ssid:
774        params["ssid"] = ssid
775    if country:
776        params["country_code"] = country
777    return params
778
779def a_only_params(channel="36", ssid=None, country=None):
780    params = {"hw_mode": "a",
781              "channel": channel}
782    if ssid:
783        params["ssid"] = ssid
784    if country:
785        params["country_code"] = country
786    return params
787
788def ht20_params(channel="1", ssid=None, country=None):
789    params = {"ieee80211n": "1",
790              "channel": channel,
791              "hw_mode": "g"}
792    if int(channel) > 14:
793        params["hw_mode"] = "a"
794    if ssid:
795        params["ssid"] = ssid
796    if country:
797        params["country_code"] = country
798    return params
799
800def ht40_plus_params(channel="1", ssid=None, country=None):
801    params = ht20_params(channel, ssid, country)
802    params['ht_capab'] = "[HT40+]"
803    return params
804
805def ht40_minus_params(channel="1", ssid=None, country=None):
806    params = ht20_params(channel, ssid, country)
807    params['ht_capab'] = "[HT40-]"
808    return params
809
810def cmd_execute(apdev, cmd, shell=False):
811    hapd_global = HostapdGlobal(apdev)
812    return hapd_global.cmd_execute(cmd, shell=shell)
813
814def send_file(apdev, src, dst):
815    hapd_global = HostapdGlobal(apdev)
816    return hapd_global.send_file(src, dst)
817
818def acl_file(dev, apdev, conf):
819    fd, filename = tempfile.mkstemp(dir='/tmp', prefix=conf + '-')
820    f = os.fdopen(fd, 'w')
821
822    if conf == 'hostapd.macaddr':
823        mac0 = dev[0].get_status_field("address")
824        f.write(mac0 + '\n')
825        f.write("02:00:00:00:00:12\n")
826        f.write("02:00:00:00:00:34\n")
827        f.write("-02:00:00:00:00:12\n")
828        f.write("-02:00:00:00:00:34\n")
829        f.write("01:01:01:01:01:01\n")
830        f.write("03:01:01:01:01:03\n")
831    elif conf == 'hostapd.accept':
832        mac0 = dev[0].get_status_field("address")
833        mac1 = dev[1].get_status_field("address")
834        f.write(mac0 + "    1\n")
835        f.write(mac1 + "    2\n")
836    elif conf == 'hostapd.accept2':
837        mac0 = dev[0].get_status_field("address")
838        mac1 = dev[1].get_status_field("address")
839        mac2 = dev[2].get_status_field("address")
840        f.write(mac0 + "    1\n")
841        f.write(mac1 + "    2\n")
842        f.write(mac2 + "    3\n")
843    else:
844        f.close()
845        os.unlink(filename)
846        return conf
847
848    return filename
849
850def bssid_inc(apdev, inc=1):
851    parts = apdev['bssid'].split(':')
852    parts[5] = '%02x' % (int(parts[5], 16) + int(inc))
853    bssid = '%s:%s:%s:%s:%s:%s' % (parts[0], parts[1], parts[2],
854                                   parts[3], parts[4], parts[5])
855    return bssid
856
857def cfg_file(apdev, conf, ifname=None):
858    match = re.search(r'^bss-.+', conf)
859    if match:
860        # put cfg file in /tmp directory
861        fd, fname = tempfile.mkstemp(dir='/tmp', prefix=conf + '-')
862        f = os.fdopen(fd, 'w')
863        idx = ''.join(filter(str.isdigit, conf.split('-')[-1]))
864        if ifname is None:
865            ifname = apdev['ifname']
866            if idx != '1':
867                ifname = ifname + '-' + idx
868
869        f.write("driver=nl80211\n")
870        f.write("ctrl_interface=/var/run/hostapd\n")
871        f.write("hw_mode=g\n")
872        f.write("channel=1\n")
873        f.write("ieee80211n=1\n")
874        if conf.startswith('bss-ht40-'):
875            f.write("ht_capab=[HT40+]\n")
876        f.write("interface=%s\n" % ifname)
877
878        f.write("ssid=bss-%s\n" % idx)
879        if conf == 'bss-2-dup.conf':
880            bssid = apdev['bssid']
881        else:
882            bssid = bssid_inc(apdev, int(idx) - 1)
883        f.write("bssid=%s\n" % bssid)
884
885        return fname
886
887    return conf
888