tests: GAS fragmentation and comeback delay
[mech_eap.git] / tests / hwsim / test_gas.py
1 # GAS tests
2 # Copyright (c) 2013, Qualcomm Atheros, Inc.
3 # Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
4 #
5 # This software may be distributed under the terms of the BSD license.
6 # See README for more details.
7
8 import time
9 import binascii
10 import logging
11 logger = logging.getLogger()
12 import re
13 import struct
14
15 import hostapd
16 from wpasupplicant import WpaSupplicant
17 from utils import alloc_fail, skip_with_fips
18 from hwsim import HWSimRadio
19
20 def hs20_ap_params():
21     params = hostapd.wpa2_params(ssid="test-gas")
22     params['wpa_key_mgmt'] = "WPA-EAP"
23     params['ieee80211w'] = "1"
24     params['ieee8021x'] = "1"
25     params['auth_server_addr'] = "127.0.0.1"
26     params['auth_server_port'] = "1812"
27     params['auth_server_shared_secret'] = "radius"
28     params['interworking'] = "1"
29     params['access_network_type'] = "14"
30     params['internet'] = "1"
31     params['asra'] = "0"
32     params['esr'] = "0"
33     params['uesa'] = "0"
34     params['venue_group'] = "7"
35     params['venue_type'] = "1"
36     params['venue_name'] = [ "eng:Example venue", "fin:Esimerkkipaikka" ]
37     params['roaming_consortium'] = [ "112233", "1020304050", "010203040506",
38                                      "fedcba" ]
39     params['domain_name'] = "example.com,another.example.com"
40     params['nai_realm'] = [ "0,example.com,13[5:6],21[2:4][5:7]",
41                             "0,another.example.com" ]
42     params['anqp_3gpp_cell_net'] = "244,91"
43     params['network_auth_type'] = "02http://www.example.com/redirect/me/here/"
44     params['ipaddr_type_availability'] = "14"
45     params['hs20'] = "1"
46     params['hs20_oper_friendly_name'] = [ "eng:Example operator", "fin:Esimerkkioperaattori" ]
47     params['hs20_wan_metrics'] = "01:8000:1000:80:240:3000"
48     params['hs20_conn_capab'] = [ "1:0:2", "6:22:1", "17:5060:0" ]
49     params['hs20_operating_class'] = "5173"
50     return params
51
52 def start_ap(ap):
53     params = hs20_ap_params()
54     params['hessid'] = ap['bssid']
55     hostapd.add_ap(ap['ifname'], params)
56     return hostapd.Hostapd(ap['ifname'])
57
58 def get_gas_response(dev, bssid, info, allow_fetch_failure=False,
59                      extra_test=False):
60     exp = r'<.>(GAS-RESPONSE-INFO) addr=([0-9a-f:]*) dialog_token=([0-9]*) status_code=([0-9]*) resp_len=([\-0-9]*)'
61     res = re.split(exp, info)
62     if len(res) < 6:
63         raise Exception("Could not parse GAS-RESPONSE-INFO")
64     if res[2] != bssid:
65         raise Exception("Unexpected BSSID in response")
66     token = res[3]
67     status = res[4]
68     if status != "0":
69         raise Exception("GAS query failed")
70     resp_len = res[5]
71     if resp_len == "-1":
72         raise Exception("GAS query reported invalid response length")
73     if int(resp_len) > 2000:
74         raise Exception("Unexpected long GAS response")
75
76     if extra_test:
77         if "FAIL" not in dev.request("GAS_RESPONSE_GET " + bssid + " 123456"):
78             raise Exception("Invalid dialog token accepted")
79         if "FAIL-Invalid range" not in dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 10000,10001"):
80             raise Exception("Invalid range accepted")
81         if "FAIL-Invalid range" not in dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 0,10000"):
82             raise Exception("Invalid range accepted")
83         if "FAIL" not in dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 0"):
84             raise Exception("Invalid GAS_RESPONSE_GET accepted")
85
86         res1_2 = dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 1,2")
87         res5_3 = dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 5,3")
88
89     resp = dev.request("GAS_RESPONSE_GET " + bssid + " " + token)
90     if "FAIL" in resp:
91         if allow_fetch_failure:
92             logger.debug("GAS response was not available anymore")
93             return
94         raise Exception("Could not fetch GAS response")
95     if len(resp) != int(resp_len) * 2:
96         raise Exception("Unexpected GAS response length")
97     logger.debug("GAS response: " + resp)
98     if extra_test:
99         if resp[2:6] != res1_2:
100             raise Exception("Unexpected response substring res1_2: " + res1_2)
101         if resp[10:16] != res5_3:
102             raise Exception("Unexpected response substring res5_3: " + res5_3)
103
104 def test_gas_generic(dev, apdev):
105     """Generic GAS query"""
106     bssid = apdev[0]['bssid']
107     params = hs20_ap_params()
108     params['hessid'] = bssid
109     hostapd.add_ap(apdev[0]['ifname'], params)
110
111     cmds = [ "foo",
112              "00:11:22:33:44:55",
113              "00:11:22:33:44:55 ",
114              "00:11:22:33:44:55  ",
115              "00:11:22:33:44:55 1",
116              "00:11:22:33:44:55 1 1234",
117              "00:11:22:33:44:55 qq",
118              "00:11:22:33:44:55 qq 1234",
119              "00:11:22:33:44:55 00      1",
120              "00:11:22:33:44:55 00 123",
121              "00:11:22:33:44:55 00 ",
122              "00:11:22:33:44:55 00 qq" ]
123     for cmd in cmds:
124         if "FAIL" not in dev[0].request("GAS_REQUEST " + cmd):
125             raise Exception("Invalid GAS_REQUEST accepted: " + cmd)
126
127     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
128     req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
129     if "FAIL" in req:
130         raise Exception("GAS query request rejected")
131     ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10)
132     if ev is None:
133         raise Exception("GAS query timed out")
134     get_gas_response(dev[0], bssid, ev, extra_test=True)
135
136     if "FAIL" not in dev[0].request("GAS_RESPONSE_GET ff"):
137         raise Exception("Invalid GAS_RESPONSE_GET accepted")
138
139 def test_gas_concurrent_scan(dev, apdev):
140     """Generic GAS queries with concurrent scan operation"""
141     bssid = apdev[0]['bssid']
142     params = hs20_ap_params()
143     params['hessid'] = bssid
144     hostapd.add_ap(apdev[0]['ifname'], params)
145
146     # get BSS entry available to allow GAS query
147     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
148
149     logger.info("Request concurrent operations")
150     req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
151     if "FAIL" in req:
152         raise Exception("GAS query request rejected")
153     req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000801")
154     if "FAIL" in req:
155         raise Exception("GAS query request rejected")
156     dev[0].scan(no_wait=True)
157     req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000201")
158     if "FAIL" in req:
159         raise Exception("GAS query request rejected")
160     req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000501")
161     if "FAIL" in req:
162         raise Exception("GAS query request rejected")
163
164     responses = 0
165     for i in range(0, 5):
166         ev = dev[0].wait_event(["GAS-RESPONSE-INFO", "CTRL-EVENT-SCAN-RESULTS"],
167                                timeout=10)
168         if ev is None:
169             raise Exception("Operation timed out")
170         if "GAS-RESPONSE-INFO" in ev:
171             responses = responses + 1
172             get_gas_response(dev[0], bssid, ev, allow_fetch_failure=True)
173
174     if responses != 4:
175         raise Exception("Unexpected number of GAS responses")
176
177 def test_gas_concurrent_connect(dev, apdev):
178     """Generic GAS queries with concurrent connection operation"""
179     skip_with_fips(dev[0])
180     bssid = apdev[0]['bssid']
181     params = hs20_ap_params()
182     params['hessid'] = bssid
183     hostapd.add_ap(apdev[0]['ifname'], params)
184
185     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
186
187     logger.debug("Start concurrent connect and GAS request")
188     dev[0].connect("test-gas", key_mgmt="WPA-EAP", eap="TTLS",
189                    identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
190                    password="password", phase2="auth=MSCHAPV2",
191                    ca_cert="auth_serv/ca.pem", wait_connect=False,
192                    scan_freq="2412")
193     req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
194     if "FAIL" in req:
195         raise Exception("GAS query request rejected")
196
197     ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "GAS-RESPONSE-INFO"],
198                            timeout=20)
199     if ev is None:
200         raise Exception("Operation timed out")
201     if "CTRL-EVENT-CONNECTED" not in ev:
202         raise Exception("Unexpected operation order")
203
204     ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "GAS-RESPONSE-INFO"],
205                            timeout=20)
206     if ev is None:
207         raise Exception("Operation timed out")
208     if "GAS-RESPONSE-INFO" not in ev:
209         raise Exception("Unexpected operation order")
210     get_gas_response(dev[0], bssid, ev)
211
212     dev[0].request("DISCONNECT")
213     dev[0].wait_disconnected(timeout=5)
214
215     logger.debug("Wait six seconds for expiration of connect-without-scan")
216     time.sleep(6)
217     dev[0].dump_monitor()
218
219     logger.debug("Start concurrent GAS request and connect")
220     req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
221     if "FAIL" in req:
222         raise Exception("GAS query request rejected")
223     dev[0].request("RECONNECT")
224
225     ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10)
226     if ev is None:
227         raise Exception("Operation timed out")
228     get_gas_response(dev[0], bssid, ev)
229
230     ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=20)
231     if ev is None:
232         raise Exception("No new scan results reported")
233
234     ev = dev[0].wait_connected(timeout=20, error="Operation tiemd out")
235     if "CTRL-EVENT-CONNECTED" not in ev:
236         raise Exception("Unexpected operation order")
237
238 def gas_fragment_and_comeback(dev, apdev, frag_limit=0, comeback_delay=0):
239     hapd = start_ap(apdev)
240     if frag_limit:
241         hapd.set("gas_frag_limit", str(frag_limit))
242     if comeback_delay:
243         hapd.set("gas_comeback_delay", str(comeback_delay))
244
245     dev.scan_for_bss(apdev['bssid'], freq="2412", force_scan=True)
246     dev.request("FETCH_ANQP")
247     ev = dev.wait_event(["GAS-QUERY-DONE"], timeout=5)
248     if ev is None:
249         raise Exception("No GAS-QUERY-DONE event")
250     if "result=SUCCESS" not in ev:
251         raise Exception("Unexpected GAS result: " + ev)
252     for i in range(0, 13):
253         ev = dev.wait_event(["RX-ANQP", "RX-HS20-ANQP"], timeout=5)
254         if ev is None:
255             raise Exception("Operation timed out")
256     ev = dev.wait_event(["ANQP-QUERY-DONE"], timeout=1)
257     if ev is None:
258         raise Exception("No ANQP-QUERY-DONE event")
259     if "result=SUCCESS" not in ev:
260         raise Exception("Unexpected ANQP result: " + ev)
261
262 def test_gas_fragment(dev, apdev):
263     """GAS fragmentation"""
264     gas_fragment_and_comeback(dev[0], apdev[0], frag_limit=50)
265
266 def test_gas_fragment_mcc(dev, apdev):
267     """GAS fragmentation with mac80211_hwsim MCC enabled"""
268     with HWSimRadio(n_channels=2) as (radio, iface):
269         wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
270         wpas.interface_add(iface)
271         gas_fragment_and_comeback(wpas, apdev[0], frag_limit=50)
272
273 def test_gas_fragment_with_comeback_delay(dev, apdev):
274     """GAS fragmentation and comeback delay"""
275     gas_fragment_and_comeback(dev[0], apdev[0], frag_limit=50,
276                               comeback_delay=500)
277
278 def test_gas_fragment_with_comeback_delay_mcc(dev, apdev):
279     """GAS fragmentation and comeback delay with mac80211_hwsim MCC enabled"""
280     with HWSimRadio(n_channels=2) as (radio, iface):
281         wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
282         wpas.interface_add(iface)
283         gas_fragment_and_comeback(wpas, apdev[0], frag_limit=50,
284                                   comeback_delay=500)
285
286 def test_gas_comeback_delay(dev, apdev):
287     """GAS comeback delay"""
288     hapd = start_ap(apdev[0])
289     hapd.set("gas_comeback_delay", "500")
290
291     dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
292     dev[0].request("FETCH_ANQP")
293     if "FAIL-BUSY" not in dev[0].request("SCAN"):
294         raise Exception("SCAN accepted during FETCH_ANQP")
295     for i in range(0, 6):
296         ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
297         if ev is None:
298             raise Exception("Operation timed out")
299
300 def test_gas_stop_fetch_anqp(dev, apdev):
301     """Stop FETCH_ANQP operation"""
302     hapd = start_ap(apdev[0])
303
304     dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
305     hapd.set("ext_mgmt_frame_handling", "1")
306     dev[0].request("FETCH_ANQP")
307     dev[0].request("STOP_FETCH_ANQP")
308     hapd.set("ext_mgmt_frame_handling", "0")
309     ev = dev[0].wait_event(["RX-ANQP", "GAS-QUERY-DONE"], timeout=10)
310     if ev is None:
311         raise Exception("GAS-QUERY-DONE timed out")
312     if "RX-ANQP" in ev:
313         raise Exception("Unexpected ANQP response received")
314
315 def test_gas_anqp_get(dev, apdev):
316     """GAS/ANQP query for both IEEE 802.11 and Hotspot 2.0 elements"""
317     hapd = start_ap(apdev[0])
318     bssid = apdev[0]['bssid']
319
320     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
321     if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258,268,hs20:3,hs20:4"):
322         raise Exception("ANQP_GET command failed")
323
324     ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
325     if ev is None:
326         raise Exception("GAS query start timed out")
327
328     ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
329     if ev is None:
330         raise Exception("GAS query timed out")
331
332     ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
333     if ev is None or "Venue Name" not in ev:
334         raise Exception("Did not receive Venue Name")
335
336     ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
337     if ev is None or "Domain Name list" not in ev:
338         raise Exception("Did not receive Domain Name list")
339
340     ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
341     if ev is None or "Operator Friendly Name" not in ev:
342         raise Exception("Did not receive Operator Friendly Name")
343
344     ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
345     if ev is None or "WAN Metrics" not in ev:
346         raise Exception("Did not receive WAN Metrics")
347
348     ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
349     if ev is None:
350         raise Exception("ANQP-QUERY-DONE event not seen")
351     if "result=SUCCESS" not in ev:
352         raise Exception("Unexpected result: " + ev)
353
354     if "OK" not in dev[0].request("HS20_ANQP_GET " + bssid + " 3,4"):
355         raise Exception("ANQP_GET command failed")
356
357     ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
358     if ev is None or "Operator Friendly Name" not in ev:
359         raise Exception("Did not receive Operator Friendly Name")
360
361     ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
362     if ev is None or "WAN Metrics" not in ev:
363         raise Exception("Did not receive WAN Metrics")
364
365     cmds = [ "",
366              "foo",
367              "00:11:22:33:44:55 258,hs20:-1",
368              "00:11:22:33:44:55 258,hs20:0",
369              "00:11:22:33:44:55 258,hs20:32",
370              "00:11:22:33:44:55 hs20:-1",
371              "00:11:22:33:44:55 hs20:0",
372              "00:11:22:33:44:55 hs20:32",
373              "00:11:22:33:44:55",
374              "00:11:22:33:44:55 ",
375              "00:11:22:33:44:55 0" ]
376     for cmd in cmds:
377         if "FAIL" not in dev[0].request("ANQP_GET " + cmd):
378             raise Exception("Invalid ANQP_GET accepted")
379
380     cmds = [ "",
381              "foo",
382              "00:11:22:33:44:55 -1",
383              "00:11:22:33:44:55 0",
384              "00:11:22:33:44:55 32",
385              "00:11:22:33:44:55",
386              "00:11:22:33:44:55 ",
387              "00:11:22:33:44:55 0" ]
388     for cmd in cmds:
389         if "FAIL" not in dev[0].request("HS20_ANQP_GET " + cmd):
390             raise Exception("Invalid HS20_ANQP_GET accepted")
391
392 def expect_gas_result(dev, result, status=None):
393     ev = dev.wait_event(["GAS-QUERY-DONE"], timeout=10)
394     if ev is None:
395         raise Exception("GAS query timed out")
396     if "result=" + result not in ev:
397         raise Exception("Unexpected GAS query result")
398     if status and "status_code=" + str(status) + ' ' not in ev:
399         raise Exception("Unexpected GAS status code")
400
401 def anqp_get(dev, bssid, id):
402     if "OK" not in dev.request("ANQP_GET " + bssid + " " + str(id)):
403         raise Exception("ANQP_GET command failed")
404     ev = dev.wait_event(["GAS-QUERY-START"], timeout=5)
405     if ev is None:
406         raise Exception("GAS query start timed out")
407
408 def test_gas_timeout(dev, apdev):
409     """GAS timeout"""
410     hapd = start_ap(apdev[0])
411     bssid = apdev[0]['bssid']
412
413     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
414     hapd.set("ext_mgmt_frame_handling", "1")
415
416     anqp_get(dev[0], bssid, 263)
417
418     ev = hapd.wait_event(["MGMT-RX"], timeout=5)
419     if ev is None:
420         raise Exception("MGMT RX wait timed out")
421
422     expect_gas_result(dev[0], "TIMEOUT")
423
424 MGMT_SUBTYPE_ACTION = 13
425 ACTION_CATEG_PUBLIC = 4
426
427 GAS_INITIAL_REQUEST = 10
428 GAS_INITIAL_RESPONSE = 11
429 GAS_COMEBACK_REQUEST = 12
430 GAS_COMEBACK_RESPONSE = 13
431 GAS_ACTIONS = [ GAS_INITIAL_REQUEST, GAS_INITIAL_RESPONSE,
432                 GAS_COMEBACK_REQUEST, GAS_COMEBACK_RESPONSE ]
433
434 def anqp_adv_proto():
435     return struct.pack('BBBB', 108, 2, 127, 0)
436
437 def anqp_initial_resp(dialog_token, status_code, comeback_delay=0):
438     return struct.pack('<BBBHH', ACTION_CATEG_PUBLIC, GAS_INITIAL_RESPONSE,
439                        dialog_token, status_code, comeback_delay) + anqp_adv_proto()
440
441 def anqp_comeback_resp(dialog_token, status_code=0, id=0, more=False, comeback_delay=0, bogus_adv_proto=False):
442     if more:
443         id |= 0x80
444     if bogus_adv_proto:
445         adv = struct.pack('BBBB', 108, 2, 127, 1)
446     else:
447         adv = anqp_adv_proto()
448     return struct.pack('<BBBHBH', ACTION_CATEG_PUBLIC, GAS_COMEBACK_RESPONSE,
449                        dialog_token, status_code, id, comeback_delay) + adv
450
451 def gas_rx(hapd):
452     count = 0
453     while count < 30:
454         count = count + 1
455         query = hapd.mgmt_rx()
456         if query is None:
457             raise Exception("Action frame not received")
458         if query['subtype'] != MGMT_SUBTYPE_ACTION:
459             continue
460         payload = query['payload']
461         if len(payload) < 2:
462             continue
463         (category, action) = struct.unpack('BB', payload[0:2])
464         if category != ACTION_CATEG_PUBLIC or action not in GAS_ACTIONS:
465             continue
466         return query
467     raise Exception("No Action frame received")
468
469 def parse_gas(payload):
470     pos = payload
471     (category, action, dialog_token) = struct.unpack('BBB', pos[0:3])
472     if category != ACTION_CATEG_PUBLIC:
473         return None
474     if action not in GAS_ACTIONS:
475         return None
476     gas = {}
477     gas['action'] = action
478     pos = pos[3:]
479
480     if len(pos) < 1 and action != GAS_COMEBACK_REQUEST:
481         return None
482
483     gas['dialog_token'] = dialog_token
484
485     if action == GAS_INITIAL_RESPONSE:
486         if len(pos) < 4:
487             return None
488         (status_code, comeback_delay) = struct.unpack('<HH', pos[0:4])
489         gas['status_code'] = status_code
490         gas['comeback_delay'] = comeback_delay
491
492     if action == GAS_COMEBACK_RESPONSE:
493         if len(pos) < 5:
494             return None
495         (status_code, frag, comeback_delay) = struct.unpack('<HBH', pos[0:5])
496         gas['status_code'] = status_code
497         gas['frag'] = frag
498         gas['comeback_delay'] = comeback_delay
499
500     return gas
501
502 def action_response(req):
503     resp = {}
504     resp['fc'] = req['fc']
505     resp['da'] = req['sa']
506     resp['sa'] = req['da']
507     resp['bssid'] = req['bssid']
508     return resp
509
510 def send_gas_resp(hapd, resp):
511     hapd.mgmt_tx(resp)
512     ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
513     if ev is None:
514         raise Exception("Missing TX status for GAS response")
515     if "ok=1" not in ev:
516         raise Exception("GAS response not acknowledged")
517
518 def test_gas_invalid_response_type(dev, apdev):
519     """GAS invalid response type"""
520     hapd = start_ap(apdev[0])
521     bssid = apdev[0]['bssid']
522
523     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
524     hapd.set("ext_mgmt_frame_handling", "1")
525
526     anqp_get(dev[0], bssid, 263)
527
528     query = gas_rx(hapd)
529     gas = parse_gas(query['payload'])
530
531     resp = action_response(query)
532     # GAS Comeback Response instead of GAS Initial Response
533     resp['payload'] = anqp_comeback_resp(gas['dialog_token']) + struct.pack('<H', 0)
534     send_gas_resp(hapd, resp)
535
536     # station drops the invalid frame, so this needs to result in GAS timeout
537     expect_gas_result(dev[0], "TIMEOUT")
538
539 def test_gas_failure_status_code(dev, apdev):
540     """GAS failure status code"""
541     hapd = start_ap(apdev[0])
542     bssid = apdev[0]['bssid']
543
544     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
545     hapd.set("ext_mgmt_frame_handling", "1")
546
547     anqp_get(dev[0], bssid, 263)
548
549     query = gas_rx(hapd)
550     gas = parse_gas(query['payload'])
551
552     resp = action_response(query)
553     resp['payload'] = anqp_initial_resp(gas['dialog_token'], 61) + struct.pack('<H', 0)
554     send_gas_resp(hapd, resp)
555
556     expect_gas_result(dev[0], "FAILURE")
557
558 def test_gas_malformed(dev, apdev):
559     """GAS malformed response frames"""
560     hapd = start_ap(apdev[0])
561     bssid = apdev[0]['bssid']
562
563     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
564     hapd.set("ext_mgmt_frame_handling", "1")
565
566     anqp_get(dev[0], bssid, 263)
567
568     query = gas_rx(hapd)
569     gas = parse_gas(query['payload'])
570
571     resp = action_response(query)
572
573     resp['payload'] = struct.pack('<BBBH', ACTION_CATEG_PUBLIC,
574                                   GAS_COMEBACK_RESPONSE,
575                                   gas['dialog_token'], 0)
576     hapd.mgmt_tx(resp)
577
578     resp['payload'] = struct.pack('<BBBHB', ACTION_CATEG_PUBLIC,
579                                   GAS_COMEBACK_RESPONSE,
580                                   gas['dialog_token'], 0, 0)
581     hapd.mgmt_tx(resp)
582
583     hdr = struct.pack('<BBBHH', ACTION_CATEG_PUBLIC, GAS_INITIAL_RESPONSE,
584                       gas['dialog_token'], 0, 0)
585     resp['payload'] = hdr + struct.pack('B', 108)
586     hapd.mgmt_tx(resp)
587     resp['payload'] = hdr + struct.pack('BB', 108, 0)
588     hapd.mgmt_tx(resp)
589     resp['payload'] = hdr + struct.pack('BB', 108, 1)
590     hapd.mgmt_tx(resp)
591     resp['payload'] = hdr + struct.pack('BB', 108, 255)
592     hapd.mgmt_tx(resp)
593     resp['payload'] = hdr + struct.pack('BBB', 108, 1, 127)
594     hapd.mgmt_tx(resp)
595     resp['payload'] = hdr + struct.pack('BBB', 108, 2, 127)
596     hapd.mgmt_tx(resp)
597     resp['payload'] = hdr + struct.pack('BBBB', 0, 2, 127, 0)
598     hapd.mgmt_tx(resp)
599
600     resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<H', 1)
601     hapd.mgmt_tx(resp)
602
603     resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<HB', 2, 0)
604     hapd.mgmt_tx(resp)
605
606     resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<H', 65535)
607     hapd.mgmt_tx(resp)
608
609     resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<HBB', 1, 0, 0)
610     hapd.mgmt_tx(resp)
611
612     # Station drops invalid frames, but the last of the responses is valid from
613     # GAS view point even though it has an extra octet in the end and the ANQP
614     # part of the response is not valid. This is reported as successfully
615     # completed GAS exchange.
616     expect_gas_result(dev[0], "SUCCESS")
617
618     ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5)
619     if ev is None:
620         raise Exception("ANQP-QUERY-DONE not reported")
621     if "result=INVALID_FRAME" not in ev:
622         raise Exception("Unexpected result: " + ev)
623
624 def init_gas(hapd, bssid, dev):
625     anqp_get(dev, bssid, 263)
626     query = gas_rx(hapd)
627     gas = parse_gas(query['payload'])
628     dialog_token = gas['dialog_token']
629
630     resp = action_response(query)
631     resp['payload'] = anqp_initial_resp(dialog_token, 0, comeback_delay=1) + struct.pack('<H', 0)
632     send_gas_resp(hapd, resp)
633
634     query = gas_rx(hapd)
635     gas = parse_gas(query['payload'])
636     if gas['action'] != GAS_COMEBACK_REQUEST:
637         raise Exception("Unexpected request action")
638     if gas['dialog_token'] != dialog_token:
639         raise Exception("Unexpected dialog token change")
640     return query, dialog_token
641
642 def allow_gas_initial_req(hapd, dialog_token):
643     msg = hapd.mgmt_rx(timeout=1)
644     if msg is not None:
645         gas = parse_gas(msg['payload'])
646         if gas['action'] != GAS_INITIAL_REQUEST or dialog_token == gas['dialog_token']:
647             raise Exception("Unexpected management frame")
648
649 def test_gas_malformed_comeback_resp(dev, apdev):
650     """GAS malformed comeback response frames"""
651     hapd = start_ap(apdev[0])
652     bssid = apdev[0]['bssid']
653
654     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
655     hapd.set("ext_mgmt_frame_handling", "1")
656
657     logger.debug("Non-zero status code in comeback response")
658     query, dialog_token = init_gas(hapd, bssid, dev[0])
659     resp = action_response(query)
660     resp['payload'] = anqp_comeback_resp(dialog_token, status_code=2) + struct.pack('<H', 0)
661     send_gas_resp(hapd, resp)
662     expect_gas_result(dev[0], "FAILURE", status=2)
663
664     logger.debug("Different advertisement protocol in comeback response")
665     query, dialog_token = init_gas(hapd, bssid, dev[0])
666     resp = action_response(query)
667     resp['payload'] = anqp_comeback_resp(dialog_token, bogus_adv_proto=True) + struct.pack('<H', 0)
668     send_gas_resp(hapd, resp)
669     expect_gas_result(dev[0], "PEER_ERROR")
670
671     logger.debug("Non-zero frag id and comeback delay in comeback response")
672     query, dialog_token = init_gas(hapd, bssid, dev[0])
673     resp = action_response(query)
674     resp['payload'] = anqp_comeback_resp(dialog_token, id=1, comeback_delay=1) + struct.pack('<H', 0)
675     send_gas_resp(hapd, resp)
676     expect_gas_result(dev[0], "PEER_ERROR")
677
678     logger.debug("Unexpected frag id in comeback response")
679     query, dialog_token = init_gas(hapd, bssid, dev[0])
680     resp = action_response(query)
681     resp['payload'] = anqp_comeback_resp(dialog_token, id=1) + struct.pack('<H', 0)
682     send_gas_resp(hapd, resp)
683     expect_gas_result(dev[0], "PEER_ERROR")
684
685     logger.debug("Empty fragment and replay in comeback response")
686     query, dialog_token = init_gas(hapd, bssid, dev[0])
687     resp = action_response(query)
688     resp['payload'] = anqp_comeback_resp(dialog_token, more=True) + struct.pack('<H', 0)
689     send_gas_resp(hapd, resp)
690     query = gas_rx(hapd)
691     gas = parse_gas(query['payload'])
692     if gas['action'] != GAS_COMEBACK_REQUEST:
693         raise Exception("Unexpected request action")
694     if gas['dialog_token'] != dialog_token:
695         raise Exception("Unexpected dialog token change")
696     resp = action_response(query)
697     resp['payload'] = anqp_comeback_resp(dialog_token) + struct.pack('<H', 0)
698     send_gas_resp(hapd, resp)
699     resp['payload'] = anqp_comeback_resp(dialog_token, id=1) + struct.pack('<H', 0)
700     send_gas_resp(hapd, resp)
701     expect_gas_result(dev[0], "SUCCESS")
702
703     logger.debug("Unexpected initial response when waiting for comeback response")
704     query, dialog_token = init_gas(hapd, bssid, dev[0])
705     resp = action_response(query)
706     resp['payload'] = anqp_initial_resp(dialog_token, 0) + struct.pack('<H', 0)
707     send_gas_resp(hapd, resp)
708     allow_gas_initial_req(hapd, dialog_token)
709     expect_gas_result(dev[0], "TIMEOUT")
710
711     logger.debug("Too short comeback response")
712     query, dialog_token = init_gas(hapd, bssid, dev[0])
713     resp = action_response(query)
714     resp['payload'] = struct.pack('<BBBH', ACTION_CATEG_PUBLIC,
715                                   GAS_COMEBACK_RESPONSE, dialog_token, 0)
716     send_gas_resp(hapd, resp)
717     allow_gas_initial_req(hapd, dialog_token)
718     expect_gas_result(dev[0], "TIMEOUT")
719
720     logger.debug("Too short comeback response(2)")
721     query, dialog_token = init_gas(hapd, bssid, dev[0])
722     resp = action_response(query)
723     resp['payload'] = struct.pack('<BBBHBB', ACTION_CATEG_PUBLIC,
724                                   GAS_COMEBACK_RESPONSE, dialog_token, 0, 0x80,
725                                   0)
726     send_gas_resp(hapd, resp)
727     allow_gas_initial_req(hapd, dialog_token)
728     expect_gas_result(dev[0], "TIMEOUT")
729
730     logger.debug("Maximum comeback response fragment claiming more fragments")
731     query, dialog_token = init_gas(hapd, bssid, dev[0])
732     resp = action_response(query)
733     resp['payload'] = anqp_comeback_resp(dialog_token, more=True) + struct.pack('<H', 0)
734     send_gas_resp(hapd, resp)
735     for i in range(1, 129):
736         query = gas_rx(hapd)
737         gas = parse_gas(query['payload'])
738         if gas['action'] != GAS_COMEBACK_REQUEST:
739             raise Exception("Unexpected request action")
740         if gas['dialog_token'] != dialog_token:
741             raise Exception("Unexpected dialog token change")
742         resp = action_response(query)
743         resp['payload'] = anqp_comeback_resp(dialog_token, id=i, more=True) + struct.pack('<H', 0)
744         send_gas_resp(hapd, resp)
745     expect_gas_result(dev[0], "PEER_ERROR")
746
747 def test_gas_comeback_resp_additional_delay(dev, apdev):
748     """GAS comeback response requesting additional delay"""
749     hapd = start_ap(apdev[0])
750     bssid = apdev[0]['bssid']
751
752     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
753     hapd.set("ext_mgmt_frame_handling", "1")
754
755     query, dialog_token = init_gas(hapd, bssid, dev[0])
756     for i in range(0, 2):
757         resp = action_response(query)
758         resp['payload'] = anqp_comeback_resp(dialog_token, status_code=95, comeback_delay=50) + struct.pack('<H', 0)
759         send_gas_resp(hapd, resp)
760         query = gas_rx(hapd)
761         gas = parse_gas(query['payload'])
762         if gas['action'] != GAS_COMEBACK_REQUEST:
763             raise Exception("Unexpected request action")
764         if gas['dialog_token'] != dialog_token:
765             raise Exception("Unexpected dialog token change")
766     resp = action_response(query)
767     resp['payload'] = anqp_comeback_resp(dialog_token, status_code=0) + struct.pack('<H', 0)
768     send_gas_resp(hapd, resp)
769     expect_gas_result(dev[0], "SUCCESS")
770
771 def test_gas_unknown_adv_proto(dev, apdev):
772     """Unknown advertisement protocol id"""
773     bssid = apdev[0]['bssid']
774     params = hs20_ap_params()
775     params['hessid'] = bssid
776     hostapd.add_ap(apdev[0]['ifname'], params)
777
778     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
779     req = dev[0].request("GAS_REQUEST " + bssid + " 42 000102000101")
780     if "FAIL" in req:
781         raise Exception("GAS query request rejected")
782     expect_gas_result(dev[0], "FAILURE", "59")
783     ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10)
784     if ev is None:
785         raise Exception("GAS query timed out")
786     exp = r'<.>(GAS-RESPONSE-INFO) addr=([0-9a-f:]*) dialog_token=([0-9]*) status_code=([0-9]*) resp_len=([\-0-9]*)'
787     res = re.split(exp, ev)
788     if len(res) < 6:
789         raise Exception("Could not parse GAS-RESPONSE-INFO")
790     if res[2] != bssid:
791         raise Exception("Unexpected BSSID in response")
792     status = res[4]
793     if status != "59":
794         raise Exception("Unexpected GAS-RESPONSE-INFO status")
795
796 def test_gas_max_pending(dev, apdev):
797     """GAS and maximum pending query limit"""
798     hapd = start_ap(apdev[0])
799     hapd.set("gas_frag_limit", "50")
800     bssid = apdev[0]['bssid']
801
802     wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
803     wpas.interface_add("wlan5")
804     if "OK" not in wpas.request("P2P_SET listen_channel 1"):
805         raise Exception("Failed to set listen channel")
806     if "OK" not in wpas.p2p_listen():
807         raise Exception("Failed to start listen state")
808     if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"):
809         raise Exception("Failed to enable external management frame handling")
810
811     anqp_query = struct.pack('<HHHHHHHHHH', 256, 16, 257, 258, 260, 261, 262, 263, 264, 268)
812     gas = struct.pack('<H', len(anqp_query)) + anqp_query
813
814     for dialog_token in range(1, 10):
815         msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST,
816                           dialog_token) + anqp_adv_proto() + gas
817         req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg))
818         if "OK" not in wpas.request(req):
819             raise Exception("Could not send management frame")
820         resp = wpas.mgmt_rx()
821         if resp is None:
822             raise Exception("MGMT-RX timeout")
823         if 'payload' not in resp:
824             raise Exception("Missing payload")
825         gresp = parse_gas(resp['payload'])
826         if gresp['dialog_token'] != dialog_token:
827             raise Exception("Dialog token mismatch")
828         status_code = gresp['status_code']
829         if dialog_token < 9 and status_code != 0:
830             raise Exception("Unexpected failure status code {} for dialog token {}".format(status_code, dialog_token))
831         if dialog_token > 8 and status_code == 0:
832             raise Exception("Unexpected success status code {} for dialog token {}".format(status_code, dialog_token))
833
834 def test_gas_no_pending(dev, apdev):
835     """GAS and no pending query for comeback request"""
836     hapd = start_ap(apdev[0])
837     bssid = apdev[0]['bssid']
838
839     wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
840     wpas.interface_add("wlan5")
841     if "OK" not in wpas.request("P2P_SET listen_channel 1"):
842         raise Exception("Failed to set listen channel")
843     if "OK" not in wpas.p2p_listen():
844         raise Exception("Failed to start listen state")
845     if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"):
846         raise Exception("Failed to enable external management frame handling")
847
848     msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_COMEBACK_REQUEST, 1)
849     req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg))
850     if "OK" not in wpas.request(req):
851         raise Exception("Could not send management frame")
852     resp = wpas.mgmt_rx()
853     if resp is None:
854         raise Exception("MGMT-RX timeout")
855     if 'payload' not in resp:
856         raise Exception("Missing payload")
857     gresp = parse_gas(resp['payload'])
858     status_code = gresp['status_code']
859     if status_code != 60:
860         raise Exception("Unexpected status code {} (expected 60)".format(status_code))
861
862 def test_gas_missing_payload(dev, apdev):
863     """No action code in the query frame"""
864     bssid = apdev[0]['bssid']
865     params = hs20_ap_params()
866     params['hessid'] = bssid
867     hostapd.add_ap(apdev[0]['ifname'], params)
868
869     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
870
871     cmd = "MGMT_TX {} {} freq=2412 action=040A".format(bssid, bssid)
872     if "FAIL" in dev[0].request(cmd):
873         raise Exception("Could not send test Action frame")
874     ev = dev[0].wait_event(["MGMT-TX-STATUS"], timeout=10)
875     if ev is None:
876         raise Exception("Timeout on MGMT-TX-STATUS")
877     if "result=SUCCESS" not in ev:
878         raise Exception("AP did not ack Action frame")
879
880     cmd = "MGMT_TX {} {} freq=2412 action=04".format(bssid, bssid)
881     if "FAIL" in dev[0].request(cmd):
882         raise Exception("Could not send test Action frame")
883     ev = dev[0].wait_event(["MGMT-TX-STATUS"], timeout=10)
884     if ev is None:
885         raise Exception("Timeout on MGMT-TX-STATUS")
886     if "result=SUCCESS" not in ev:
887         raise Exception("AP did not ack Action frame")
888
889 def test_gas_query_deinit(dev, apdev):
890     """Pending GAS/ANQP query during deinit"""
891     hapd = start_ap(apdev[0])
892     bssid = apdev[0]['bssid']
893
894     wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
895     wpas.interface_add("wlan5")
896
897     wpas.scan_for_bss(bssid, freq="2412", force_scan=True)
898     id = wpas.request("RADIO_WORK add block-work")
899     if "OK" not in wpas.request("ANQP_GET " + bssid + " 258"):
900         raise Exception("ANQP_GET command failed")
901
902     ev = wpas.wait_event(["GAS-QUERY-START", "EXT-RADIO-WORK-START"], timeout=5)
903     if ev is None:
904         raise Exception("Timeout while waiting radio work to start")
905     ev = wpas.wait_event(["GAS-QUERY-START", "EXT-RADIO-WORK-START"], timeout=5)
906     if ev is None:
907         raise Exception("Timeout while waiting radio work to start (2)")
908
909     # Remove the interface while the gas-query radio work is still pending and
910     # GAS query has not yet been started.
911     wpas.interface_remove("wlan5")
912
913 def test_gas_anqp_oom_wpas(dev, apdev):
914     """GAS/ANQP query and OOM in wpa_supplicant"""
915     hapd = start_ap(apdev[0])
916     bssid = apdev[0]['bssid']
917
918     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
919
920     with alloc_fail(dev[0], 1, "gas_build_req"):
921         if "FAIL" not in dev[0].request("ANQP_GET " + bssid + " 258"):
922             raise Exception("Unexpected ANQP_GET command success (OOM)")
923
924 def test_gas_anqp_oom_hapd(dev, apdev):
925     """GAS/ANQP query and OOM in hostapd"""
926     hapd = start_ap(apdev[0])
927     bssid = apdev[0]['bssid']
928
929     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
930
931     with alloc_fail(hapd, 1, "gas_build_resp"):
932         # This query will time out due to the AP not sending a response (OOM).
933         if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"):
934             raise Exception("ANQP_GET command failed")
935
936         ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
937         if ev is None:
938             raise Exception("GAS query start timed out")
939
940         ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
941         if ev is None:
942             raise Exception("GAS query timed out")
943         if "result=TIMEOUT" not in ev:
944             raise Exception("Unexpected result: " + ev)
945
946         ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
947         if ev is None:
948             raise Exception("ANQP-QUERY-DONE event not seen")
949         if "result=FAILURE" not in ev:
950             raise Exception("Unexpected result: " + ev)
951
952     with alloc_fail(hapd, 1, "gas_anqp_build_comeback_resp"):
953         hapd.set("gas_frag_limit", "50")
954
955         # The first attempt of this query will time out due to the AP not
956         # sending a response (OOM), but the retry succeeds.
957         dev[0].request("FETCH_ANQP")
958         ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
959         if ev is None:
960             raise Exception("GAS query start timed out")
961
962         ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
963         if ev is None:
964             raise Exception("GAS query timed out")
965         if "result=SUCCESS" not in ev:
966             raise Exception("Unexpected result: " + ev)
967
968         ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
969         if ev is None:
970             raise Exception("ANQP-QUERY-DONE event not seen")
971         if "result=SUCCESS" not in ev:
972             raise Exception("Unexpected result: " + ev)
973
974 def test_gas_anqp_extra_elements(dev, apdev):
975     """GAS/ANQP and extra ANQP elements"""
976     geo_loc = "001052834d12efd2b08b9b4bf1cc2c00004104050000000000060100"
977     civic_loc = "0000f9555302f50102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5"
978     params = { "ssid": "gas/anqp",
979                "interworking": "1",
980                "anqp_elem": [ "265:" + geo_loc,
981                               "266:" + civic_loc,
982                               "262:1122334455",
983                               "275:01020304",
984                               "60000:01",
985                               "299:0102" ] }
986     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
987     bssid = apdev[0]['bssid']
988
989     dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
990     if "OK" not in dev[0].request("ANQP_GET " + bssid + " 265,266"):
991         raise Exception("ANQP_GET command failed")
992
993     ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
994     if ev is None:
995         raise Exception("GAS query timed out")
996
997     bss = dev[0].get_bss(bssid)
998
999     if 'anqp[265]' not in bss:
1000         raise Exception("AP Geospatial Location ANQP-element not seen")
1001     if bss['anqp[265]'] != geo_loc:
1002         raise Exception("Unexpected AP Geospatial Location ANQP-element value: " + bss['anqp[265]'])
1003
1004     if 'anqp[266]' not in bss:
1005         raise Exception("AP Civic Location ANQP-element not seen")
1006     if bss['anqp[266]'] != civic_loc:
1007         raise Exception("Unexpected AP Civic Location ANQP-element value: " + bss['anqp[266]'])
1008
1009     dev[1].scan_for_bss(bssid, freq="2412", force_scan=True)
1010     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"):
1011         raise Exception("ANQP_GET command failed")
1012
1013     ev = dev[1].wait_event(["GAS-QUERY-DONE"], timeout=10)
1014     if ev is None:
1015         raise Exception("GAS query timed out")
1016
1017     bss = dev[1].get_bss(bssid)
1018
1019     if 'anqp[265]' not in bss:
1020         raise Exception("AP Geospatial Location ANQP-element not seen")
1021     if bss['anqp[265]'] != geo_loc:
1022         raise Exception("Unexpected AP Geospatial Location ANQP-element value: " + bss['anqp[265]'])
1023
1024     if 'anqp[266]' in bss:
1025         raise Exception("AP Civic Location ANQP-element unexpectedly seen")
1026
1027     if 'anqp[275]' not in bss:
1028         raise Exception("ANQP-element Info ID 275 not seen")
1029     if bss['anqp[275]'] != "01020304":
1030         raise Exception("Unexpected AP ANQP-element Info ID 299 value: " + bss['anqp[299]'])
1031
1032     if 'anqp[299]' not in bss:
1033         raise Exception("ANQP-element Info ID 299 not seen")
1034     if bss['anqp[299]'] != "0102":
1035         raise Exception("Unexpected AP ANQP-element Info ID 299 value: " + bss['anqp[299]'])
1036
1037     if 'anqp_ip_addr_type_availability' not in bss:
1038         raise Exception("ANQP-element Info ID 292 not seen")
1039     if bss['anqp_ip_addr_type_availability'] != "1122334455":
1040         raise Exception("Unexpected AP ANQP-element Info ID 262 value: " + bss['anqp_ip_addr_type_availability'])