1# Dynamic wpa_supplicant interface
2# Copyright (c) 2013, 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 logging
8logger = logging.getLogger()
9import subprocess
10import time
11
12import hwsim_utils
13import hostapd
14from wpasupplicant import WpaSupplicant
15
16def test_sta_dynamic(dev, apdev):
17    """Dynamically added wpa_supplicant interface"""
18    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
19    hostapd.add_ap(apdev[0], params)
20
21    logger.info("Create a dynamic wpa_supplicant interface and connect")
22    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
23    wpas.interface_add("wlan5")
24
25    wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
26
27def test_sta_ap_scan_0(dev, apdev):
28    """Dynamically added wpa_supplicant interface with AP_SCAN 0 connection"""
29    hostapd.add_ap(apdev[0], {"ssid": "test"})
30    bssid = apdev[0]['bssid']
31
32    logger.info("Create a dynamic wpa_supplicant interface and connect")
33    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
34    wpas.interface_add("wlan5")
35
36    if "OK" not in wpas.request("AP_SCAN 0"):
37        raise Exception("Failed to set AP_SCAN 2")
38
39    id = wpas.connect("", key_mgmt="NONE", bssid=bssid,
40                      only_add_network=True)
41    wpas.request("ENABLE_NETWORK " + str(id) + " no-connect")
42    wpas.request("SCAN")
43    time.sleep(0.5)
44    subprocess.call(['iw', wpas.ifname, 'connect', 'test', '2412'])
45    wpas.wait_connected(timeout=10)
46    wpas.request("SCAN")
47    wpas.wait_connected(timeout=5)
48
49def test_sta_ap_scan_2(dev, apdev):
50    """Dynamically added wpa_supplicant interface with AP_SCAN 2 connection"""
51    hostapd.add_ap(apdev[0], {"ssid": "test"})
52    bssid = apdev[0]['bssid']
53
54    logger.info("Create a dynamic wpa_supplicant interface and connect")
55    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
56    wpas.interface_add("wlan5")
57
58    if "FAIL" not in wpas.request("AP_SCAN -1"):
59        raise Exception("Invalid AP_SCAN -1 accepted")
60    if "FAIL" not in wpas.request("AP_SCAN 3"):
61        raise Exception("Invalid AP_SCAN 3 accepted")
62    if "OK" not in wpas.request("AP_SCAN 2"):
63        raise Exception("Failed to set AP_SCAN 2")
64
65    id = wpas.connect("", key_mgmt="NONE", bssid=bssid,
66                      only_add_network=True)
67    wpas.request("ENABLE_NETWORK " + str(id) + " no-connect")
68    subprocess.call(['iw', wpas.ifname, 'scan', 'trigger', 'freq', '2412'])
69    time.sleep(1)
70    subprocess.call(['iw', wpas.ifname, 'connect', 'test', '2412'])
71    wpas.wait_connected(timeout=10)
72
73    wpas.request("SET disallow_aps bssid " + bssid)
74    wpas.wait_disconnected(timeout=10)
75
76    subprocess.call(['iw', wpas.ifname, 'connect', 'test', '2412'])
77    ev = wpas.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
78    if ev is not None:
79        raise Exception("Unexpected connection reported")
80
81def test_sta_ap_scan_2b(dev, apdev):
82    """Dynamically added wpa_supplicant interface with AP_SCAN 2 operation"""
83    hapd = hostapd.add_ap(apdev[0], {"ssid": "test"})
84    bssid = apdev[0]['bssid']
85
86    logger.info("Create a dynamic wpa_supplicant interface and connect")
87    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
88    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
89
90    if "OK" not in wpas.request("AP_SCAN 2"):
91        raise Exception("Failed to set AP_SCAN 2")
92
93    id = wpas.connect("test", key_mgmt="NONE", bssid=bssid)
94    wpas.request("DISCONNECT")
95    wpas.set_network(id, "disabled", "1")
96    id2 = wpas.add_network()
97    wpas.set_network_quoted(id2, "ssid", "test2")
98    wpas.set_network(id2, "key_mgmt", "NONE")
99    wpas.set_network(id2, "disabled", "0")
100    wpas.request("REASSOCIATE")
101    ev = wpas.wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=15)
102    if ev is None:
103        raise Exception("Association rejection not reported")
104    hapd.disable()
105    wpas.set_network(id, "disabled", "0")
106    wpas.set_network(id2, "disabled", "1")
107    for i in range(3):
108        ev = wpas.wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=15)
109        if ev is None:
110            raise Exception("Association rejection not reported")
111    wpas.request("DISCONNECT")
112
113def test_sta_dynamic_down_up(dev, apdev):
114    """Dynamically added wpa_supplicant interface down/up"""
115    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
116    hapd = hostapd.add_ap(apdev[0], params)
117
118    logger.info("Create a dynamic wpa_supplicant interface and connect")
119    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
120    wpas.interface_add("wlan5")
121    wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
122    hapd.wait_sta()
123    hwsim_utils.test_connectivity(wpas, hapd)
124    subprocess.call(['ifconfig', wpas.ifname, 'down'])
125    wpas.wait_disconnected(timeout=10)
126    if wpas.get_status_field("wpa_state") != "INTERFACE_DISABLED":
127        raise Exception("Unexpected wpa_state")
128    subprocess.call(['ifconfig', wpas.ifname, 'up'])
129    wpas.wait_connected(timeout=15, error="Reconnection not reported")
130    hapd.wait_sta()
131    hwsim_utils.test_connectivity(wpas, hapd)
132
133def test_sta_dynamic_ext_mac_addr_change(dev, apdev):
134    """Dynamically added wpa_supplicant interface with external MAC address change"""
135    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
136    hapd = hostapd.add_ap(apdev[0], params)
137
138    logger.info("Create a dynamic wpa_supplicant interface and connect")
139    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
140    wpas.interface_add("wlan5")
141    wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
142    hapd.wait_sta()
143    hwsim_utils.test_connectivity(wpas, hapd)
144    subprocess.call(['ifconfig', wpas.ifname, 'down'])
145    wpas.wait_disconnected(timeout=10)
146    if wpas.get_status_field("wpa_state") != "INTERFACE_DISABLED":
147        raise Exception("Unexpected wpa_state")
148    prev_addr = wpas.p2p_interface_addr()
149    new_addr = '02:11:22:33:44:55'
150    try:
151        subprocess.call(['ip', 'link', 'set', 'dev', wpas.ifname,
152                         'address', new_addr])
153        subprocess.call(['ifconfig', wpas.ifname, 'up'])
154        wpas.wait_connected(timeout=15, error="Reconnection not reported")
155        if wpas.get_driver_status_field('addr') != new_addr:
156            raise Exception("Address change not reported")
157        hapd.wait_sta()
158        hwsim_utils.test_connectivity(wpas, hapd)
159        sta = hapd.get_sta(new_addr)
160        if sta['addr'] != new_addr:
161            raise Exception("STA association with new address not found")
162    finally:
163        subprocess.call(['ifconfig', wpas.ifname, 'down'])
164        subprocess.call(['ip', 'link', 'set', 'dev', wpas.ifname,
165                         'address', prev_addr])
166        subprocess.call(['ifconfig', wpas.ifname, 'up'])
167
168def test_sta_dynamic_ext_mac_addr_change_for_connection(dev, apdev):
169    """Dynamically added wpa_supplicant interface with external MAC address change for connection"""
170    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
171    hapd = hostapd.add_ap(apdev[0], params)
172    bssid = apdev[0]['ifname']
173
174    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
175    wpas.interface_add("wlan5")
176    wpas.scan_for_bss(bssid, freq=2412)
177    subprocess.call(['ifconfig', wpas.ifname, 'down'])
178    if wpas.get_status_field("wpa_state") != "INTERFACE_DISABLED":
179        raise Exception("Unexpected wpa_state")
180    prev_addr = wpas.own_addr()
181    new_addr = '02:11:22:33:44:55'
182    try:
183        subprocess.call(['ip', 'link', 'set', 'dev', wpas.ifname,
184                         'address', new_addr])
185        subprocess.call(['ifconfig', wpas.ifname, 'up'])
186        wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412",
187                     wait_connect=False)
188        ev = wpas.wait_event(["CTRL-EVENT-CONNECTED",
189                              "CTRL-EVENT-SCAN-RESULTS"], timeout=10)
190        if "CTRL-EVENT-SCAN-RESULTS" in ev:
191            raise Exception("Unexpected scan after MAC address change")
192        hapd.wait_sta()
193        hwsim_utils.test_connectivity(wpas, hapd)
194        sta = hapd.get_sta(new_addr)
195        if sta['addr'] != new_addr:
196            raise Exception("STA association with new address not found")
197        wpas.request("DISCONNECT")
198        wpas.wait_disconnected()
199        wpas.dump_monitor()
200        subprocess.call(['ifconfig', wpas.ifname, 'down'])
201        time.sleep(0.1)
202        res = wpas.get_bss(bssid)
203        if res is None:
204            raise Exception("BSS entry not maintained after interface disabling")
205        ev = wpas.wait_event(["CTRL-EVENT-BSS-REMOVED"], timeout=5.5)
206        if ev is None:
207            raise Exception("BSS entry not removed after interface has been disabled for a while")
208        res2 = wpas.get_bss(bssid)
209        if res2 is not None:
210            raise Exception("Unexpected BSS entry found on a disabled interface")
211    finally:
212        subprocess.call(['ifconfig', wpas.ifname, 'down'])
213        subprocess.call(['ip', 'link', 'set', 'dev', wpas.ifname,
214                         'address', prev_addr])
215        subprocess.call(['ifconfig', wpas.ifname, 'up'])
216
217def test_sta_dynamic_random_mac_addr(dev, apdev):
218    """Dynamically added wpa_supplicant interface and random MAC address"""
219    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
220    hapd = hostapd.add_ap(apdev[0], params)
221
222    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
223    wpas.interface_add("wlan5")
224    addr0 = wpas.get_driver_status_field("addr")
225    wpas.request("SET preassoc_mac_addr 1")
226    wpas.request("SET rand_addr_lifetime 0")
227
228    id = wpas.connect("sta-dynamic", psk="12345678", mac_addr="1",
229                      scan_freq="2412")
230    addr1 = wpas.get_driver_status_field("addr")
231
232    if addr0 == addr1:
233        raise Exception("Random MAC address not used")
234
235    sta = hapd.get_sta(addr0)
236    if sta['addr'] != "FAIL":
237        raise Exception("Unexpected STA association with permanent address")
238    sta = hapd.get_sta(addr1)
239    if sta['addr'] != addr1:
240        raise Exception("STA association with random address not found")
241
242    wpas.request("DISCONNECT")
243    wpas.connect_network(id)
244    addr2 = wpas.get_driver_status_field("addr")
245    if addr1 != addr2:
246        raise Exception("Random MAC address changed unexpectedly")
247
248    wpas.remove_network(id)
249    id = wpas.connect("sta-dynamic", psk="12345678", mac_addr="1",
250                      scan_freq="2412")
251    addr2 = wpas.get_driver_status_field("addr")
252    if addr1 == addr2:
253        raise Exception("Random MAC address did not change")
254
255def test_sta_dynamic_random_mac_addr_keep_oui(dev, apdev):
256    """Dynamically added wpa_supplicant interface and random MAC address (keep OUI)"""
257    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
258    hapd = hostapd.add_ap(apdev[0], params)
259
260    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
261    wpas.interface_add("wlan5")
262    addr0 = wpas.get_driver_status_field("addr")
263    wpas.request("SET preassoc_mac_addr 2")
264    wpas.request("SET rand_addr_lifetime 0")
265
266    id = wpas.connect("sta-dynamic", psk="12345678", mac_addr="2",
267                      scan_freq="2412")
268    addr1 = wpas.get_driver_status_field("addr")
269
270    if addr0 == addr1:
271        raise Exception("Random MAC address not used")
272    if addr1[3:8] != addr0[3:8]:
273        raise Exception("OUI was not kept")
274
275    sta = hapd.get_sta(addr0)
276    if sta['addr'] != "FAIL":
277        raise Exception("Unexpected STA association with permanent address")
278    sta = hapd.get_sta(addr1)
279    if sta['addr'] != addr1:
280        raise Exception("STA association with random address not found")
281
282    wpas.request("DISCONNECT")
283    wpas.connect_network(id)
284    addr2 = wpas.get_driver_status_field("addr")
285    if addr1 != addr2:
286        raise Exception("Random MAC address changed unexpectedly")
287
288    wpas.remove_network(id)
289    id = wpas.connect("sta-dynamic", psk="12345678", mac_addr="2",
290                      scan_freq="2412")
291    addr2 = wpas.get_driver_status_field("addr")
292    if addr1 == addr2:
293        raise Exception("Random MAC address did not change")
294    if addr2[3:8] != addr0[3:8]:
295        raise Exception("OUI was not kept")
296
297def test_sta_dynamic_random_mac_addr_scan(dev, apdev):
298    """Dynamically added wpa_supplicant interface and random MAC address for scan"""
299    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
300    hapd = hostapd.add_ap(apdev[0], params)
301
302    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
303    wpas.interface_add("wlan5")
304    addr0 = wpas.get_driver_status_field("addr")
305    wpas.request("SET preassoc_mac_addr 1")
306    wpas.request("SET rand_addr_lifetime 0")
307
308    id = wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
309    addr1 = wpas.get_driver_status_field("addr")
310
311    if addr0 != addr1:
312        raise Exception("Random MAC address used unexpectedly")
313
314def test_sta_dynamic_random_mac_addr_scan_keep_oui(dev, apdev):
315    """Dynamically added wpa_supplicant interface and random MAC address for scan (keep OUI)"""
316    params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
317    hapd = hostapd.add_ap(apdev[0], params)
318
319    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
320    wpas.interface_add("wlan5")
321    addr0 = wpas.get_driver_status_field("addr")
322    wpas.request("SET preassoc_mac_addr 2")
323    wpas.request("SET rand_addr_lifetime 0")
324
325    id = wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
326    addr1 = wpas.get_driver_status_field("addr")
327
328    if addr0 != addr1:
329        raise Exception("Random MAC address used unexpectedly")
330