]>
Commit | Line | Data |
---|---|---|
ebc61dc2 JM |
1 | # GAS tests |
2 | # Copyright (c) 2013, Qualcomm Atheros, Inc. | |
6d01255e | 3 | # Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi> |
ebc61dc2 JM |
4 | # |
5 | # This software may be distributed under the terms of the BSD license. | |
6 | # See README for more details. | |
7 | ||
9fd6804d | 8 | from remotehost import remote_compatible |
ebc61dc2 | 9 | import time |
bfe375ec | 10 | import binascii |
ebc61dc2 | 11 | import logging |
c9aa4308 | 12 | logger = logging.getLogger() |
61854f16 | 13 | import os |
ebc61dc2 | 14 | import re |
d9474958 | 15 | import struct |
ebc61dc2 JM |
16 | |
17 | import hostapd | |
1d1de230 | 18 | from wpasupplicant import WpaSupplicant |
61854f16 | 19 | from tshark import run_tshark |
16f91791 | 20 | from utils import alloc_fail, wait_fail_trigger, skip_with_fips, HwsimSkip |
70f2a3f4 | 21 | from hwsim import HWSimRadio |
ebc61dc2 JM |
22 | |
23 | def hs20_ap_params(): | |
24 | params = hostapd.wpa2_params(ssid="test-gas") | |
25 | params['wpa_key_mgmt'] = "WPA-EAP" | |
26 | params['ieee80211w'] = "1" | |
27 | params['ieee8021x'] = "1" | |
28 | params['auth_server_addr'] = "127.0.0.1" | |
29 | params['auth_server_port'] = "1812" | |
30 | params['auth_server_shared_secret'] = "radius" | |
31 | params['interworking'] = "1" | |
32 | params['access_network_type'] = "14" | |
33 | params['internet'] = "1" | |
34 | params['asra'] = "0" | |
35 | params['esr'] = "0" | |
36 | params['uesa'] = "0" | |
37 | params['venue_group'] = "7" | |
38 | params['venue_type'] = "1" | |
39 | params['venue_name'] = [ "eng:Example venue", "fin:Esimerkkipaikka" ] | |
40 | params['roaming_consortium'] = [ "112233", "1020304050", "010203040506", | |
41 | "fedcba" ] | |
42 | params['domain_name'] = "example.com,another.example.com" | |
43 | params['nai_realm'] = [ "0,example.com,13[5:6],21[2:4][5:7]", | |
44 | "0,another.example.com" ] | |
45 | params['anqp_3gpp_cell_net'] = "244,91" | |
cef16c47 JM |
46 | params['network_auth_type'] = "02http://www.example.com/redirect/me/here/" |
47 | params['ipaddr_type_availability'] = "14" | |
48 | params['hs20'] = "1" | |
49 | params['hs20_oper_friendly_name'] = [ "eng:Example operator", "fin:Esimerkkioperaattori" ] | |
50 | params['hs20_wan_metrics'] = "01:8000:1000:80:240:3000" | |
51 | params['hs20_conn_capab'] = [ "1:0:2", "6:22:1", "17:5060:0" ] | |
52 | params['hs20_operating_class'] = "5173" | |
ebc61dc2 JM |
53 | return params |
54 | ||
18dd2af0 JM |
55 | def start_ap(ap): |
56 | params = hs20_ap_params() | |
57 | params['hessid'] = ap['bssid'] | |
6f334bf7 | 58 | return hostapd.add_ap(ap, params) |
18dd2af0 | 59 | |
5e80b502 JM |
60 | def get_gas_response(dev, bssid, info, allow_fetch_failure=False, |
61 | extra_test=False): | |
ebc61dc2 JM |
62 | exp = r'<.>(GAS-RESPONSE-INFO) addr=([0-9a-f:]*) dialog_token=([0-9]*) status_code=([0-9]*) resp_len=([\-0-9]*)' |
63 | res = re.split(exp, info) | |
64 | if len(res) < 6: | |
65 | raise Exception("Could not parse GAS-RESPONSE-INFO") | |
66 | if res[2] != bssid: | |
67 | raise Exception("Unexpected BSSID in response") | |
68 | token = res[3] | |
69 | status = res[4] | |
70 | if status != "0": | |
71 | raise Exception("GAS query failed") | |
72 | resp_len = res[5] | |
73 | if resp_len == "-1": | |
74 | raise Exception("GAS query reported invalid response length") | |
75 | if int(resp_len) > 2000: | |
76 | raise Exception("Unexpected long GAS response") | |
77 | ||
5e80b502 JM |
78 | if extra_test: |
79 | if "FAIL" not in dev.request("GAS_RESPONSE_GET " + bssid + " 123456"): | |
80 | raise Exception("Invalid dialog token accepted") | |
81 | if "FAIL-Invalid range" not in dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 10000,10001"): | |
82 | raise Exception("Invalid range accepted") | |
83 | if "FAIL-Invalid range" not in dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 0,10000"): | |
84 | raise Exception("Invalid range accepted") | |
85 | if "FAIL" not in dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 0"): | |
86 | raise Exception("Invalid GAS_RESPONSE_GET accepted") | |
87 | ||
88 | res1_2 = dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 1,2") | |
89 | res5_3 = dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 5,3") | |
90 | ||
ebc61dc2 JM |
91 | resp = dev.request("GAS_RESPONSE_GET " + bssid + " " + token) |
92 | if "FAIL" in resp: | |
93 | if allow_fetch_failure: | |
94 | logger.debug("GAS response was not available anymore") | |
95 | return | |
96 | raise Exception("Could not fetch GAS response") | |
97 | if len(resp) != int(resp_len) * 2: | |
98 | raise Exception("Unexpected GAS response length") | |
99 | logger.debug("GAS response: " + resp) | |
5e80b502 JM |
100 | if extra_test: |
101 | if resp[2:6] != res1_2: | |
102 | raise Exception("Unexpected response substring res1_2: " + res1_2) | |
103 | if resp[10:16] != res5_3: | |
104 | raise Exception("Unexpected response substring res5_3: " + res5_3) | |
ebc61dc2 JM |
105 | |
106 | def test_gas_generic(dev, apdev): | |
107 | """Generic GAS query""" | |
108 | bssid = apdev[0]['bssid'] | |
109 | params = hs20_ap_params() | |
110 | params['hessid'] = bssid | |
8b8a1864 | 111 | hostapd.add_ap(apdev[0], params) |
ebc61dc2 | 112 | |
5e80b502 JM |
113 | cmds = [ "foo", |
114 | "00:11:22:33:44:55", | |
115 | "00:11:22:33:44:55 ", | |
116 | "00:11:22:33:44:55 ", | |
117 | "00:11:22:33:44:55 1", | |
118 | "00:11:22:33:44:55 1 1234", | |
119 | "00:11:22:33:44:55 qq", | |
120 | "00:11:22:33:44:55 qq 1234", | |
121 | "00:11:22:33:44:55 00 1", | |
122 | "00:11:22:33:44:55 00 123", | |
123 | "00:11:22:33:44:55 00 ", | |
124 | "00:11:22:33:44:55 00 qq" ] | |
125 | for cmd in cmds: | |
126 | if "FAIL" not in dev[0].request("GAS_REQUEST " + cmd): | |
127 | raise Exception("Invalid GAS_REQUEST accepted: " + cmd) | |
128 | ||
4ef70531 | 129 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) |
ebc61dc2 JM |
130 | req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101") |
131 | if "FAIL" in req: | |
132 | raise Exception("GAS query request rejected") | |
133 | ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10) | |
134 | if ev is None: | |
135 | raise Exception("GAS query timed out") | |
5e80b502 JM |
136 | get_gas_response(dev[0], bssid, ev, extra_test=True) |
137 | ||
138 | if "FAIL" not in dev[0].request("GAS_RESPONSE_GET ff"): | |
139 | raise Exception("Invalid GAS_RESPONSE_GET accepted") | |
ebc61dc2 | 140 | |
16f91791 VK |
141 | def test_gas_rand_ta(dev, apdev, params): |
142 | """Generic GAS query with random TA""" | |
143 | flags = int(dev[0].get_driver_status_field('capa.flags'), 16) | |
144 | if flags & 0x0000400000000000 == 0: | |
145 | raise HwsimSkip("Driver does not support random GAS TA") | |
146 | ||
147 | try: | |
148 | _test_gas_rand_ta(dev, apdev, params['logdir']) | |
149 | finally: | |
150 | dev[0].request("SET gas_rand_mac_addr 0") | |
151 | ||
152 | def _test_gas_rand_ta(dev, apdev, logdir): | |
153 | bssid = apdev[0]['bssid'] | |
154 | params = hs20_ap_params() | |
155 | params['hessid'] = bssid | |
156 | hostapd.add_ap(apdev[0], params) | |
157 | ||
158 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
159 | req = dev[0].request("SET gas_rand_mac_addr 1") | |
160 | if "FAIL" in req: | |
161 | raise Exception("Failed to set gas_rand_mac_addr") | |
162 | ||
163 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
164 | req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101") | |
165 | if "FAIL" in req: | |
166 | raise Exception("GAS query request rejected") | |
167 | ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10) | |
168 | if ev is None: | |
169 | raise Exception("GAS query timed out") | |
170 | get_gas_response(dev[0], bssid, ev, extra_test=True) | |
171 | ||
172 | out = run_tshark(os.path.join(logdir, "hwsim0.pcapng"), | |
173 | "wlan_mgt.fixed.category_code == 4 && (wlan_mgt.fixed.publicact == 0x0a || wlan_mgt.fixed.publicact == 0x0b)", | |
174 | display=["wlan.ta", "wlan.ra"]) | |
175 | res = out.splitlines() | |
176 | if len(res) != 2: | |
177 | raise Exception("Unexpected number of GAS frames") | |
178 | req_ta = res[0].split('\t')[0] | |
179 | resp_ra = res[1].split('\t')[1] | |
180 | logger.info("Request TA: %s, Response RA: %s" % (req_ta, resp_ra)) | |
181 | if req_ta != resp_ra: | |
182 | raise Exception("Request TA does not match response RA") | |
183 | if req_ta == dev[0].own_addr(): | |
184 | raise Exception("Request TA was own permanent MAC address, not random") | |
185 | ||
ebc61dc2 JM |
186 | def test_gas_concurrent_scan(dev, apdev): |
187 | """Generic GAS queries with concurrent scan operation""" | |
188 | bssid = apdev[0]['bssid'] | |
189 | params = hs20_ap_params() | |
190 | params['hessid'] = bssid | |
8b8a1864 | 191 | hostapd.add_ap(apdev[0], params) |
ebc61dc2 | 192 | |
d7a99700 | 193 | # get BSS entry available to allow GAS query |
4ef70531 | 194 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) |
ebc61dc2 JM |
195 | |
196 | logger.info("Request concurrent operations") | |
197 | req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101") | |
198 | if "FAIL" in req: | |
199 | raise Exception("GAS query request rejected") | |
200 | req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000801") | |
201 | if "FAIL" in req: | |
202 | raise Exception("GAS query request rejected") | |
d7a99700 | 203 | dev[0].scan(no_wait=True) |
ebc61dc2 JM |
204 | req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000201") |
205 | if "FAIL" in req: | |
206 | raise Exception("GAS query request rejected") | |
207 | req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000501") | |
208 | if "FAIL" in req: | |
209 | raise Exception("GAS query request rejected") | |
210 | ||
211 | responses = 0 | |
212 | for i in range(0, 5): | |
213 | ev = dev[0].wait_event(["GAS-RESPONSE-INFO", "CTRL-EVENT-SCAN-RESULTS"], | |
214 | timeout=10) | |
215 | if ev is None: | |
216 | raise Exception("Operation timed out") | |
217 | if "GAS-RESPONSE-INFO" in ev: | |
218 | responses = responses + 1 | |
219 | get_gas_response(dev[0], bssid, ev, allow_fetch_failure=True) | |
220 | ||
221 | if responses != 4: | |
222 | raise Exception("Unexpected number of GAS responses") | |
223 | ||
224 | def test_gas_concurrent_connect(dev, apdev): | |
225 | """Generic GAS queries with concurrent connection operation""" | |
ca158ea6 | 226 | skip_with_fips(dev[0]) |
ebc61dc2 JM |
227 | bssid = apdev[0]['bssid'] |
228 | params = hs20_ap_params() | |
229 | params['hessid'] = bssid | |
8b8a1864 | 230 | hostapd.add_ap(apdev[0], params) |
ebc61dc2 | 231 | |
4ef70531 | 232 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) |
ebc61dc2 JM |
233 | |
234 | logger.debug("Start concurrent connect and GAS request") | |
235 | dev[0].connect("test-gas", key_mgmt="WPA-EAP", eap="TTLS", | |
236 | identity="DOMAIN\mschapv2 user", anonymous_identity="ttls", | |
237 | password="password", phase2="auth=MSCHAPV2", | |
d7a99700 JM |
238 | ca_cert="auth_serv/ca.pem", wait_connect=False, |
239 | scan_freq="2412") | |
ebc61dc2 JM |
240 | req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101") |
241 | if "FAIL" in req: | |
242 | raise Exception("GAS query request rejected") | |
243 | ||
244 | ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "GAS-RESPONSE-INFO"], | |
245 | timeout=20) | |
246 | if ev is None: | |
247 | raise Exception("Operation timed out") | |
248 | if "CTRL-EVENT-CONNECTED" not in ev: | |
249 | raise Exception("Unexpected operation order") | |
250 | ||
251 | ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "GAS-RESPONSE-INFO"], | |
252 | timeout=20) | |
253 | if ev is None: | |
254 | raise Exception("Operation timed out") | |
255 | if "GAS-RESPONSE-INFO" not in ev: | |
256 | raise Exception("Unexpected operation order") | |
257 | get_gas_response(dev[0], bssid, ev) | |
258 | ||
259 | dev[0].request("DISCONNECT") | |
5f35a5e2 | 260 | dev[0].wait_disconnected(timeout=5) |
ebc61dc2 JM |
261 | |
262 | logger.debug("Wait six seconds for expiration of connect-without-scan") | |
263 | time.sleep(6) | |
adf277a0 | 264 | dev[0].dump_monitor() |
ebc61dc2 JM |
265 | |
266 | logger.debug("Start concurrent GAS request and connect") | |
267 | req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101") | |
268 | if "FAIL" in req: | |
269 | raise Exception("GAS query request rejected") | |
270 | dev[0].request("RECONNECT") | |
271 | ||
272 | ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10) | |
273 | if ev is None: | |
274 | raise Exception("Operation timed out") | |
275 | get_gas_response(dev[0], bssid, ev) | |
276 | ||
277 | ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=20) | |
278 | if ev is None: | |
279 | raise Exception("No new scan results reported") | |
280 | ||
5f35a5e2 | 281 | ev = dev[0].wait_connected(timeout=20, error="Operation tiemd out") |
ebc61dc2 JM |
282 | if "CTRL-EVENT-CONNECTED" not in ev: |
283 | raise Exception("Unexpected operation order") | |
836a3745 | 284 | |
f2dfb1da JM |
285 | def gas_fragment_and_comeback(dev, apdev, frag_limit=0, comeback_delay=0): |
286 | hapd = start_ap(apdev) | |
287 | if frag_limit: | |
288 | hapd.set("gas_frag_limit", str(frag_limit)) | |
289 | if comeback_delay: | |
290 | hapd.set("gas_comeback_delay", str(comeback_delay)) | |
291 | ||
292 | dev.scan_for_bss(apdev['bssid'], freq="2412", force_scan=True) | |
293 | dev.request("FETCH_ANQP") | |
294 | ev = dev.wait_event(["GAS-QUERY-DONE"], timeout=5) | |
fee39910 JM |
295 | if ev is None: |
296 | raise Exception("No GAS-QUERY-DONE event") | |
297 | if "result=SUCCESS" not in ev: | |
298 | raise Exception("Unexpected GAS result: " + ev) | |
cef16c47 | 299 | for i in range(0, 13): |
f2dfb1da | 300 | ev = dev.wait_event(["RX-ANQP", "RX-HS20-ANQP"], timeout=5) |
e4a44b3c JM |
301 | if ev is None: |
302 | raise Exception("Operation timed out") | |
f2dfb1da | 303 | ev = dev.wait_event(["ANQP-QUERY-DONE"], timeout=1) |
fee39910 JM |
304 | if ev is None: |
305 | raise Exception("No ANQP-QUERY-DONE event") | |
306 | if "result=SUCCESS" not in ev: | |
307 | raise Exception("Unexpected ANQP result: " + ev) | |
e4a44b3c | 308 | |
f2dfb1da JM |
309 | def test_gas_fragment(dev, apdev): |
310 | """GAS fragmentation""" | |
311 | gas_fragment_and_comeback(dev[0], apdev[0], frag_limit=50) | |
312 | ||
70f2a3f4 JM |
313 | def test_gas_fragment_mcc(dev, apdev): |
314 | """GAS fragmentation with mac80211_hwsim MCC enabled""" | |
315 | with HWSimRadio(n_channels=2) as (radio, iface): | |
316 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
317 | wpas.interface_add(iface) | |
f2dfb1da JM |
318 | gas_fragment_and_comeback(wpas, apdev[0], frag_limit=50) |
319 | ||
320 | def test_gas_fragment_with_comeback_delay(dev, apdev): | |
321 | """GAS fragmentation and comeback delay""" | |
322 | gas_fragment_and_comeback(dev[0], apdev[0], frag_limit=50, | |
323 | comeback_delay=500) | |
324 | ||
325 | def test_gas_fragment_with_comeback_delay_mcc(dev, apdev): | |
326 | """GAS fragmentation and comeback delay with mac80211_hwsim MCC enabled""" | |
327 | with HWSimRadio(n_channels=2) as (radio, iface): | |
328 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
329 | wpas.interface_add(iface) | |
330 | gas_fragment_and_comeback(wpas, apdev[0], frag_limit=50, | |
331 | comeback_delay=500) | |
70f2a3f4 | 332 | |
e4a44b3c | 333 | def test_gas_comeback_delay(dev, apdev): |
5a145a55 | 334 | """GAS comeback delay""" |
1d2e6a45 JM |
335 | run_gas_comeback_delay(dev, apdev, 500) |
336 | ||
337 | def test_gas_comeback_delay_long(dev, apdev): | |
338 | """GAS long comeback delay""" | |
339 | run_gas_comeback_delay(dev, apdev, 2500) | |
340 | ||
0d7b2e90 JM |
341 | def test_gas_comeback_delay_long2(dev, apdev): |
342 | """GAS long comeback delay over default STA timeout""" | |
343 | run_gas_comeback_delay(dev, apdev, 6000) | |
344 | ||
1d2e6a45 | 345 | def run_gas_comeback_delay(dev, apdev, delay): |
18dd2af0 | 346 | hapd = start_ap(apdev[0]) |
1d2e6a45 | 347 | hapd.set("gas_comeback_delay", str(delay)) |
e4a44b3c | 348 | |
4ef70531 | 349 | dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True) |
836a3745 | 350 | dev[0].request("FETCH_ANQP") |
5a145a55 JM |
351 | if "FAIL-BUSY" not in dev[0].request("SCAN"): |
352 | raise Exception("SCAN accepted during FETCH_ANQP") | |
836a3745 | 353 | for i in range(0, 6): |
0d7b2e90 | 354 | ev = dev[0].wait_event(["RX-ANQP"], timeout=10) |
836a3745 JM |
355 | if ev is None: |
356 | raise Exception("Operation timed out") | |
2cace98e | 357 | |
9fd6804d | 358 | @remote_compatible |
ea27f662 JM |
359 | def test_gas_stop_fetch_anqp(dev, apdev): |
360 | """Stop FETCH_ANQP operation""" | |
361 | hapd = start_ap(apdev[0]) | |
362 | ||
363 | dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True) | |
364 | hapd.set("ext_mgmt_frame_handling", "1") | |
365 | dev[0].request("FETCH_ANQP") | |
366 | dev[0].request("STOP_FETCH_ANQP") | |
367 | hapd.set("ext_mgmt_frame_handling", "0") | |
368 | ev = dev[0].wait_event(["RX-ANQP", "GAS-QUERY-DONE"], timeout=10) | |
369 | if ev is None: | |
370 | raise Exception("GAS-QUERY-DONE timed out") | |
371 | if "RX-ANQP" in ev: | |
372 | raise Exception("Unexpected ANQP response received") | |
373 | ||
e56e286d JM |
374 | def test_gas_anqp_get(dev, apdev): |
375 | """GAS/ANQP query for both IEEE 802.11 and Hotspot 2.0 elements""" | |
376 | hapd = start_ap(apdev[0]) | |
377 | bssid = apdev[0]['bssid'] | |
378 | ||
4ef70531 JM |
379 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) |
380 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258,268,hs20:3,hs20:4"): | |
381 | raise Exception("ANQP_GET command failed") | |
e56e286d JM |
382 | |
383 | ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5) | |
384 | if ev is None: | |
385 | raise Exception("GAS query start timed out") | |
386 | ||
387 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
388 | if ev is None: | |
389 | raise Exception("GAS query timed out") | |
390 | ||
391 | ev = dev[0].wait_event(["RX-ANQP"], timeout=1) | |
392 | if ev is None or "Venue Name" not in ev: | |
393 | raise Exception("Did not receive Venue Name") | |
394 | ||
395 | ev = dev[0].wait_event(["RX-ANQP"], timeout=1) | |
396 | if ev is None or "Domain Name list" not in ev: | |
397 | raise Exception("Did not receive Domain Name list") | |
398 | ||
399 | ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1) | |
400 | if ev is None or "Operator Friendly Name" not in ev: | |
6d6f3c09 JM |
401 | raise Exception("Did not receive Operator Friendly Name") |
402 | ||
403 | ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1) | |
404 | if ev is None or "WAN Metrics" not in ev: | |
405 | raise Exception("Did not receive WAN Metrics") | |
406 | ||
a60dbbce JM |
407 | ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10) |
408 | if ev is None: | |
409 | raise Exception("ANQP-QUERY-DONE event not seen") | |
410 | if "result=SUCCESS" not in ev: | |
411 | raise Exception("Unexpected result: " + ev) | |
412 | ||
2453f251 JM |
413 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " hs20:3"): |
414 | raise Exception("ANQP_GET command failed") | |
415 | ||
416 | ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5) | |
417 | if ev is None: | |
418 | raise Exception("GAS query start timed out") | |
419 | ||
420 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
421 | if ev is None: | |
422 | raise Exception("GAS query timed out") | |
423 | ||
424 | ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1) | |
425 | if ev is None or "Operator Friendly Name" not in ev: | |
426 | raise Exception("Did not receive Operator Friendly Name") | |
427 | ||
428 | ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10) | |
429 | if ev is None: | |
430 | raise Exception("ANQP-QUERY-DONE event not seen") | |
431 | if "result=SUCCESS" not in ev: | |
432 | raise Exception("Unexpected result: " + ev) | |
433 | ||
4ef70531 JM |
434 | if "OK" not in dev[0].request("HS20_ANQP_GET " + bssid + " 3,4"): |
435 | raise Exception("ANQP_GET command failed") | |
6d6f3c09 JM |
436 | |
437 | ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1) | |
438 | if ev is None or "Operator Friendly Name" not in ev: | |
e56e286d JM |
439 | raise Exception("Did not receive Operator Friendly Name") |
440 | ||
441 | ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1) | |
442 | if ev is None or "WAN Metrics" not in ev: | |
443 | raise Exception("Did not receive WAN Metrics") | |
444 | ||
09b812c9 | 445 | logger.info("Attempt an MBO request with an AP that does not support MBO") |
a9be28bf | 446 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 272,mbo:2"): |
09b812c9 JM |
447 | raise Exception("ANQP_GET command failed (2)") |
448 | ||
449 | ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5) | |
450 | if ev is None: | |
451 | raise Exception("GAS query start timed out (2)") | |
452 | ||
453 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
454 | if ev is None: | |
455 | raise Exception("GAS query timed out (2)") | |
456 | ||
ea68ed56 JM |
457 | cmds = [ "", |
458 | "foo", | |
459 | "00:11:22:33:44:55 258,hs20:-1", | |
460 | "00:11:22:33:44:55 258,hs20:0", | |
461 | "00:11:22:33:44:55 258,hs20:32", | |
462 | "00:11:22:33:44:55 hs20:-1", | |
463 | "00:11:22:33:44:55 hs20:0", | |
464 | "00:11:22:33:44:55 hs20:32", | |
09b812c9 JM |
465 | "00:11:22:33:44:55 mbo:-1", |
466 | "00:11:22:33:44:55 mbo:0", | |
467 | "00:11:22:33:44:55 mbo:999", | |
2453f251 | 468 | "00:11:22:33:44:55 mbo:1,258,mbo:2,mbo:3,259", |
ea68ed56 JM |
469 | "00:11:22:33:44:55", |
470 | "00:11:22:33:44:55 ", | |
60547477 JM |
471 | "00:11:22:33:44:55 0", |
472 | "00:11:22:33:44:55 1" ] | |
ea68ed56 JM |
473 | for cmd in cmds: |
474 | if "FAIL" not in dev[0].request("ANQP_GET " + cmd): | |
475 | raise Exception("Invalid ANQP_GET accepted") | |
476 | ||
477 | cmds = [ "", | |
478 | "foo", | |
479 | "00:11:22:33:44:55 -1", | |
480 | "00:11:22:33:44:55 0", | |
481 | "00:11:22:33:44:55 32", | |
482 | "00:11:22:33:44:55", | |
483 | "00:11:22:33:44:55 ", | |
3e5f7818 JM |
484 | "00:11:22:33:44:55 0", |
485 | "00:11:22:33:44:55 1" ] | |
ea68ed56 JM |
486 | for cmd in cmds: |
487 | if "FAIL" not in dev[0].request("HS20_ANQP_GET " + cmd): | |
488 | raise Exception("Invalid HS20_ANQP_GET accepted") | |
489 | ||
3429e2ff JM |
490 | def test_gas_anqp_get_oom(dev, apdev): |
491 | """GAS/ANQP query OOM""" | |
492 | hapd = start_ap(apdev[0]) | |
493 | bssid = apdev[0]['bssid'] | |
494 | ||
495 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
496 | with alloc_fail(dev[0], 1, "wpabuf_alloc;anqp_send_req"): | |
497 | if "FAIL" not in dev[0].request("ANQP_GET " + bssid + " 258,268,hs20:3,hs20:4"): | |
498 | raise Exception("ANQP_GET command accepted during OOM") | |
3e5f7818 JM |
499 | with alloc_fail(dev[0], 1, "hs20_build_anqp_req;hs20_anqp_send_req"): |
500 | if "FAIL" not in dev[0].request("HS20_ANQP_GET " + bssid + " 1"): | |
501 | raise Exception("HS20_ANQP_GET command accepted during OOM") | |
502 | with alloc_fail(dev[0], 1, "gas_query_req;hs20_anqp_send_req"): | |
503 | if "FAIL" not in dev[0].request("HS20_ANQP_GET " + bssid + " 1"): | |
504 | raise Exception("HS20_ANQP_GET command accepted during OOM") | |
505 | with alloc_fail(dev[0], 1, "=hs20_anqp_send_req"): | |
506 | if "FAIL" not in dev[0].request("REQ_HS20_ICON " + bssid + " w1fi_logo"): | |
507 | raise Exception("REQ_HS20_ICON command accepted during OOM") | |
508 | with alloc_fail(dev[0], 2, "=hs20_anqp_send_req"): | |
509 | if "FAIL" not in dev[0].request("REQ_HS20_ICON " + bssid + " w1fi_logo"): | |
510 | raise Exception("REQ_HS20_ICON command accepted during OOM") | |
3429e2ff | 511 | |
5dec77ee JM |
512 | def test_gas_anqp_icon_binary_proto(dev, apdev): |
513 | """GAS/ANQP and icon binary protocol testing""" | |
514 | hapd = start_ap(apdev[0]) | |
515 | bssid = apdev[0]['bssid'] | |
516 | ||
517 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
518 | hapd.set("ext_mgmt_frame_handling", "1") | |
519 | ||
520 | tests = [ '010000', '01000000', '00000000', '00030000', '00020000', | |
521 | '00000100', '0001ff0100ee', '0001ff0200ee' ] | |
522 | for test in tests: | |
523 | dev[0].request("HS20_ICON_REQUEST " + bssid + " w1fi_logo") | |
524 | query = gas_rx(hapd) | |
525 | gas = parse_gas(query['payload']) | |
526 | resp = action_response(query) | |
527 | data = binascii.unhexlify(test) | |
528 | data = binascii.unhexlify('506f9a110b00') + data | |
529 | data = struct.pack('<HHH', len(data) + 4, 0xdddd, len(data)) + data | |
530 | resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + data | |
531 | send_gas_resp(hapd, resp) | |
532 | expect_gas_result(dev[0], "SUCCESS") | |
533 | ||
21d410ca JM |
534 | def test_gas_anqp_hs20_proto(dev, apdev): |
535 | """GAS/ANQP and Hotspot 2.0 element protocol testing""" | |
536 | hapd = start_ap(apdev[0]) | |
537 | bssid = apdev[0]['bssid'] | |
538 | ||
539 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
540 | hapd.set("ext_mgmt_frame_handling", "1") | |
541 | ||
542 | tests = [ '00', '0100', '0201', '0300', '0400', '0500', '0600', '0700', | |
543 | '0800', '0900', '0a00', '0b0000000000' ] | |
544 | for test in tests: | |
545 | dev[0].request("HS20_ANQP_GET " + bssid + " 3,4") | |
546 | query = gas_rx(hapd) | |
547 | gas = parse_gas(query['payload']) | |
548 | resp = action_response(query) | |
549 | data = binascii.unhexlify(test) | |
550 | data = binascii.unhexlify('506f9a11') + data | |
551 | data = struct.pack('<HHH', len(data) + 4, 0xdddd, len(data)) + data | |
552 | resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + data | |
553 | send_gas_resp(hapd, resp) | |
554 | expect_gas_result(dev[0], "SUCCESS") | |
555 | ||
75f6134d | 556 | def expect_gas_result(dev, result, status=None): |
bfe375ec JM |
557 | ev = dev.wait_event(["GAS-QUERY-DONE"], timeout=10) |
558 | if ev is None: | |
559 | raise Exception("GAS query timed out") | |
560 | if "result=" + result not in ev: | |
561 | raise Exception("Unexpected GAS query result") | |
75f6134d JM |
562 | if status and "status_code=" + str(status) + ' ' not in ev: |
563 | raise Exception("Unexpected GAS status code") | |
bfe375ec | 564 | |
18dd2af0 | 565 | def anqp_get(dev, bssid, id): |
4ef70531 JM |
566 | if "OK" not in dev.request("ANQP_GET " + bssid + " " + str(id)): |
567 | raise Exception("ANQP_GET command failed") | |
18dd2af0 JM |
568 | ev = dev.wait_event(["GAS-QUERY-START"], timeout=5) |
569 | if ev is None: | |
570 | raise Exception("GAS query start timed out") | |
571 | ||
2cace98e JM |
572 | def test_gas_timeout(dev, apdev): |
573 | """GAS timeout""" | |
18dd2af0 | 574 | hapd = start_ap(apdev[0]) |
2cace98e | 575 | bssid = apdev[0]['bssid'] |
2cace98e | 576 | |
4ef70531 | 577 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) |
2cace98e JM |
578 | hapd.set("ext_mgmt_frame_handling", "1") |
579 | ||
18dd2af0 | 580 | anqp_get(dev[0], bssid, 263) |
2cace98e JM |
581 | |
582 | ev = hapd.wait_event(["MGMT-RX"], timeout=5) | |
583 | if ev is None: | |
584 | raise Exception("MGMT RX wait timed out") | |
585 | ||
bfe375ec JM |
586 | expect_gas_result(dev[0], "TIMEOUT") |
587 | ||
d9474958 JM |
588 | MGMT_SUBTYPE_ACTION = 13 |
589 | ACTION_CATEG_PUBLIC = 4 | |
590 | ||
591 | GAS_INITIAL_REQUEST = 10 | |
592 | GAS_INITIAL_RESPONSE = 11 | |
593 | GAS_COMEBACK_REQUEST = 12 | |
594 | GAS_COMEBACK_RESPONSE = 13 | |
595 | GAS_ACTIONS = [ GAS_INITIAL_REQUEST, GAS_INITIAL_RESPONSE, | |
596 | GAS_COMEBACK_REQUEST, GAS_COMEBACK_RESPONSE ] | |
597 | ||
598 | def anqp_adv_proto(): | |
599 | return struct.pack('BBBB', 108, 2, 127, 0) | |
600 | ||
75f6134d | 601 | def anqp_initial_resp(dialog_token, status_code, comeback_delay=0): |
fe871e48 | 602 | return struct.pack('<BBBHH', ACTION_CATEG_PUBLIC, GAS_INITIAL_RESPONSE, |
75f6134d JM |
603 | dialog_token, status_code, comeback_delay) + anqp_adv_proto() |
604 | ||
605 | def anqp_comeback_resp(dialog_token, status_code=0, id=0, more=False, comeback_delay=0, bogus_adv_proto=False): | |
606 | if more: | |
607 | id |= 0x80 | |
608 | if bogus_adv_proto: | |
609 | adv = struct.pack('BBBB', 108, 2, 127, 1) | |
610 | else: | |
611 | adv = anqp_adv_proto() | |
d9474958 | 612 | return struct.pack('<BBBHBH', ACTION_CATEG_PUBLIC, GAS_COMEBACK_RESPONSE, |
75f6134d | 613 | dialog_token, status_code, id, comeback_delay) + adv |
d9474958 JM |
614 | |
615 | def gas_rx(hapd): | |
616 | count = 0 | |
617 | while count < 30: | |
618 | count = count + 1 | |
619 | query = hapd.mgmt_rx() | |
620 | if query is None: | |
621 | raise Exception("Action frame not received") | |
622 | if query['subtype'] != MGMT_SUBTYPE_ACTION: | |
623 | continue | |
624 | payload = query['payload'] | |
625 | if len(payload) < 2: | |
626 | continue | |
627 | (category, action) = struct.unpack('BB', payload[0:2]) | |
628 | if category != ACTION_CATEG_PUBLIC or action not in GAS_ACTIONS: | |
629 | continue | |
630 | return query | |
631 | raise Exception("No Action frame received") | |
632 | ||
633 | def parse_gas(payload): | |
634 | pos = payload | |
635 | (category, action, dialog_token) = struct.unpack('BBB', pos[0:3]) | |
636 | if category != ACTION_CATEG_PUBLIC: | |
637 | return None | |
638 | if action not in GAS_ACTIONS: | |
639 | return None | |
640 | gas = {} | |
641 | gas['action'] = action | |
642 | pos = pos[3:] | |
643 | ||
75f6134d | 644 | if len(pos) < 1 and action != GAS_COMEBACK_REQUEST: |
d9474958 JM |
645 | return None |
646 | ||
647 | gas['dialog_token'] = dialog_token | |
1d1de230 JM |
648 | |
649 | if action == GAS_INITIAL_RESPONSE: | |
650 | if len(pos) < 4: | |
651 | return None | |
652 | (status_code, comeback_delay) = struct.unpack('<HH', pos[0:4]) | |
653 | gas['status_code'] = status_code | |
654 | gas['comeback_delay'] = comeback_delay | |
de8c4144 JM |
655 | |
656 | if action == GAS_COMEBACK_RESPONSE: | |
657 | if len(pos) < 5: | |
658 | return None | |
659 | (status_code, frag, comeback_delay) = struct.unpack('<HBH', pos[0:5]) | |
660 | gas['status_code'] = status_code | |
661 | gas['frag'] = frag | |
662 | gas['comeback_delay'] = comeback_delay | |
663 | ||
d9474958 JM |
664 | return gas |
665 | ||
666 | def action_response(req): | |
667 | resp = {} | |
668 | resp['fc'] = req['fc'] | |
669 | resp['da'] = req['sa'] | |
670 | resp['sa'] = req['da'] | |
671 | resp['bssid'] = req['bssid'] | |
672 | return resp | |
673 | ||
75f6134d JM |
674 | def send_gas_resp(hapd, resp): |
675 | hapd.mgmt_tx(resp) | |
676 | ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5) | |
677 | if ev is None: | |
678 | raise Exception("Missing TX status for GAS response") | |
679 | if "ok=1" not in ev: | |
680 | raise Exception("GAS response not acknowledged") | |
681 | ||
bfe375ec JM |
682 | def test_gas_invalid_response_type(dev, apdev): |
683 | """GAS invalid response type""" | |
18dd2af0 | 684 | hapd = start_ap(apdev[0]) |
bfe375ec | 685 | bssid = apdev[0]['bssid'] |
bfe375ec | 686 | |
4ef70531 | 687 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) |
bfe375ec JM |
688 | hapd.set("ext_mgmt_frame_handling", "1") |
689 | ||
18dd2af0 | 690 | anqp_get(dev[0], bssid, 263) |
bfe375ec | 691 | |
d9474958 JM |
692 | query = gas_rx(hapd) |
693 | gas = parse_gas(query['payload']) | |
694 | ||
695 | resp = action_response(query) | |
bfe375ec | 696 | # GAS Comeback Response instead of GAS Initial Response |
d9474958 | 697 | resp['payload'] = anqp_comeback_resp(gas['dialog_token']) + struct.pack('<H', 0) |
75f6134d | 698 | send_gas_resp(hapd, resp) |
bfe375ec JM |
699 | |
700 | # station drops the invalid frame, so this needs to result in GAS timeout | |
701 | expect_gas_result(dev[0], "TIMEOUT") | |
fe871e48 JM |
702 | |
703 | def test_gas_failure_status_code(dev, apdev): | |
704 | """GAS failure status code""" | |
705 | hapd = start_ap(apdev[0]) | |
706 | bssid = apdev[0]['bssid'] | |
707 | ||
4ef70531 | 708 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) |
fe871e48 JM |
709 | hapd.set("ext_mgmt_frame_handling", "1") |
710 | ||
711 | anqp_get(dev[0], bssid, 263) | |
712 | ||
713 | query = gas_rx(hapd) | |
714 | gas = parse_gas(query['payload']) | |
715 | ||
716 | resp = action_response(query) | |
717 | resp['payload'] = anqp_initial_resp(gas['dialog_token'], 61) + struct.pack('<H', 0) | |
75f6134d | 718 | send_gas_resp(hapd, resp) |
fe871e48 JM |
719 | |
720 | expect_gas_result(dev[0], "FAILURE") | |
84262fef JM |
721 | |
722 | def test_gas_malformed(dev, apdev): | |
723 | """GAS malformed response frames""" | |
724 | hapd = start_ap(apdev[0]) | |
725 | bssid = apdev[0]['bssid'] | |
726 | ||
4ef70531 | 727 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) |
84262fef JM |
728 | hapd.set("ext_mgmt_frame_handling", "1") |
729 | ||
730 | anqp_get(dev[0], bssid, 263) | |
731 | ||
732 | query = gas_rx(hapd) | |
733 | gas = parse_gas(query['payload']) | |
734 | ||
735 | resp = action_response(query) | |
736 | ||
737 | resp['payload'] = struct.pack('<BBBH', ACTION_CATEG_PUBLIC, | |
738 | GAS_COMEBACK_RESPONSE, | |
739 | gas['dialog_token'], 0) | |
740 | hapd.mgmt_tx(resp) | |
741 | ||
742 | resp['payload'] = struct.pack('<BBBHB', ACTION_CATEG_PUBLIC, | |
743 | GAS_COMEBACK_RESPONSE, | |
744 | gas['dialog_token'], 0, 0) | |
745 | hapd.mgmt_tx(resp) | |
746 | ||
747 | hdr = struct.pack('<BBBHH', ACTION_CATEG_PUBLIC, GAS_INITIAL_RESPONSE, | |
748 | gas['dialog_token'], 0, 0) | |
749 | resp['payload'] = hdr + struct.pack('B', 108) | |
750 | hapd.mgmt_tx(resp) | |
751 | resp['payload'] = hdr + struct.pack('BB', 108, 0) | |
752 | hapd.mgmt_tx(resp) | |
753 | resp['payload'] = hdr + struct.pack('BB', 108, 1) | |
754 | hapd.mgmt_tx(resp) | |
755 | resp['payload'] = hdr + struct.pack('BB', 108, 255) | |
756 | hapd.mgmt_tx(resp) | |
757 | resp['payload'] = hdr + struct.pack('BBB', 108, 1, 127) | |
758 | hapd.mgmt_tx(resp) | |
759 | resp['payload'] = hdr + struct.pack('BBB', 108, 2, 127) | |
760 | hapd.mgmt_tx(resp) | |
761 | resp['payload'] = hdr + struct.pack('BBBB', 0, 2, 127, 0) | |
762 | hapd.mgmt_tx(resp) | |
763 | ||
764 | resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<H', 1) | |
765 | hapd.mgmt_tx(resp) | |
766 | ||
767 | resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<HB', 2, 0) | |
768 | hapd.mgmt_tx(resp) | |
769 | ||
770 | resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<H', 65535) | |
771 | hapd.mgmt_tx(resp) | |
772 | ||
773 | resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<HBB', 1, 0, 0) | |
774 | hapd.mgmt_tx(resp) | |
775 | ||
776 | # Station drops invalid frames, but the last of the responses is valid from | |
777 | # GAS view point even though it has an extra octet in the end and the ANQP | |
a60dbbce | 778 | # part of the response is not valid. This is reported as successfully |
84262fef JM |
779 | # completed GAS exchange. |
780 | expect_gas_result(dev[0], "SUCCESS") | |
75f6134d | 781 | |
a60dbbce JM |
782 | ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5) |
783 | if ev is None: | |
784 | raise Exception("ANQP-QUERY-DONE not reported") | |
785 | if "result=INVALID_FRAME" not in ev: | |
786 | raise Exception("Unexpected result: " + ev) | |
787 | ||
75f6134d JM |
788 | def init_gas(hapd, bssid, dev): |
789 | anqp_get(dev, bssid, 263) | |
790 | query = gas_rx(hapd) | |
791 | gas = parse_gas(query['payload']) | |
792 | dialog_token = gas['dialog_token'] | |
793 | ||
794 | resp = action_response(query) | |
795 | resp['payload'] = anqp_initial_resp(dialog_token, 0, comeback_delay=1) + struct.pack('<H', 0) | |
796 | send_gas_resp(hapd, resp) | |
797 | ||
798 | query = gas_rx(hapd) | |
799 | gas = parse_gas(query['payload']) | |
800 | if gas['action'] != GAS_COMEBACK_REQUEST: | |
801 | raise Exception("Unexpected request action") | |
802 | if gas['dialog_token'] != dialog_token: | |
803 | raise Exception("Unexpected dialog token change") | |
804 | return query, dialog_token | |
805 | ||
f9a93044 JM |
806 | def allow_gas_initial_req(hapd, dialog_token): |
807 | msg = hapd.mgmt_rx(timeout=1) | |
808 | if msg is not None: | |
809 | gas = parse_gas(msg['payload']) | |
810 | if gas['action'] != GAS_INITIAL_REQUEST or dialog_token == gas['dialog_token']: | |
811 | raise Exception("Unexpected management frame") | |
812 | ||
75f6134d JM |
813 | def test_gas_malformed_comeback_resp(dev, apdev): |
814 | """GAS malformed comeback response frames""" | |
815 | hapd = start_ap(apdev[0]) | |
816 | bssid = apdev[0]['bssid'] | |
817 | ||
4ef70531 | 818 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) |
75f6134d JM |
819 | hapd.set("ext_mgmt_frame_handling", "1") |
820 | ||
821 | logger.debug("Non-zero status code in comeback response") | |
822 | query, dialog_token = init_gas(hapd, bssid, dev[0]) | |
823 | resp = action_response(query) | |
824 | resp['payload'] = anqp_comeback_resp(dialog_token, status_code=2) + struct.pack('<H', 0) | |
825 | send_gas_resp(hapd, resp) | |
826 | expect_gas_result(dev[0], "FAILURE", status=2) | |
827 | ||
828 | logger.debug("Different advertisement protocol in comeback response") | |
829 | query, dialog_token = init_gas(hapd, bssid, dev[0]) | |
830 | resp = action_response(query) | |
831 | resp['payload'] = anqp_comeback_resp(dialog_token, bogus_adv_proto=True) + struct.pack('<H', 0) | |
832 | send_gas_resp(hapd, resp) | |
833 | expect_gas_result(dev[0], "PEER_ERROR") | |
834 | ||
835 | logger.debug("Non-zero frag id and comeback delay in comeback response") | |
836 | query, dialog_token = init_gas(hapd, bssid, dev[0]) | |
837 | resp = action_response(query) | |
838 | resp['payload'] = anqp_comeback_resp(dialog_token, id=1, comeback_delay=1) + struct.pack('<H', 0) | |
839 | send_gas_resp(hapd, resp) | |
840 | expect_gas_result(dev[0], "PEER_ERROR") | |
841 | ||
842 | logger.debug("Unexpected frag id in comeback response") | |
843 | query, dialog_token = init_gas(hapd, bssid, dev[0]) | |
844 | resp = action_response(query) | |
845 | resp['payload'] = anqp_comeback_resp(dialog_token, id=1) + struct.pack('<H', 0) | |
846 | send_gas_resp(hapd, resp) | |
847 | expect_gas_result(dev[0], "PEER_ERROR") | |
848 | ||
849 | logger.debug("Empty fragment and replay in comeback response") | |
850 | query, dialog_token = init_gas(hapd, bssid, dev[0]) | |
851 | resp = action_response(query) | |
852 | resp['payload'] = anqp_comeback_resp(dialog_token, more=True) + struct.pack('<H', 0) | |
853 | send_gas_resp(hapd, resp) | |
854 | query = gas_rx(hapd) | |
855 | gas = parse_gas(query['payload']) | |
856 | if gas['action'] != GAS_COMEBACK_REQUEST: | |
857 | raise Exception("Unexpected request action") | |
858 | if gas['dialog_token'] != dialog_token: | |
859 | raise Exception("Unexpected dialog token change") | |
860 | resp = action_response(query) | |
861 | resp['payload'] = anqp_comeback_resp(dialog_token) + struct.pack('<H', 0) | |
862 | send_gas_resp(hapd, resp) | |
863 | resp['payload'] = anqp_comeback_resp(dialog_token, id=1) + struct.pack('<H', 0) | |
864 | send_gas_resp(hapd, resp) | |
865 | expect_gas_result(dev[0], "SUCCESS") | |
866 | ||
867 | logger.debug("Unexpected initial response when waiting for comeback response") | |
868 | query, dialog_token = init_gas(hapd, bssid, dev[0]) | |
869 | resp = action_response(query) | |
870 | resp['payload'] = anqp_initial_resp(dialog_token, 0) + struct.pack('<H', 0) | |
871 | send_gas_resp(hapd, resp) | |
f9a93044 | 872 | allow_gas_initial_req(hapd, dialog_token) |
75f6134d JM |
873 | expect_gas_result(dev[0], "TIMEOUT") |
874 | ||
875 | logger.debug("Too short comeback response") | |
876 | query, dialog_token = init_gas(hapd, bssid, dev[0]) | |
877 | resp = action_response(query) | |
878 | resp['payload'] = struct.pack('<BBBH', ACTION_CATEG_PUBLIC, | |
879 | GAS_COMEBACK_RESPONSE, dialog_token, 0) | |
880 | send_gas_resp(hapd, resp) | |
f9a93044 | 881 | allow_gas_initial_req(hapd, dialog_token) |
75f6134d JM |
882 | expect_gas_result(dev[0], "TIMEOUT") |
883 | ||
884 | logger.debug("Too short comeback response(2)") | |
885 | query, dialog_token = init_gas(hapd, bssid, dev[0]) | |
886 | resp = action_response(query) | |
887 | resp['payload'] = struct.pack('<BBBHBB', ACTION_CATEG_PUBLIC, | |
888 | GAS_COMEBACK_RESPONSE, dialog_token, 0, 0x80, | |
889 | 0) | |
890 | send_gas_resp(hapd, resp) | |
f9a93044 | 891 | allow_gas_initial_req(hapd, dialog_token) |
75f6134d JM |
892 | expect_gas_result(dev[0], "TIMEOUT") |
893 | ||
894 | logger.debug("Maximum comeback response fragment claiming more fragments") | |
895 | query, dialog_token = init_gas(hapd, bssid, dev[0]) | |
896 | resp = action_response(query) | |
897 | resp['payload'] = anqp_comeback_resp(dialog_token, more=True) + struct.pack('<H', 0) | |
898 | send_gas_resp(hapd, resp) | |
899 | for i in range(1, 129): | |
900 | query = gas_rx(hapd) | |
901 | gas = parse_gas(query['payload']) | |
902 | if gas['action'] != GAS_COMEBACK_REQUEST: | |
903 | raise Exception("Unexpected request action") | |
904 | if gas['dialog_token'] != dialog_token: | |
905 | raise Exception("Unexpected dialog token change") | |
906 | resp = action_response(query) | |
907 | resp['payload'] = anqp_comeback_resp(dialog_token, id=i, more=True) + struct.pack('<H', 0) | |
908 | send_gas_resp(hapd, resp) | |
909 | expect_gas_result(dev[0], "PEER_ERROR") | |
910 | ||
911 | def test_gas_comeback_resp_additional_delay(dev, apdev): | |
912 | """GAS comeback response requesting additional delay""" | |
913 | hapd = start_ap(apdev[0]) | |
914 | bssid = apdev[0]['bssid'] | |
915 | ||
4ef70531 | 916 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) |
75f6134d JM |
917 | hapd.set("ext_mgmt_frame_handling", "1") |
918 | ||
919 | query, dialog_token = init_gas(hapd, bssid, dev[0]) | |
920 | for i in range(0, 2): | |
921 | resp = action_response(query) | |
922 | resp['payload'] = anqp_comeback_resp(dialog_token, status_code=95, comeback_delay=50) + struct.pack('<H', 0) | |
923 | send_gas_resp(hapd, resp) | |
924 | query = gas_rx(hapd) | |
925 | gas = parse_gas(query['payload']) | |
926 | if gas['action'] != GAS_COMEBACK_REQUEST: | |
927 | raise Exception("Unexpected request action") | |
928 | if gas['dialog_token'] != dialog_token: | |
929 | raise Exception("Unexpected dialog token change") | |
930 | resp = action_response(query) | |
931 | resp['payload'] = anqp_comeback_resp(dialog_token, status_code=0) + struct.pack('<H', 0) | |
932 | send_gas_resp(hapd, resp) | |
933 | expect_gas_result(dev[0], "SUCCESS") | |
d61ed3ac JM |
934 | |
935 | def test_gas_unknown_adv_proto(dev, apdev): | |
936 | """Unknown advertisement protocol id""" | |
937 | bssid = apdev[0]['bssid'] | |
938 | params = hs20_ap_params() | |
939 | params['hessid'] = bssid | |
8b8a1864 | 940 | hostapd.add_ap(apdev[0], params) |
d61ed3ac | 941 | |
4ef70531 | 942 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) |
d61ed3ac JM |
943 | req = dev[0].request("GAS_REQUEST " + bssid + " 42 000102000101") |
944 | if "FAIL" in req: | |
945 | raise Exception("GAS query request rejected") | |
946 | expect_gas_result(dev[0], "FAILURE", "59") | |
947 | ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10) | |
948 | if ev is None: | |
949 | raise Exception("GAS query timed out") | |
950 | exp = r'<.>(GAS-RESPONSE-INFO) addr=([0-9a-f:]*) dialog_token=([0-9]*) status_code=([0-9]*) resp_len=([\-0-9]*)' | |
951 | res = re.split(exp, ev) | |
952 | if len(res) < 6: | |
953 | raise Exception("Could not parse GAS-RESPONSE-INFO") | |
954 | if res[2] != bssid: | |
955 | raise Exception("Unexpected BSSID in response") | |
956 | status = res[4] | |
957 | if status != "59": | |
958 | raise Exception("Unexpected GAS-RESPONSE-INFO status") | |
1d1de230 | 959 | |
69e85c7f JM |
960 | def test_gas_request_oom(dev, apdev): |
961 | """GAS_REQUEST OOM""" | |
962 | bssid = apdev[0]['bssid'] | |
963 | params = hs20_ap_params() | |
964 | params['hessid'] = bssid | |
965 | hostapd.add_ap(apdev[0], params) | |
966 | ||
967 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
968 | ||
969 | with alloc_fail(dev[0], 1, "gas_build_req;gas_send_request"): | |
970 | if "FAIL" not in dev[0].request("GAS_REQUEST " + bssid + " 42"): | |
971 | raise Exception("GAS query request rejected") | |
972 | ||
973 | with alloc_fail(dev[0], 1, "gas_query_req;gas_send_request"): | |
974 | if "FAIL" not in dev[0].request("GAS_REQUEST " + bssid + " 42"): | |
975 | raise Exception("GAS query request rejected") | |
976 | ||
977 | with alloc_fail(dev[0], 1, "wpabuf_dup;gas_resp_cb"): | |
978 | if "OK" not in dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101"): | |
979 | raise Exception("GAS query request rejected") | |
980 | ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10) | |
981 | if ev is None: | |
982 | raise Exception("No GAS response") | |
983 | if "status_code=0" not in ev: | |
984 | raise Exception("GAS response indicated a failure") | |
985 | ||
1d1de230 JM |
986 | def test_gas_max_pending(dev, apdev): |
987 | """GAS and maximum pending query limit""" | |
988 | hapd = start_ap(apdev[0]) | |
989 | hapd.set("gas_frag_limit", "50") | |
990 | bssid = apdev[0]['bssid'] | |
991 | ||
992 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
993 | wpas.interface_add("wlan5") | |
994 | if "OK" not in wpas.request("P2P_SET listen_channel 1"): | |
995 | raise Exception("Failed to set listen channel") | |
996 | if "OK" not in wpas.p2p_listen(): | |
997 | raise Exception("Failed to start listen state") | |
998 | if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"): | |
999 | raise Exception("Failed to enable external management frame handling") | |
1000 | ||
1001 | anqp_query = struct.pack('<HHHHHHHHHH', 256, 16, 257, 258, 260, 261, 262, 263, 264, 268) | |
1002 | gas = struct.pack('<H', len(anqp_query)) + anqp_query | |
1003 | ||
1004 | for dialog_token in range(1, 10): | |
1005 | msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST, | |
1006 | dialog_token) + anqp_adv_proto() + gas | |
7ab74770 | 1007 | req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg).decode()) |
1d1de230 JM |
1008 | if "OK" not in wpas.request(req): |
1009 | raise Exception("Could not send management frame") | |
1010 | resp = wpas.mgmt_rx() | |
1011 | if resp is None: | |
1012 | raise Exception("MGMT-RX timeout") | |
1013 | if 'payload' not in resp: | |
1014 | raise Exception("Missing payload") | |
1015 | gresp = parse_gas(resp['payload']) | |
1016 | if gresp['dialog_token'] != dialog_token: | |
1017 | raise Exception("Dialog token mismatch") | |
1018 | status_code = gresp['status_code'] | |
1019 | if dialog_token < 9 and status_code != 0: | |
1020 | raise Exception("Unexpected failure status code {} for dialog token {}".format(status_code, dialog_token)) | |
1021 | if dialog_token > 8 and status_code == 0: | |
1022 | raise Exception("Unexpected success status code {} for dialog token {}".format(status_code, dialog_token)) | |
de8c4144 JM |
1023 | |
1024 | def test_gas_no_pending(dev, apdev): | |
1025 | """GAS and no pending query for comeback request""" | |
1026 | hapd = start_ap(apdev[0]) | |
1027 | bssid = apdev[0]['bssid'] | |
1028 | ||
1029 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
1030 | wpas.interface_add("wlan5") | |
1031 | if "OK" not in wpas.request("P2P_SET listen_channel 1"): | |
1032 | raise Exception("Failed to set listen channel") | |
1033 | if "OK" not in wpas.p2p_listen(): | |
1034 | raise Exception("Failed to start listen state") | |
1035 | if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"): | |
1036 | raise Exception("Failed to enable external management frame handling") | |
1037 | ||
1038 | msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_COMEBACK_REQUEST, 1) | |
7ab74770 | 1039 | req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg).decode()) |
de8c4144 JM |
1040 | if "OK" not in wpas.request(req): |
1041 | raise Exception("Could not send management frame") | |
1042 | resp = wpas.mgmt_rx() | |
1043 | if resp is None: | |
1044 | raise Exception("MGMT-RX timeout") | |
1045 | if 'payload' not in resp: | |
1046 | raise Exception("Missing payload") | |
1047 | gresp = parse_gas(resp['payload']) | |
1048 | status_code = gresp['status_code'] | |
1049 | if status_code != 60: | |
1050 | raise Exception("Unexpected status code {} (expected 60)".format(status_code)) | |
6ec64f3e | 1051 | |
1ad6f0d5 JM |
1052 | def test_gas_delete_at_deinit(dev, apdev): |
1053 | """GAS query deleted at deinit""" | |
1054 | hapd = start_ap(apdev[0]) | |
1055 | hapd.set("gas_comeback_delay", "1000") | |
1056 | bssid = apdev[0]['bssid'] | |
1057 | ||
1058 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
1059 | wpas.interface_add("wlan5") | |
1060 | wpas.scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True) | |
1061 | wpas.request("ANQP_GET " + bssid + " 258") | |
1062 | ||
1063 | wpas.global_request("INTERFACE_REMOVE " + wpas.ifname) | |
1064 | ev = wpas.wait_event(["GAS-QUERY-DONE"], timeout=2) | |
1065 | del wpas | |
1066 | if ev is None: | |
1067 | raise Exception("GAS-QUERY-DONE not seen") | |
1068 | if "result=DELETED_AT_DEINIT" not in ev: | |
1069 | raise Exception("Unexpected result code: " + ev) | |
1070 | ||
6ec64f3e JM |
1071 | def test_gas_missing_payload(dev, apdev): |
1072 | """No action code in the query frame""" | |
1073 | bssid = apdev[0]['bssid'] | |
1074 | params = hs20_ap_params() | |
1075 | params['hessid'] = bssid | |
8b8a1864 | 1076 | hostapd.add_ap(apdev[0], params) |
6ec64f3e JM |
1077 | |
1078 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1079 | ||
1080 | cmd = "MGMT_TX {} {} freq=2412 action=040A".format(bssid, bssid) | |
1081 | if "FAIL" in dev[0].request(cmd): | |
1082 | raise Exception("Could not send test Action frame") | |
1083 | ev = dev[0].wait_event(["MGMT-TX-STATUS"], timeout=10) | |
1084 | if ev is None: | |
1085 | raise Exception("Timeout on MGMT-TX-STATUS") | |
1086 | if "result=SUCCESS" not in ev: | |
1087 | raise Exception("AP did not ack Action frame") | |
1088 | ||
1089 | cmd = "MGMT_TX {} {} freq=2412 action=04".format(bssid, bssid) | |
1090 | if "FAIL" in dev[0].request(cmd): | |
1091 | raise Exception("Could not send test Action frame") | |
1092 | ev = dev[0].wait_event(["MGMT-TX-STATUS"], timeout=10) | |
1093 | if ev is None: | |
1094 | raise Exception("Timeout on MGMT-TX-STATUS") | |
1095 | if "result=SUCCESS" not in ev: | |
1096 | raise Exception("AP did not ack Action frame") | |
c518fecc JM |
1097 | |
1098 | def test_gas_query_deinit(dev, apdev): | |
1099 | """Pending GAS/ANQP query during deinit""" | |
1100 | hapd = start_ap(apdev[0]) | |
1101 | bssid = apdev[0]['bssid'] | |
1102 | ||
1103 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
1104 | wpas.interface_add("wlan5") | |
1105 | ||
1106 | wpas.scan_for_bss(bssid, freq="2412", force_scan=True) | |
1107 | id = wpas.request("RADIO_WORK add block-work") | |
1108 | if "OK" not in wpas.request("ANQP_GET " + bssid + " 258"): | |
1109 | raise Exception("ANQP_GET command failed") | |
1110 | ||
1111 | ev = wpas.wait_event(["GAS-QUERY-START", "EXT-RADIO-WORK-START"], timeout=5) | |
1112 | if ev is None: | |
1113 | raise Exception("Timeout while waiting radio work to start") | |
1114 | ev = wpas.wait_event(["GAS-QUERY-START", "EXT-RADIO-WORK-START"], timeout=5) | |
1115 | if ev is None: | |
1116 | raise Exception("Timeout while waiting radio work to start (2)") | |
1117 | ||
1118 | # Remove the interface while the gas-query radio work is still pending and | |
1119 | # GAS query has not yet been started. | |
1120 | wpas.interface_remove("wlan5") | |
6d01255e | 1121 | |
9fd6804d | 1122 | @remote_compatible |
6d01255e JM |
1123 | def test_gas_anqp_oom_wpas(dev, apdev): |
1124 | """GAS/ANQP query and OOM in wpa_supplicant""" | |
1125 | hapd = start_ap(apdev[0]) | |
1126 | bssid = apdev[0]['bssid'] | |
1127 | ||
1128 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1129 | ||
20c7d26f JM |
1130 | with alloc_fail(dev[0], 1, "wpa_bss_anqp_alloc"): |
1131 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"): | |
1132 | raise Exception("ANQP_GET command failed") | |
1133 | ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5) | |
1134 | if ev is None: | |
1135 | raise Exception("ANQP query did not complete") | |
1136 | ||
6d01255e JM |
1137 | with alloc_fail(dev[0], 1, "gas_build_req"): |
1138 | if "FAIL" not in dev[0].request("ANQP_GET " + bssid + " 258"): | |
1139 | raise Exception("Unexpected ANQP_GET command success (OOM)") | |
1140 | ||
1141 | def test_gas_anqp_oom_hapd(dev, apdev): | |
1142 | """GAS/ANQP query and OOM in hostapd""" | |
1143 | hapd = start_ap(apdev[0]) | |
1144 | bssid = apdev[0]['bssid'] | |
1145 | ||
1146 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1147 | ||
1148 | with alloc_fail(hapd, 1, "gas_build_resp"): | |
1149 | # This query will time out due to the AP not sending a response (OOM). | |
1150 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"): | |
1151 | raise Exception("ANQP_GET command failed") | |
1152 | ||
1153 | ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5) | |
1154 | if ev is None: | |
1155 | raise Exception("GAS query start timed out") | |
1156 | ||
1157 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1158 | if ev is None: | |
1159 | raise Exception("GAS query timed out") | |
1160 | if "result=TIMEOUT" not in ev: | |
1161 | raise Exception("Unexpected result: " + ev) | |
1162 | ||
1163 | ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10) | |
1164 | if ev is None: | |
1165 | raise Exception("ANQP-QUERY-DONE event not seen") | |
1166 | if "result=FAILURE" not in ev: | |
1167 | raise Exception("Unexpected result: " + ev) | |
1168 | ||
1169 | with alloc_fail(hapd, 1, "gas_anqp_build_comeback_resp"): | |
1170 | hapd.set("gas_frag_limit", "50") | |
1171 | ||
35c146bc JM |
1172 | # The first attempt of this query will time out due to the AP not |
1173 | # sending a response (OOM), but the retry succeeds. | |
2c68ae2b | 1174 | dev[0].request("FETCH_ANQP") |
6d01255e JM |
1175 | ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5) |
1176 | if ev is None: | |
1177 | raise Exception("GAS query start timed out") | |
1178 | ||
1179 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1180 | if ev is None: | |
1181 | raise Exception("GAS query timed out") | |
35c146bc | 1182 | if "result=SUCCESS" not in ev: |
6d01255e JM |
1183 | raise Exception("Unexpected result: " + ev) |
1184 | ||
1185 | ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10) | |
1186 | if ev is None: | |
1187 | raise Exception("ANQP-QUERY-DONE event not seen") | |
35c146bc | 1188 | if "result=SUCCESS" not in ev: |
6d01255e | 1189 | raise Exception("Unexpected result: " + ev) |
1c532fd5 JM |
1190 | |
1191 | def test_gas_anqp_extra_elements(dev, apdev): | |
1192 | """GAS/ANQP and extra ANQP elements""" | |
1193 | geo_loc = "001052834d12efd2b08b9b4bf1cc2c00004104050000000000060100" | |
1194 | civic_loc = "0000f9555302f50102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5" | |
47eac38a JM |
1195 | held_uri = "https://held.example.com/location" |
1196 | held = struct.pack('BBB', 0, 1 + len(held_uri), 1) + held_uri | |
1197 | supl_fqdn = "supl.example.com" | |
1198 | supl = struct.pack('BBB', 0, 1 + len(supl_fqdn), 1) + supl_fqdn | |
7ab74770 | 1199 | public_id = binascii.hexlify(held + supl).decode() |
1c532fd5 JM |
1200 | params = { "ssid": "gas/anqp", |
1201 | "interworking": "1", | |
1202 | "anqp_elem": [ "265:" + geo_loc, | |
1203 | "266:" + civic_loc, | |
1204 | "262:1122334455", | |
47eac38a | 1205 | "267:" + public_id, |
1a4b4c84 | 1206 | "279:01020304", |
1c532fd5 JM |
1207 | "60000:01", |
1208 | "299:0102" ] } | |
8b8a1864 | 1209 | hapd = hostapd.add_ap(apdev[0], params) |
1c532fd5 JM |
1210 | bssid = apdev[0]['bssid'] |
1211 | ||
1212 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1213 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 265,266"): | |
1214 | raise Exception("ANQP_GET command failed") | |
1215 | ||
1216 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1217 | if ev is None: | |
1218 | raise Exception("GAS query timed out") | |
1219 | ||
1220 | bss = dev[0].get_bss(bssid) | |
1221 | ||
1222 | if 'anqp[265]' not in bss: | |
1223 | raise Exception("AP Geospatial Location ANQP-element not seen") | |
1224 | if bss['anqp[265]'] != geo_loc: | |
1225 | raise Exception("Unexpected AP Geospatial Location ANQP-element value: " + bss['anqp[265]']) | |
1226 | ||
1227 | if 'anqp[266]' not in bss: | |
1228 | raise Exception("AP Civic Location ANQP-element not seen") | |
1229 | if bss['anqp[266]'] != civic_loc: | |
1230 | raise Exception("Unexpected AP Civic Location ANQP-element value: " + bss['anqp[266]']) | |
1231 | ||
1232 | dev[1].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1233 | if "OK" not in dev[1].request("ANQP_GET " + bssid + " 257,258,259,260,261,262,263,264,265,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299"): | |
1234 | raise Exception("ANQP_GET command failed") | |
1235 | ||
1236 | ev = dev[1].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1237 | if ev is None: | |
1238 | raise Exception("GAS query timed out") | |
1239 | ||
1240 | bss = dev[1].get_bss(bssid) | |
1241 | ||
1242 | if 'anqp[265]' not in bss: | |
1243 | raise Exception("AP Geospatial Location ANQP-element not seen") | |
1244 | if bss['anqp[265]'] != geo_loc: | |
1245 | raise Exception("Unexpected AP Geospatial Location ANQP-element value: " + bss['anqp[265]']) | |
1246 | ||
1247 | if 'anqp[266]' in bss: | |
1248 | raise Exception("AP Civic Location ANQP-element unexpectedly seen") | |
1249 | ||
47eac38a JM |
1250 | if 'anqp[267]' not in bss: |
1251 | raise Exception("AP Location Public Identifier ANQP-element not seen") | |
1252 | if bss['anqp[267]'] != public_id: | |
1253 | raise Exception("Unexpected AP Location Public Identifier ANQP-element value: " + bss['anqp[267]']) | |
1254 | ||
1a4b4c84 JM |
1255 | if 'anqp[279]' not in bss: |
1256 | raise Exception("ANQP-element Info ID 279 not seen") | |
1257 | if bss['anqp[279]'] != "01020304": | |
1258 | raise Exception("Unexpected AP ANQP-element Info ID 279 value: " + bss['anqp[279]']) | |
1c532fd5 JM |
1259 | |
1260 | if 'anqp[299]' not in bss: | |
1261 | raise Exception("ANQP-element Info ID 299 not seen") | |
1262 | if bss['anqp[299]'] != "0102": | |
1263 | raise Exception("Unexpected AP ANQP-element Info ID 299 value: " + bss['anqp[299]']) | |
1264 | ||
1265 | if 'anqp_ip_addr_type_availability' not in bss: | |
1266 | raise Exception("ANQP-element Info ID 292 not seen") | |
1267 | if bss['anqp_ip_addr_type_availability'] != "1122334455": | |
1268 | raise Exception("Unexpected AP ANQP-element Info ID 262 value: " + bss['anqp_ip_addr_type_availability']) | |
61854f16 JM |
1269 | |
1270 | def test_gas_anqp_address3_not_assoc(dev, apdev, params): | |
1271 | """GAS/ANQP query using IEEE 802.11 compliant Address 3 value when not associated""" | |
1272 | try: | |
1273 | _test_gas_anqp_address3_not_assoc(dev, apdev, params) | |
1274 | finally: | |
1275 | dev[0].request("SET gas_address3 0") | |
1276 | ||
1277 | def _test_gas_anqp_address3_not_assoc(dev, apdev, params): | |
1278 | hapd = start_ap(apdev[0]) | |
1279 | bssid = apdev[0]['bssid'] | |
1280 | ||
1281 | if "OK" not in dev[0].request("SET gas_address3 1"): | |
1282 | raise Exception("Failed to set gas_address3") | |
1283 | ||
1284 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1285 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"): | |
1286 | raise Exception("ANQP_GET command failed") | |
1287 | ||
1288 | ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5) | |
1289 | if ev is None: | |
1290 | raise Exception("GAS query start timed out") | |
1291 | ||
1292 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1293 | if ev is None: | |
1294 | raise Exception("GAS query timed out") | |
1295 | ||
1296 | ev = dev[0].wait_event(["RX-ANQP"], timeout=1) | |
1297 | if ev is None or "Venue Name" not in ev: | |
1298 | raise Exception("Did not receive Venue Name") | |
1299 | ||
1300 | ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10) | |
1301 | if ev is None: | |
1302 | raise Exception("ANQP-QUERY-DONE event not seen") | |
1303 | if "result=SUCCESS" not in ev: | |
1304 | raise Exception("Unexpected result: " + ev) | |
1305 | ||
1306 | out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"), | |
1307 | "wlan_mgt.fixed.category_code == 4 && (wlan_mgt.fixed.publicact == 0x0a || wlan_mgt.fixed.publicact == 0x0b)", | |
1308 | display=["wlan.bssid"]) | |
1309 | res = out.splitlines() | |
1310 | if len(res) != 2: | |
1311 | raise Exception("Unexpected number of GAS frames") | |
1312 | if res[0] != 'ff:ff:ff:ff:ff:ff': | |
1313 | raise Exception("GAS request used unexpected Address3 field value: " + res[0]) | |
1314 | if res[1] != 'ff:ff:ff:ff:ff:ff': | |
1315 | raise Exception("GAS response used unexpected Address3 field value: " + res[1]) | |
1316 | ||
1317 | def test_gas_anqp_address3_assoc(dev, apdev, params): | |
1318 | """GAS/ANQP query using IEEE 802.11 compliant Address 3 value when associated""" | |
1319 | try: | |
1320 | _test_gas_anqp_address3_assoc(dev, apdev, params) | |
1321 | finally: | |
1322 | dev[0].request("SET gas_address3 0") | |
1323 | ||
1324 | def _test_gas_anqp_address3_assoc(dev, apdev, params): | |
1325 | hapd = start_ap(apdev[0]) | |
1326 | bssid = apdev[0]['bssid'] | |
1327 | ||
1328 | if "OK" not in dev[0].request("SET gas_address3 1"): | |
1329 | raise Exception("Failed to set gas_address3") | |
1330 | ||
1331 | dev[0].scan_for_bss(bssid, freq="2412") | |
1332 | dev[0].connect("test-gas", key_mgmt="WPA-EAP", eap="TTLS", | |
1333 | identity="DOMAIN\mschapv2 user", anonymous_identity="ttls", | |
1334 | password="password", phase2="auth=MSCHAPV2", | |
1335 | ca_cert="auth_serv/ca.pem", scan_freq="2412") | |
1336 | ||
1337 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"): | |
1338 | raise Exception("ANQP_GET command failed") | |
1339 | ||
1340 | ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5) | |
1341 | if ev is None: | |
1342 | raise Exception("GAS query start timed out") | |
1343 | ||
1344 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1345 | if ev is None: | |
1346 | raise Exception("GAS query timed out") | |
1347 | ||
1348 | ev = dev[0].wait_event(["RX-ANQP"], timeout=1) | |
1349 | if ev is None or "Venue Name" not in ev: | |
1350 | raise Exception("Did not receive Venue Name") | |
1351 | ||
1352 | ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10) | |
1353 | if ev is None: | |
1354 | raise Exception("ANQP-QUERY-DONE event not seen") | |
1355 | if "result=SUCCESS" not in ev: | |
1356 | raise Exception("Unexpected result: " + ev) | |
1357 | ||
1358 | out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"), | |
1359 | "wlan_mgt.fixed.category_code == 4 && (wlan_mgt.fixed.publicact == 0x0a || wlan_mgt.fixed.publicact == 0x0b)", | |
1360 | display=["wlan.bssid"]) | |
1361 | res = out.splitlines() | |
1362 | if len(res) != 2: | |
1363 | raise Exception("Unexpected number of GAS frames") | |
1364 | if res[0] != bssid: | |
1365 | raise Exception("GAS request used unexpected Address3 field value: " + res[0]) | |
1366 | if res[1] != bssid: | |
1367 | raise Exception("GAS response used unexpected Address3 field value: " + res[1]) | |
1368 | ||
1369 | def test_gas_anqp_address3_ap_forced(dev, apdev, params): | |
1370 | """GAS/ANQP query using IEEE 802.11 compliant Address 3 value on AP""" | |
1371 | hapd = start_ap(apdev[0]) | |
1372 | bssid = apdev[0]['bssid'] | |
1373 | hapd.set("gas_address3", "1") | |
1374 | ||
1375 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1376 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"): | |
1377 | raise Exception("ANQP_GET command failed") | |
1378 | ||
1379 | ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5) | |
1380 | if ev is None: | |
1381 | raise Exception("GAS query start timed out") | |
1382 | ||
1383 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1384 | if ev is None: | |
1385 | raise Exception("GAS query timed out") | |
1386 | ||
1387 | ev = dev[0].wait_event(["RX-ANQP"], timeout=1) | |
1388 | if ev is None or "Venue Name" not in ev: | |
1389 | raise Exception("Did not receive Venue Name") | |
1390 | ||
1391 | ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10) | |
1392 | if ev is None: | |
1393 | raise Exception("ANQP-QUERY-DONE event not seen") | |
1394 | if "result=SUCCESS" not in ev: | |
1395 | raise Exception("Unexpected result: " + ev) | |
1396 | ||
1397 | out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"), | |
1398 | "wlan_mgt.fixed.category_code == 4 && (wlan_mgt.fixed.publicact == 0x0a || wlan_mgt.fixed.publicact == 0x0b)", | |
1399 | display=["wlan.bssid"]) | |
1400 | res = out.splitlines() | |
1401 | if len(res) != 2: | |
1402 | raise Exception("Unexpected number of GAS frames") | |
1403 | if res[0] != bssid: | |
1404 | raise Exception("GAS request used unexpected Address3 field value: " + res[0]) | |
1405 | if res[1] != 'ff:ff:ff:ff:ff:ff': | |
1406 | raise Exception("GAS response used unexpected Address3 field value: " + res[1]) | |
1407 | ||
1408 | def test_gas_anqp_address3_ap_non_compliant(dev, apdev, params): | |
1409 | """GAS/ANQP query using IEEE 802.11 non-compliant Address 3 (AP)""" | |
1410 | try: | |
1411 | _test_gas_anqp_address3_ap_non_compliant(dev, apdev, params) | |
1412 | finally: | |
1413 | dev[0].request("SET gas_address3 0") | |
1414 | ||
1415 | def _test_gas_anqp_address3_ap_non_compliant(dev, apdev, params): | |
1416 | hapd = start_ap(apdev[0]) | |
1417 | bssid = apdev[0]['bssid'] | |
1418 | hapd.set("gas_address3", "2") | |
1419 | ||
1420 | if "OK" not in dev[0].request("SET gas_address3 1"): | |
1421 | raise Exception("Failed to set gas_address3") | |
1422 | ||
1423 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1424 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"): | |
1425 | raise Exception("ANQP_GET command failed") | |
1426 | ||
1427 | ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5) | |
1428 | if ev is None: | |
1429 | raise Exception("GAS query start timed out") | |
1430 | ||
1431 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1432 | if ev is None: | |
1433 | raise Exception("GAS query timed out") | |
1434 | ||
1435 | ev = dev[0].wait_event(["RX-ANQP"], timeout=1) | |
1436 | if ev is None or "Venue Name" not in ev: | |
1437 | raise Exception("Did not receive Venue Name") | |
1438 | ||
1439 | ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10) | |
1440 | if ev is None: | |
1441 | raise Exception("ANQP-QUERY-DONE event not seen") | |
1442 | if "result=SUCCESS" not in ev: | |
1443 | raise Exception("Unexpected result: " + ev) | |
1444 | ||
1445 | out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"), | |
1446 | "wlan_mgt.fixed.category_code == 4 && (wlan_mgt.fixed.publicact == 0x0a || wlan_mgt.fixed.publicact == 0x0b)", | |
1447 | display=["wlan.bssid"]) | |
1448 | res = out.splitlines() | |
1449 | if len(res) != 2: | |
1450 | raise Exception("Unexpected number of GAS frames") | |
1451 | if res[0] != 'ff:ff:ff:ff:ff:ff': | |
1452 | raise Exception("GAS request used unexpected Address3 field value: " + res[0]) | |
1453 | if res[1] != bssid: | |
1454 | raise Exception("GAS response used unexpected Address3 field value: " + res[1]) | |
37a67659 | 1455 | |
008aa15a JM |
1456 | def test_gas_anqp_address3_pmf(dev, apdev): |
1457 | """GAS/ANQP query using IEEE 802.11 compliant Address 3 value with PMF""" | |
1458 | try: | |
1459 | _test_gas_anqp_address3_pmf(dev, apdev) | |
1460 | finally: | |
1461 | dev[0].request("SET gas_address3 0") | |
1462 | ||
1463 | def _test_gas_anqp_address3_pmf(dev, apdev): | |
1464 | hapd = start_ap(apdev[0]) | |
1465 | bssid = apdev[0]['bssid'] | |
1466 | hapd.set("gas_comeback_delay", "2") | |
1467 | hapd.set("gas_address3", "1") | |
1468 | ||
1469 | if "OK" not in dev[0].request("SET gas_address3 1"): | |
1470 | raise Exception("Failed to set gas_address3") | |
1471 | ||
1472 | dev[0].scan_for_bss(bssid, freq="2412") | |
1473 | dev[0].connect("test-gas", key_mgmt="WPA-EAP", eap="TTLS", | |
1474 | identity="DOMAIN\mschapv2 user", anonymous_identity="ttls", | |
1475 | password="password", phase2="auth=MSCHAPV2", | |
1476 | ca_cert="auth_serv/ca.pem", scan_freq="2412", | |
1477 | ieee80211w="2") | |
1478 | ||
1479 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"): | |
1480 | raise Exception("ANQP_GET command failed") | |
1481 | ||
1482 | ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5) | |
1483 | if ev is None: | |
1484 | raise Exception("GAS query start timed out") | |
1485 | ||
1486 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1487 | if ev is None: | |
1488 | raise Exception("GAS query timed out") | |
1489 | ||
1490 | ev = dev[0].wait_event(["RX-ANQP"], timeout=1) | |
1491 | if ev is None or "Venue Name" not in ev: | |
1492 | raise Exception("Did not receive Venue Name") | |
1493 | ||
1494 | ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10) | |
1495 | if ev is None: | |
1496 | raise Exception("ANQP-QUERY-DONE event not seen") | |
1497 | if "result=SUCCESS" not in ev: | |
1498 | raise Exception("Unexpected result: " + ev) | |
1499 | ||
1500 | req = dev[0].request("GAS_REQUEST " + bssid + " 42 000102000101") | |
1501 | if "FAIL" in req: | |
1502 | raise Exception("GAS query request rejected") | |
1503 | expect_gas_result(dev[0], "FAILURE", "59") | |
1504 | ||
37a67659 JM |
1505 | def test_gas_prot_vs_not_prot(dev, apdev, params): |
1506 | """GAS/ANQP query protected vs. not protected""" | |
1507 | hapd = start_ap(apdev[0]) | |
1508 | bssid = apdev[0]['bssid'] | |
1509 | ||
1510 | dev[0].scan_for_bss(bssid, freq="2412") | |
1511 | dev[0].connect("test-gas", key_mgmt="WPA-EAP", eap="TTLS", | |
1512 | identity="DOMAIN\mschapv2 user", anonymous_identity="ttls", | |
1513 | password="password", phase2="auth=MSCHAPV2", | |
1514 | ca_cert="auth_serv/ca.pem", scan_freq="2412", | |
1515 | ieee80211w="2") | |
1516 | ||
1517 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"): | |
1518 | raise Exception("ANQP_GET command failed") | |
1519 | ||
1520 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5) | |
1521 | if ev is None: | |
1522 | raise Exception("No GAS-QUERY-DONE event") | |
1523 | if "result=SUCCESS" not in ev: | |
1524 | raise Exception("Unexpected GAS result: " + ev) | |
1525 | ||
1526 | # GAS: Drop unexpected unprotected GAS frame when PMF is enabled | |
1527 | dev[0].request("SET ext_mgmt_frame_handling 1") | |
1528 | res = dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=d0003a010200000000000200000003000200000003001000040b00000005006c027f000000") | |
1529 | dev[0].request("SET ext_mgmt_frame_handling 0") | |
1530 | if "OK" not in res: | |
1531 | raise Exception("MGMT_RX_PROCESS failed") | |
1532 | ||
1533 | dev[0].request("DISCONNECT") | |
1534 | dev[0].wait_disconnected() | |
1535 | ||
1536 | # GAS: No pending query found for 02:00:00:00:03:00 dialog token 0 | |
1537 | dev[0].request("SET ext_mgmt_frame_handling 1") | |
1538 | res = dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=d0003a010200000000000200000003000200000003001000040b00000005006c027f000000") | |
1539 | dev[0].request("SET ext_mgmt_frame_handling 0") | |
1540 | if "OK" not in res: | |
1541 | raise Exception("MGMT_RX_PROCESS failed") | |
1542 | ||
1543 | # GAS: Drop unexpected protected GAS frame when PMF is disabled | |
1544 | dev[0].request("SET ext_mgmt_frame_handling 1") | |
1545 | res = dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=d0003a010200000000000200000003000200000003001000090b00000005006c027f000000") | |
1546 | dev[0].request("SET ext_mgmt_frame_handling 0") | |
1547 | if "OK" not in res: | |
1548 | raise Exception("MGMT_RX_PROCESS failed") | |
643be15d JM |
1549 | |
1550 | def test_gas_failures(dev, apdev): | |
1551 | """GAS failure cases""" | |
1552 | hapd = start_ap(apdev[0]) | |
1553 | hapd.set("gas_comeback_delay", "5") | |
1554 | bssid = apdev[0]['bssid'] | |
1555 | ||
1556 | hapd2 = start_ap(apdev[1]) | |
1557 | bssid2 = apdev[1]['bssid'] | |
1558 | ||
1559 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1560 | dev[0].scan_for_bss(bssid2, freq="2412") | |
1561 | ||
1562 | tests = [ (bssid, "gas_build_req;gas_query_tx_comeback_req"), | |
1563 | (bssid, "gas_query_tx;gas_query_tx_comeback_req"), | |
1564 | (bssid, "gas_query_append;gas_query_rx_comeback"), | |
1565 | (bssid2, "gas_query_append;gas_query_rx_initial"), | |
1566 | (bssid2, "wpabuf_alloc_copy;gas_query_rx_initial"), | |
1567 | (bssid, "gas_query_tx;gas_query_tx_initial_req") ] | |
1568 | for addr,func in tests: | |
1569 | with alloc_fail(dev[0], 1, func): | |
1570 | dev[0].request("ANQP_GET " + addr + " 258") | |
1571 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5) | |
1572 | if ev is None: | |
1573 | raise Exception("No GAS-QUERY-DONE seen") | |
1574 | if "result=INTERNAL_ERROR" not in ev: | |
1575 | raise Exception("Unexpected result code: " + ev) | |
1576 | dev[0].dump_monitor() | |
1577 | ||
1578 | tests = [ "=gas_query_req", "radio_add_work;gas_query_req" ] | |
1579 | for func in tests: | |
1580 | with alloc_fail(dev[0], 1, func): | |
1581 | if "FAIL" not in dev[0].request("ANQP_GET " + bssid + " 258"): | |
1582 | raise Exception("ANQP_GET succeeded unexpectedly during OOM") | |
1583 | dev[0].dump_monitor() | |
1584 | ||
1585 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
1586 | wpas.interface_add("wlan5") | |
1587 | wpas.scan_for_bss(bssid2, freq="2412") | |
1588 | wpas.request("SET preassoc_mac_addr 1111") | |
1589 | wpas.request("ANQP_GET " + bssid2 + " 258") | |
1590 | ev = wpas.wait_event(["Failed to assign random MAC address for GAS"], | |
1591 | timeout=5) | |
1592 | wpas.request("SET preassoc_mac_addr 0") | |
1593 | if ev is None: | |
1594 | raise Exception("No random MAC address error seen") | |
8e5c2c20 JM |
1595 | |
1596 | def test_gas_anqp_venue_url(dev, apdev): | |
1597 | """GAS/ANQP and Venue URL""" | |
1598 | venue_group = 1 | |
1599 | venue_type = 13 | |
1600 | venue_info = struct.pack('BB', venue_group, venue_type) | |
1601 | lang1 = "eng" | |
1602 | name1= "Example venue" | |
1603 | lang2 = "fin" | |
1604 | name2 = "Esimerkkipaikka" | |
1605 | venue1 = struct.pack('B', len(lang1 + name1)) + lang1 + name1 | |
1606 | venue2 = struct.pack('B', len(lang2 + name2)) + lang2 + name2 | |
7ab74770 | 1607 | venue_name = binascii.hexlify(venue_info + venue1 + venue2).decode() |
8e5c2c20 JM |
1608 | |
1609 | url1 = "http://example.com/venue" | |
1610 | url2 = "https://example.org/venue-info/" | |
f68caa35 JM |
1611 | duple1 = struct.pack('BB', 1 + len(url1), 1) + url1 |
1612 | duple2 = struct.pack('BB', 1 + len(url2), 2) + url2 | |
7ab74770 | 1613 | venue_url = binascii.hexlify(duple1 + duple2).decode() |
8e5c2c20 JM |
1614 | |
1615 | params = { "ssid": "gas/anqp", | |
1616 | "interworking": "1", | |
1617 | "venue_group": str(venue_group), | |
1618 | "venue_type": str(venue_type), | |
1619 | "venue_name": [ lang1 + ":" + name1, lang2 + ":" + name2 ], | |
1620 | "anqp_elem": [ "277:" + venue_url ] } | |
1621 | hapd = hostapd.add_ap(apdev[0], params) | |
1622 | bssid = apdev[0]['bssid'] | |
1623 | ||
1624 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1625 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 257,258,277"): | |
1626 | raise Exception("ANQP_GET command failed") | |
1627 | ||
1628 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1629 | if ev is None: | |
1630 | raise Exception("GAS query timed out") | |
1631 | ||
bb2c25d0 JM |
1632 | ev = dev[0].wait_event(["RX-VENUE-URL"], timeout=0.1) |
1633 | if ev is not None: | |
1634 | raise Exception("Unexpected Venue URL indication without PMF") | |
1635 | ||
8e5c2c20 JM |
1636 | bss = dev[0].get_bss(bssid) |
1637 | ||
1638 | if 'anqp_venue_name' not in bss: | |
1639 | raise Exception("Venue Name ANQP-element not seen") | |
1640 | if bss['anqp_venue_name'] != venue_name: | |
1641 | raise Exception("Unexpected Venue Name ANQP-element value: " + bss['anqp_venue_name']) | |
1642 | if 'anqp[277]' not in bss: | |
1643 | raise Exception("Venue URL ANQP-element not seen") | |
1644 | if bss['anqp[277]'] != venue_url: | |
1645 | raise Exception("Unexpected Venue URL ANQP-element value: " + bss['anqp[277]']) | |
1646 | ||
1647 | if 'anqp_capability_list' not in bss: | |
1648 | raise Exception("Capability List ANQP-element not seen") | |
1649 | ids = struct.pack('<HHH', 257, 258, 277) | |
7ab74770 | 1650 | if not bss['anqp_capability_list'].startswith(binascii.hexlify(ids).decode()): |
8e5c2c20 | 1651 | raise Exception("Unexpected Capability List ANQP-element value: " + bss['anqp_capability_list']) |
f68caa35 JM |
1652 | |
1653 | def test_gas_anqp_venue_url2(dev, apdev): | |
1654 | """GAS/ANQP and Venue URL (hostapd venue_url)""" | |
1655 | venue_group = 1 | |
1656 | venue_type = 13 | |
1657 | venue_info = struct.pack('BB', venue_group, venue_type) | |
1658 | lang1 = "eng" | |
1659 | name1= "Example venue" | |
1660 | lang2 = "fin" | |
1661 | name2 = "Esimerkkipaikka" | |
1662 | venue1 = struct.pack('B', len(lang1 + name1)) + lang1 + name1 | |
1663 | venue2 = struct.pack('B', len(lang2 + name2)) + lang2 + name2 | |
7ab74770 | 1664 | venue_name = binascii.hexlify(venue_info + venue1 + venue2).decode() |
f68caa35 JM |
1665 | |
1666 | url1 = "http://example.com/venue" | |
1667 | url2 = "https://example.org/venue-info/" | |
1668 | duple1 = struct.pack('BB', 1 + len(url1), 1) + url1 | |
1669 | duple2 = struct.pack('BB', 1 + len(url2), 2) + url2 | |
7ab74770 | 1670 | venue_url = binascii.hexlify(duple1 + duple2).decode() |
f68caa35 JM |
1671 | |
1672 | params = { "ssid": "gas/anqp", | |
1673 | "interworking": "1", | |
1674 | "venue_group": str(venue_group), | |
1675 | "venue_type": str(venue_type), | |
1676 | "venue_name": [ lang1 + ":" + name1, lang2 + ":" + name2 ], | |
1677 | "venue_url": [ "1:" + url1, "2:" + url2 ] } | |
1678 | hapd = hostapd.add_ap(apdev[0], params) | |
1679 | bssid = apdev[0]['bssid'] | |
1680 | ||
1681 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1682 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 257,258,277"): | |
1683 | raise Exception("ANQP_GET command failed") | |
1684 | ||
1685 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1686 | if ev is None: | |
1687 | raise Exception("GAS query timed out") | |
1688 | ||
1689 | bss = dev[0].get_bss(bssid) | |
1690 | ||
1691 | if 'anqp_venue_name' not in bss: | |
1692 | raise Exception("Venue Name ANQP-element not seen") | |
1693 | if bss['anqp_venue_name'] != venue_name: | |
1694 | raise Exception("Unexpected Venue Name ANQP-element value: " + bss['anqp_venue_name']) | |
1695 | if 'anqp[277]' not in bss: | |
1696 | raise Exception("Venue URL ANQP-element not seen") | |
1697 | if bss['anqp[277]'] != venue_url: | |
89896c00 | 1698 | print(venue_url) |
f68caa35 JM |
1699 | raise Exception("Unexpected Venue URL ANQP-element value: " + bss['anqp[277]']) |
1700 | ||
1701 | if 'anqp_capability_list' not in bss: | |
1702 | raise Exception("Capability List ANQP-element not seen") | |
1703 | ids = struct.pack('<HHH', 257, 258, 277) | |
7ab74770 | 1704 | if not bss['anqp_capability_list'].startswith(binascii.hexlify(ids).decode()): |
f68caa35 | 1705 | raise Exception("Unexpected Capability List ANQP-element value: " + bss['anqp_capability_list']) |
242fc738 | 1706 | |
bb2c25d0 JM |
1707 | def test_gas_anqp_venue_url_pmf(dev, apdev): |
1708 | """GAS/ANQP and Venue URL with PMF""" | |
1709 | venue_group = 1 | |
1710 | venue_type = 13 | |
1711 | venue_info = struct.pack('BB', venue_group, venue_type) | |
1712 | lang1 = "eng" | |
1713 | name1= "Example venue" | |
1714 | lang2 = "fin" | |
1715 | name2 = "Esimerkkipaikka" | |
1716 | venue1 = struct.pack('B', len(lang1 + name1)) + lang1 + name1 | |
1717 | venue2 = struct.pack('B', len(lang2 + name2)) + lang2 + name2 | |
1718 | venue_name = binascii.hexlify(venue_info + venue1 + venue2) | |
1719 | ||
1720 | url1 = "http://example.com/venue" | |
1721 | url2 = "https://example.org/venue-info/" | |
1722 | ||
1723 | params = { "ssid": "gas/anqp/pmf", | |
1724 | "wpa": "2", | |
1725 | "wpa_key_mgmt": "WPA-PSK", | |
1726 | "rsn_pairwise": "CCMP", | |
1727 | "wpa_passphrase": "12345678", | |
1728 | "ieee80211w": "2", | |
1729 | "interworking": "1", | |
1730 | "venue_group": str(venue_group), | |
1731 | "venue_type": str(venue_type), | |
1732 | "venue_name": [ lang1 + ":" + name1, lang2 + ":" + name2 ], | |
1733 | "venue_url": [ "1:" + url1, "2:" + url2 ] } | |
1734 | hapd = hostapd.add_ap(apdev[0], params) | |
1735 | bssid = apdev[0]['bssid'] | |
1736 | ||
1737 | dev[0].connect("gas/anqp/pmf", psk="12345678", ieee80211w="2", | |
1738 | scan_freq="2412") | |
1739 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 277"): | |
1740 | raise Exception("ANQP_GET command failed") | |
1741 | ||
1742 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1743 | if ev is None: | |
1744 | raise Exception("GAS query timed out") | |
1745 | ||
1746 | ev = dev[0].wait_event(["RX-VENUE-URL"], timeout=5) | |
1747 | if ev is None: | |
1748 | raise Exception("No Venue URL indication seen") | |
1749 | if "1 " + url1 not in ev: | |
1750 | raise Exception("Unexpected Venue URL information: " + ev) | |
1751 | ||
1752 | ev = dev[0].wait_event(["RX-VENUE-URL"], timeout=5) | |
1753 | if ev is None: | |
1754 | raise Exception("No Venue URL indication seen (2)") | |
1755 | if "2 " + url2 not in ev: | |
1756 | raise Exception("Unexpected Venue URL information (2): " + ev) | |
1757 | ||
242fc738 JM |
1758 | def test_gas_anqp_capab_list(dev, apdev): |
1759 | """GAS/ANQP and Capability List ANQP-element""" | |
1760 | params = { "ssid": "gas/anqp", | |
1761 | "interworking": "1" } | |
1762 | params["anqp_elem"] = [] | |
1763 | for i in range(0, 400): | |
1764 | if i not in [ 257 ]: | |
1765 | params["anqp_elem"] += [ "%d:010203" % i ] | |
1766 | hapd = hostapd.add_ap(apdev[0], params) | |
1767 | bssid = apdev[0]['bssid'] | |
1768 | ||
1769 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1770 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 257"): | |
1771 | raise Exception("ANQP_GET command failed") | |
1772 | ||
1773 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1774 | if ev is None: | |
1775 | raise Exception("GAS query timed out") | |
1776 | ||
1777 | bss = dev[0].get_bss(bssid) | |
1778 | ||
1779 | if 'anqp_capability_list' not in bss: | |
1780 | raise Exception("Capability List ANQP-element not seen") | |
1781 | val = bss['anqp_capability_list'] | |
1782 | logger.info("anqp_capability_list: " + val) | |
1783 | ids = [] | |
1784 | while len(val) >= 4: | |
1785 | id_bin = binascii.unhexlify(val[0:4]) | |
1786 | id = struct.unpack('<H', id_bin)[0] | |
1787 | if id == 0xdddd: | |
1788 | break | |
1789 | ids.append(id) | |
1790 | val = val[4:] | |
1791 | logger.info("InfoIDs: " + str(ids)) | |
1792 | for i in range(257, 300): | |
1a4b4c84 JM |
1793 | if i in [ 273, 274 ]: |
1794 | continue | |
242fc738 JM |
1795 | if i not in ids: |
1796 | raise Exception("Unexpected Capability List ANQP-element value (missing %d): %s" % (i, bss['anqp_capability_list'])) | |
008aa15a JM |
1797 | |
1798 | def test_gas_server_oom(dev, apdev): | |
1799 | """GAS server OOM""" | |
1800 | bssid = apdev[0]['bssid'] | |
1801 | params = hs20_ap_params() | |
1802 | params['hessid'] = bssid | |
1803 | params['gas_comeback_delay'] = "5" | |
1804 | hapd = hostapd.add_ap(apdev[0], params) | |
1805 | ||
1806 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1807 | ||
1808 | tests = [ "ap_sta_add;gas_dialog_create", | |
1809 | "=gas_dialog_create", | |
1810 | "wpabuf_alloc_copy;gas_serv_rx_gas_comeback_req" ] | |
1811 | for t in tests: | |
1812 | with alloc_fail(hapd, 1, t): | |
1813 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"): | |
1814 | raise Exception("ANQP_GET command failed") | |
1815 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5) | |
1816 | if ev is None: | |
1817 | raise Exception("No GAS-QUERY-DONE seen") | |
1818 | dev[0].dump_monitor() | |
1819 | ||
1820 | hapd.set("gas_comeback_delay", "0") | |
1821 | ||
1822 | tests = [ "gas_serv_build_gas_resp_payload" ] | |
1823 | for t in tests: | |
1824 | with alloc_fail(hapd, 1, t): | |
1825 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"): | |
1826 | raise Exception("ANQP_GET command failed") | |
1827 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5) | |
1828 | if ev is None: | |
1829 | raise Exception("No GAS-QUERY-DONE seen") | |
1830 | dev[0].dump_monitor() | |
1831 | ||
1832 | with alloc_fail(hapd, 1, | |
1833 | "gas_build_initial_resp;gas_serv_rx_gas_initial_req"): | |
1834 | req = dev[0].request("GAS_REQUEST " + bssid + " 42 000102000101") | |
1835 | if "FAIL" in req: | |
1836 | raise Exception("GAS query request rejected") | |
1837 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5) | |
1838 | if ev is None: | |
1839 | raise Exception("No GAS-QUERY-DONE seen") | |
1840 | dev[0].dump_monitor() | |
1841 | ||
1842 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
1843 | wpas.interface_add("wlan5") | |
1844 | if "OK" not in wpas.request("P2P_SET listen_channel 1"): | |
1845 | raise Exception("Failed to set listen channel") | |
1846 | if "OK" not in wpas.p2p_listen(): | |
1847 | raise Exception("Failed to start listen state") | |
1848 | if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"): | |
1849 | raise Exception("Failed to enable external management frame handling") | |
1850 | ||
1851 | msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_COMEBACK_REQUEST, 1) | |
7ab74770 | 1852 | req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg).decode()) |
008aa15a JM |
1853 | with alloc_fail(hapd, 1, |
1854 | "gas_anqp_build_comeback_resp_buf;gas_serv_rx_gas_comeback_req"): | |
1855 | if "OK" not in wpas.request(req): | |
1856 | raise Exception("Could not send management frame") | |
1857 | wait_fail_trigger(hapd, "GET_ALLOC_FAIL") | |
1858 | ||
1859 | def test_gas_anqp_overrides(dev, apdev): | |
1860 | """GAS and ANQP overrides""" | |
1861 | params = { "ssid": "gas/anqp", | |
1862 | "interworking": "1", | |
1863 | "anqp_elem": [ "257:111111", | |
1864 | "258:222222", | |
1865 | "260:333333", | |
1866 | "261:444444", | |
1867 | "262:555555", | |
1868 | "263:666666", | |
1869 | "264:777777", | |
1870 | "268:888888", | |
1871 | "275:999999" ] } | |
1872 | hapd = hostapd.add_ap(apdev[0], params) | |
1873 | bssid = apdev[0]['bssid'] | |
1874 | ||
1875 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1876 | if "OK" not in dev[0].request("ANQP_GET " + bssid + " 257,258,260,261,262,263,264,268,275"): | |
1877 | raise Exception("ANQP_GET command failed") | |
1878 | ||
1879 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10) | |
1880 | if ev is None: | |
1881 | raise Exception("GAS query timed out") | |
de5ca36e JM |
1882 | elems = 9 |
1883 | capa = dev[0].get_capability("fils") | |
1884 | if capa is None or "FILS" not in capa: | |
1885 | # FILS Realm Info not supported in the build | |
1886 | elems -= 1 | |
1887 | for i in range(elems): | |
008aa15a JM |
1888 | ev = dev[0].wait_event(["RX-ANQP"], timeout=5) |
1889 | if ev is None: | |
1890 | raise Exception("ANQP response not seen") | |
1891 | ||
1892 | def test_gas_no_dialog_token_match(dev, apdev): | |
1893 | """GAS and no dialog token match for comeback request""" | |
1894 | hapd = start_ap(apdev[0]) | |
1895 | hapd.set("gas_frag_limit", "50") | |
1896 | bssid = apdev[0]['bssid'] | |
1897 | ||
1898 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
1899 | wpas.interface_add("wlan5") | |
1900 | if "OK" not in wpas.request("P2P_SET listen_channel 1"): | |
1901 | raise Exception("Failed to set listen channel") | |
1902 | if "OK" not in wpas.p2p_listen(): | |
1903 | raise Exception("Failed to start listen state") | |
1904 | if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"): | |
1905 | raise Exception("Failed to enable external management frame handling") | |
1906 | ||
1907 | anqp_query = struct.pack('<HHHHHHHHHH', 256, 16, 257, 258, 260, 261, 262, 263, 264, 268) | |
1908 | gas = struct.pack('<H', len(anqp_query)) + anqp_query | |
1909 | ||
1910 | dialog_token = 100 | |
1911 | msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST, | |
1912 | dialog_token) + anqp_adv_proto() + gas | |
7ab74770 | 1913 | req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg).decode()) |
008aa15a JM |
1914 | if "OK" not in wpas.request(req): |
1915 | raise Exception("Could not send management frame") | |
1916 | resp = wpas.mgmt_rx() | |
1917 | if resp is None: | |
1918 | raise Exception("MGMT-RX timeout") | |
1919 | if 'payload' not in resp: | |
1920 | raise Exception("Missing payload") | |
1921 | gresp = parse_gas(resp['payload']) | |
1922 | if gresp['dialog_token'] != dialog_token: | |
1923 | raise Exception("Dialog token mismatch") | |
1924 | status_code = gresp['status_code'] | |
1925 | if status_code != 0: | |
1926 | raise Exception("Unexpected status code {}".format(status_code)) | |
1927 | ||
1928 | msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_COMEBACK_REQUEST, | |
1929 | dialog_token + 1) | |
7ab74770 | 1930 | req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg).decode()) |
008aa15a JM |
1931 | if "OK" not in wpas.request(req): |
1932 | raise Exception("Could not send management frame") | |
1933 | resp = wpas.mgmt_rx() | |
1934 | if resp is None: | |
1935 | raise Exception("MGMT-RX timeout") | |
1936 | if 'payload' not in resp: | |
1937 | raise Exception("Missing payload") | |
1938 | gresp = parse_gas(resp['payload']) | |
1939 | status_code = gresp['status_code'] | |
1940 | if status_code != 60: | |
1941 | raise Exception("Unexpected failure status code {}".format(status_code)) | |
1942 | ||
1943 | def test_gas_vendor_spec_errors(dev, apdev): | |
1944 | """GAS and vendor specific request error cases""" | |
1945 | bssid = apdev[0]['bssid'] | |
1946 | params = hs20_ap_params() | |
1947 | params['hessid'] = bssid | |
1948 | params['osu_server_uri'] = "uri" | |
1949 | params['hs20_icon'] = "32:32:eng:image/png:icon32:/tmp/icon32.png" | |
1950 | del params['nai_realm'] | |
1951 | hapd = hostapd.add_ap(apdev[0], params) | |
1952 | ||
1953 | dev[0].scan_for_bss(bssid, freq="2412", force_scan=True) | |
1954 | tests = [ "00 12340000", | |
1955 | "00 dddd0300506fff", | |
1956 | "00 dddd0400506fffff", | |
1957 | "00 dddd0400506f9aff", | |
1958 | "00 dddd0400506f9a11", | |
1959 | "00 dddd0600506f9a11ff00", | |
1960 | "00 dddd0600506f9a110600", | |
1961 | "00 dddd0600506f9a110600", | |
1962 | "00 dddd0700506f9a11060000", | |
1963 | "00 dddd0700506f9a110600ff", | |
1964 | "00 dddd0800506f9a110600ff00", | |
1965 | "00 dddd0900506f9a110600ff0000", | |
1966 | "00 dddd0900506f9a110600ff0001", | |
1967 | "00 dddd0900506f9a110600ffff00", | |
1968 | "00 dddd0a00506f9a110600ff00013b", | |
1969 | "00 dddd0700506f9a110100ff", | |
1970 | "00 dddd0700506f9a11010008", | |
1971 | "00 dddd14", | |
1972 | "00 dddd1400506f9a11" ] | |
1973 | for t in tests: | |
1974 | req = dev[0].request("GAS_REQUEST " + bssid + " " + t) | |
1975 | if "FAIL" in req: | |
1976 | raise Exception("GAS query request rejected") | |
1977 | ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5) | |
1978 | if ev is None: | |
1979 | raise Exception("GAS query did not start") | |
1980 | ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5) | |
1981 | if ev is None: | |
1982 | raise Exception("GAS query did not complete") | |
1983 | if t == "00 dddd0600506f9a110600": | |
1984 | hapd.set("nai_realm", "0,another.example.com") | |
1985 | ||
1986 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
1987 | wpas.interface_add("wlan5") | |
1988 | if "OK" not in wpas.request("P2P_SET listen_channel 1"): | |
1989 | raise Exception("Failed to set listen channel") | |
1990 | if "OK" not in wpas.p2p_listen(): | |
1991 | raise Exception("Failed to start listen state") | |
1992 | if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"): | |
1993 | raise Exception("Failed to enable external management frame handling") | |
1994 | ||
1995 | anqp_query = struct.pack('<HHHHHHHHHH', 256, 16, 257, 258, 260, 261, 262, 263, 264, 268) | |
1996 | gas = struct.pack('<H', len(anqp_query)) + anqp_query | |
1997 | ||
1998 | dialog_token = 100 | |
1999 | adv = struct.pack('BBBB', 109, 2, 0, 0) | |
2000 | adv2 = struct.pack('BBB', 108, 1, 0) | |
2001 | adv3 = struct.pack('BBBB', 108, 3, 0, 0) | |
2002 | msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST, | |
2003 | dialog_token) + adv + gas | |
2004 | msg2 = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST, | |
2005 | dialog_token) + adv2 + gas | |
2006 | msg3 = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST, | |
2007 | dialog_token) + adv3 | |
2008 | msg4 = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST, | |
2009 | dialog_token) + anqp_adv_proto() | |
2010 | msg5 = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST, | |
2011 | dialog_token) + anqp_adv_proto() + struct.pack('<H', 1) | |
2012 | msg6 = struct.pack('<BB', ACTION_CATEG_PUBLIC, GAS_COMEBACK_REQUEST) | |
2013 | tests = [ msg, msg2, msg3, msg4, msg5, msg6 ] | |
2014 | for t in tests: | |
7ab74770 | 2015 | req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(t).decode()) |
008aa15a JM |
2016 | if "OK" not in wpas.request(req): |
2017 | raise Exception("Could not send management frame") | |
2018 | ev = wpas.wait_event(["MGMT-TX-STATUS"], timeout=5) | |
2019 | if ev is None: | |
2020 | raise Exception("No ACK frame seen") |