]> git.ipfire.org Git - thirdparty/hostap.git/blob - tests/hwsim/test_ap_hs20.py
tests: Verify sharing and unsharing of ANQP results
[thirdparty/hostap.git] / tests / hwsim / test_ap_hs20.py
1 #!/usr/bin/python
2 #
3 # Hotspot 2.0 tests
4 # Copyright (c) 2013, Jouni Malinen <j@w1.fi>
5 #
6 # This software may be distributed under the terms of the BSD license.
7 # See README for more details.
8
9 import time
10 import subprocess
11 import logging
12 logger = logging.getLogger()
13 import os.path
14 import subprocess
15
16 import hostapd
17 from wlantest import Wlantest
18
19 def hs20_ap_params():
20 params = hostapd.wpa2_params(ssid="test-hs20")
21 params['wpa_key_mgmt'] = "WPA-EAP"
22 params['ieee80211w'] = "1"
23 params['ieee8021x'] = "1"
24 params['auth_server_addr'] = "127.0.0.1"
25 params['auth_server_port'] = "1812"
26 params['auth_server_shared_secret'] = "radius"
27 params['interworking'] = "1"
28 params['access_network_type'] = "14"
29 params['internet'] = "1"
30 params['asra'] = "0"
31 params['esr'] = "0"
32 params['uesa'] = "0"
33 params['venue_group'] = "7"
34 params['venue_type'] = "1"
35 params['venue_name'] = [ "eng:Example venue", "fin:Esimerkkipaikka" ]
36 params['roaming_consortium'] = [ "112233", "1020304050", "010203040506",
37 "fedcba" ]
38 params['domain_name'] = "example.com,another.example.com"
39 params['nai_realm'] = [ "0,example.com,13[5:6],21[2:4][5:7]",
40 "0,another.example.com" ]
41 params['hs20'] = "1"
42 params['hs20_wan_metrics'] = "01:8000:1000:80:240:3000"
43 params['hs20_conn_capab'] = [ "1:0:2", "6:22:1", "17:5060:0" ]
44 params['hs20_operating_class'] = "5173"
45 params['anqp_3gpp_cell_net'] = "244,91"
46 return params
47
48 def interworking_select(dev, bssid, type=None, no_match=False):
49 dev.dump_monitor()
50 dev.request("INTERWORKING_SELECT")
51 ev = dev.wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH"],
52 timeout=15)
53 if ev is None:
54 raise Exception("Network selection timed out");
55 if no_match:
56 if "INTERWORKING-NO-MATCH" not in ev:
57 raise Exception("Unexpected network match")
58 return
59 if "INTERWORKING-NO-MATCH" in ev:
60 raise Exception("Matching network not found")
61 if bssid and bssid not in ev:
62 raise Exception("Unexpected BSSID in match")
63 if type and "type=" + type not in ev:
64 raise Exception("Network type not recognized correctly")
65
66 def check_sp_type(dev, sp_type):
67 type = dev.get_status_field("sp_type")
68 if type is None:
69 raise Exception("sp_type not available")
70 if type != sp_type:
71 raise Exception("sp_type did not indicate home network")
72
73 def hlr_auc_gw_available():
74 if not os.path.exists("/tmp/hlr_auc_gw.sock"):
75 logger.info("No hlr_auc_gw available");
76 return False
77 if not os.path.exists("../../hostapd/hlr_auc_gw"):
78 logger.info("No hlr_auc_gw available");
79 return False
80 return True
81
82 def interworking_ext_sim_connect(dev, bssid, method):
83 dev.request("INTERWORKING_CONNECT " + bssid)
84
85 ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
86 if ev is None:
87 raise Exception("Network connected timed out")
88 if "(" + method + ")" not in ev:
89 raise Exception("Unexpected EAP method selection")
90
91 ev = dev.wait_event(["CTRL-REQ-SIM"], timeout=15)
92 if ev is None:
93 raise Exception("Wait for external SIM processing request timed out")
94 p = ev.split(':', 2)
95 if p[1] != "GSM-AUTH":
96 raise Exception("Unexpected CTRL-REQ-SIM type")
97 id = p[0].split('-')[3]
98 rand = p[2].split(' ')[0]
99
100 res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
101 "-m",
102 "auth_serv/hlr_auc_gw.milenage_db",
103 "GSM-AUTH-REQ 232010000000000 " + rand])
104 if "GSM-AUTH-RESP" not in res:
105 raise Exception("Unexpected hlr_auc_gw response")
106 resp = res.split(' ')[2].rstrip()
107
108 dev.request("CTRL-RSP-SIM-" + id + ":GSM-AUTH:" + resp)
109 ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
110 if ev is None:
111 raise Exception("Connection timed out")
112
113 def interworking_connect(dev, bssid, method):
114 dev.request("INTERWORKING_CONNECT " + bssid)
115
116 ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
117 if ev is None:
118 raise Exception("Network connected timed out")
119 if "(" + method + ")" not in ev:
120 raise Exception("Unexpected EAP method selection")
121
122 ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
123 if ev is None:
124 raise Exception("Connection timed out")
125
126 def check_probe_resp(wt, bssid_unexpected, bssid_expected):
127 if bssid_unexpected:
128 count = wt.get_bss_counter("probe_response", bssid_unexpected)
129 if count > 0:
130 raise Exception("Unexpected Probe Response frame from AP")
131
132 if bssid_expected:
133 count = wt.get_bss_counter("probe_response", bssid_expected)
134 if count == 0:
135 raise Exception("No Probe Response frame from AP")
136
137 def test_ap_anqp_sharing(dev, apdev):
138 """ANQP sharing within ESS and explicit unshare"""
139 bssid = apdev[0]['bssid']
140 params = hs20_ap_params()
141 params['hessid'] = bssid
142 hostapd.add_ap(apdev[0]['ifname'], params)
143
144 bssid2 = apdev[1]['bssid']
145 params = hs20_ap_params()
146 params['hessid'] = bssid
147 params['nai_realm'] = [ "0,example.com,13[5:6],21[2:4][5:7]" ]
148 hostapd.add_ap(apdev[1]['ifname'], params)
149
150 dev[0].request("SET ignore_old_scan_res 1")
151 dev[0].hs20_enable()
152 id = dev[0].add_cred_values({ 'realm': "example.com", 'username': "test",
153 'password': "secret",
154 'domain': "example.com" })
155 logger.info("Normal network selection with shared ANQP results")
156 interworking_select(dev[0], None, "home")
157 dev[0].dump_monitor()
158
159 res1 = dev[0].get_bss(bssid)
160 res2 = dev[0].get_bss(bssid2)
161 if res1['anqp_nai_realm'] != res2['anqp_nai_realm']:
162 raise Exception("ANQP results were not shared between BSSes")
163
164 logger.info("Explicit ANQP request to unshare ANQP results")
165 dev[0].request("ANQP_GET " + bssid + " 263")
166 ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
167 if ev is None:
168 raise Exception("ANQP operation timed out")
169
170 dev[0].request("ANQP_GET " + bssid2 + " 263")
171 ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
172 if ev is None:
173 raise Exception("ANQP operation timed out")
174
175 res1 = dev[0].get_bss(bssid)
176 res2 = dev[0].get_bss(bssid2)
177 if res1['anqp_nai_realm'] == res2['anqp_nai_realm']:
178 raise Exception("ANQP results were not unshared")
179
180 def test_ap_interworking_scan_filtering(dev, apdev):
181 """Interworking scan filtering with HESSID and access network type"""
182 bssid = apdev[0]['bssid']
183 params = hs20_ap_params()
184 ssid = "test-hs20-ap1"
185 params['ssid'] = ssid
186 params['hessid'] = bssid
187 hostapd.add_ap(apdev[0]['ifname'], params)
188
189 bssid2 = apdev[1]['bssid']
190 params = hs20_ap_params()
191 ssid2 = "test-hs20-ap2"
192 params['ssid'] = ssid2
193 params['hessid'] = bssid2
194 params['access_network_type'] = "1"
195 del params['venue_group']
196 del params['venue_type']
197 hostapd.add_ap(apdev[1]['ifname'], params)
198
199 dev[0].request("SET ignore_old_scan_res 1")
200 dev[0].hs20_enable()
201
202 wt = Wlantest()
203 wt.flush()
204
205 logger.info("Check probe request filtering based on HESSID")
206
207 dev[0].request("SET hessid " + bssid2)
208 dev[0].scan(freq="2412")
209 check_probe_resp(wt, bssid, bssid2)
210
211 logger.info("Check probe request filtering based on access network type")
212
213 wt.clear_bss_counters(bssid)
214 wt.clear_bss_counters(bssid2)
215 dev[0].request("SET hessid 00:00:00:00:00:00")
216 dev[0].request("SET access_network_type 14")
217 dev[0].scan(freq="2412")
218 check_probe_resp(wt, bssid2, bssid)
219
220 wt.clear_bss_counters(bssid)
221 wt.clear_bss_counters(bssid2)
222 dev[0].request("SET hessid 00:00:00:00:00:00")
223 dev[0].request("SET access_network_type 1")
224 dev[0].scan(freq="2412")
225 check_probe_resp(wt, bssid, bssid2)
226
227 logger.info("Check probe request filtering based on HESSID and ANT")
228
229 wt.clear_bss_counters(bssid)
230 wt.clear_bss_counters(bssid2)
231 dev[0].request("SET hessid " + bssid)
232 dev[0].request("SET access_network_type 14")
233 dev[0].scan(freq="2412")
234 check_probe_resp(wt, bssid2, bssid)
235
236 wt.clear_bss_counters(bssid)
237 wt.clear_bss_counters(bssid2)
238 dev[0].request("SET hessid " + bssid2)
239 dev[0].request("SET access_network_type 14")
240 dev[0].scan(freq="2412")
241 check_probe_resp(wt, bssid, None)
242 check_probe_resp(wt, bssid2, None)
243
244 wt.clear_bss_counters(bssid)
245 wt.clear_bss_counters(bssid2)
246 dev[0].request("SET hessid " + bssid)
247 dev[0].request("SET access_network_type 1")
248 dev[0].scan(freq="2412")
249 check_probe_resp(wt, bssid, None)
250 check_probe_resp(wt, bssid2, None)
251
252 def test_ap_hs20_select(dev, apdev):
253 """Hotspot 2.0 network selection"""
254 bssid = apdev[0]['bssid']
255 params = hs20_ap_params()
256 params['hessid'] = bssid
257 hostapd.add_ap(apdev[0]['ifname'], params)
258
259 dev[0].request("SET ignore_old_scan_res 1")
260 dev[0].hs20_enable()
261 id = dev[0].add_cred_values({ 'realm': "example.com", 'username': "test",
262 'password': "secret",
263 'domain': "example.com" })
264 interworking_select(dev[0], bssid, "home")
265
266 dev[0].remove_cred(id)
267 id = dev[0].add_cred_values({ 'realm': "example.com", 'username': "test",
268 'password': "secret",
269 'domain': "no.match.example.com" })
270 interworking_select(dev[0], bssid, "roaming")
271
272 dev[0].set_cred_quoted(id, "realm", "no.match.example.com");
273 interworking_select(dev[0], bssid, no_match=True)
274
275 def test_ap_hs20_ext_sim(dev, apdev):
276 """Hotspot 2.0 with external SIM processing"""
277 if not hlr_auc_gw_available():
278 return "skip"
279 bssid = apdev[0]['bssid']
280 params = hs20_ap_params()
281 params['hessid'] = bssid
282 params['anqp_3gpp_cell_net'] = "232,01"
283 params['domain_name'] = "wlan.mnc001.mcc232.3gppnetwork.org"
284 hostapd.add_ap(apdev[0]['ifname'], params)
285
286 dev[0].request("SET ignore_old_scan_res 1")
287 dev[0].hs20_enable()
288 dev[0].request("SET external_sim 1")
289 dev[0].add_cred_values({ 'imsi': "23201-0000000000", 'eap': "SIM" })
290 interworking_select(dev[0], "home")
291 interworking_ext_sim_connect(dev[0], bssid, "SIM")
292 check_sp_type(dev[0], "home")
293
294 def test_ap_hs20_ext_sim_roaming(dev, apdev):
295 """Hotspot 2.0 with external SIM processing in roaming network"""
296 if not hlr_auc_gw_available():
297 return "skip"
298 bssid = apdev[0]['bssid']
299 params = hs20_ap_params()
300 params['hessid'] = bssid
301 params['anqp_3gpp_cell_net'] = "244,91;310,026;232,01;234,56"
302 params['domain_name'] = "wlan.mnc091.mcc244.3gppnetwork.org"
303 hostapd.add_ap(apdev[0]['ifname'], params)
304
305 dev[0].request("SET ignore_old_scan_res 1")
306 dev[0].hs20_enable()
307 dev[0].request("SET external_sim 1")
308 dev[0].add_cred_values({ 'imsi': "23201-0000000000", 'eap': "SIM" })
309 interworking_select(dev[0], "roaming")
310 interworking_ext_sim_connect(dev[0], bssid, "SIM")
311 check_sp_type(dev[0], "roaming")
312
313 def test_ap_hs20_username(dev, apdev):
314 """Hotspot 2.0 connection in username/password credential"""
315 bssid = apdev[0]['bssid']
316 params = hs20_ap_params()
317 params['hessid'] = bssid
318 hostapd.add_ap(apdev[0]['ifname'], params)
319
320 dev[0].request("SET ignore_old_scan_res 1")
321 dev[0].hs20_enable()
322 id = dev[0].add_cred_values({ 'realm': "example.com",
323 'username': "hs20-test",
324 'password': "password",
325 'domain': "example.com" })
326 interworking_select(dev[0], bssid, "home")
327 interworking_connect(dev[0], bssid, "TTLS")
328 check_sp_type(dev[0], "home")
329
330 def test_ap_hs20_username_roaming(dev, apdev):
331 """Hotspot 2.0 connection in username/password credential (roaming)"""
332 bssid = apdev[0]['bssid']
333 params = hs20_ap_params()
334 params['nai_realm'] = [ "0,example.com,13[5:6],21[2:4][5:7]",
335 "0,roaming.example.com,21[2:4][5:7]",
336 "0,another.example.com" ]
337 params['domain_name'] = "another.example.com"
338 params['hessid'] = bssid
339 hostapd.add_ap(apdev[0]['ifname'], params)
340
341 dev[0].request("SET ignore_old_scan_res 1")
342 dev[0].hs20_enable()
343 id = dev[0].add_cred_values({ 'realm': "roaming.example.com",
344 'username': "hs20-test",
345 'password': "password",
346 'domain': "example.com" })
347 interworking_select(dev[0], bssid, "roaming")
348 interworking_connect(dev[0], bssid, "TTLS")
349 check_sp_type(dev[0], "roaming")
350
351 def test_ap_hs20_username_unknown(dev, apdev):
352 """Hotspot 2.0 connection in username/password credential (no domain in cred)"""
353 bssid = apdev[0]['bssid']
354 params = hs20_ap_params()
355 params['hessid'] = bssid
356 hostapd.add_ap(apdev[0]['ifname'], params)
357
358 dev[0].request("SET ignore_old_scan_res 1")
359 dev[0].hs20_enable()
360 id = dev[0].add_cred_values({ 'realm': "example.com",
361 'username': "hs20-test",
362 'password': "password" })
363 interworking_select(dev[0], bssid, "unknown")
364 interworking_connect(dev[0], bssid, "TTLS")
365 check_sp_type(dev[0], "unknown")
366
367 def test_ap_hs20_username_unknown2(dev, apdev):
368 """Hotspot 2.0 connection in username/password credential (no domain advertized)"""
369 bssid = apdev[0]['bssid']
370 params = hs20_ap_params()
371 params['hessid'] = bssid
372 del params['domain_name']
373 hostapd.add_ap(apdev[0]['ifname'], params)
374
375 dev[0].request("SET ignore_old_scan_res 1")
376 dev[0].hs20_enable()
377 id = dev[0].add_cred_values({ 'realm': "example.com",
378 'username': "hs20-test",
379 'password': "password",
380 'domain': "example.com" })
381 interworking_select(dev[0], bssid, "unknown")
382 interworking_connect(dev[0], bssid, "TTLS")
383 check_sp_type(dev[0], "unknown")
384
385 def test_ap_hs20_gas_while_associated(dev, apdev):
386 """Hotspot 2.0 connection with GAS query while associated"""
387 bssid = apdev[0]['bssid']
388 params = hs20_ap_params()
389 params['hessid'] = bssid
390 hostapd.add_ap(apdev[0]['ifname'], params)
391
392 dev[0].request("SET ignore_old_scan_res 1")
393 dev[0].hs20_enable()
394 id = dev[0].add_cred_values({ 'realm': "example.com",
395 'username': "hs20-test",
396 'password': "password",
397 'domain': "example.com" })
398 interworking_select(dev[0], bssid, "home")
399 interworking_connect(dev[0], bssid, "TTLS")
400
401 logger.info("Verifying GAS query while associated")
402 dev[0].request("FETCH_ANQP")
403 for i in range(0, 6):
404 ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
405 if ev is None:
406 raise Exception("Operation timed out")
407
408 def test_ap_hs20_gas_frag_while_associated(dev, apdev):
409 """Hotspot 2.0 connection with fragmented GAS query while associated"""
410 bssid = apdev[0]['bssid']
411 params = hs20_ap_params()
412 params['hessid'] = bssid
413 hostapd.add_ap(apdev[0]['ifname'], params)
414 hapd = hostapd.Hostapd(apdev[0]['ifname'])
415 hapd.set("gas_frag_limit", "50")
416
417 dev[0].request("SET ignore_old_scan_res 1")
418 dev[0].hs20_enable()
419 id = dev[0].add_cred_values({ 'realm': "example.com",
420 'username': "hs20-test",
421 'password': "password",
422 'domain': "example.com" })
423 interworking_select(dev[0], bssid, "home")
424 interworking_connect(dev[0], bssid, "TTLS")
425
426 logger.info("Verifying GAS query while associated")
427 dev[0].request("FETCH_ANQP")
428 for i in range(0, 6):
429 ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
430 if ev is None:
431 raise Exception("Operation timed out")
432
433 def test_ap_hs20_multiple_connects(dev, apdev):
434 """Hotspot 2.0 connection through multiple network selections"""
435 bssid = apdev[0]['bssid']
436 params = hs20_ap_params()
437 params['hessid'] = bssid
438 hostapd.add_ap(apdev[0]['ifname'], params)
439
440 dev[0].request("SET ignore_old_scan_res 1")
441 dev[0].hs20_enable()
442 values = { 'realm': "example.com",
443 'username': "hs20-test",
444 'password': "password",
445 'domain': "example.com" }
446 id = dev[0].add_cred_values(values)
447
448 for i in range(0, 3):
449 logger.info("Starting Interworking network selection")
450 dev[0].request("INTERWORKING_SELECT auto")
451 while True:
452 ev = dev[0].wait_event(["INTERWORKING-NO-MATCH",
453 "INTERWORKING-ALREADY-CONNECTED",
454 "CTRL-EVENT-CONNECTED"], timeout=15)
455 if ev is None:
456 raise Exception("Connection timed out")
457 if "INTERWORKING-NO-MATCH" in ev:
458 raise Exception("Matching AP not found")
459 if "CTRL-EVENT-CONNECTED" in ev:
460 break
461 if i == 2 and "INTERWORKING-ALREADY-CONNECTED" in ev:
462 break
463 if i == 0:
464 dev[0].request("DISCONNECT")
465 dev[0].dump_monitor()
466
467 networks = dev[0].list_networks()
468 if len(networks) > 1:
469 raise Exception("Duplicated network block detected")
470
471 def test_ap_hs20_disallow_aps(dev, apdev):
472 """Hotspot 2.0 connection and disallow_aps"""
473 bssid = apdev[0]['bssid']
474 params = hs20_ap_params()
475 params['hessid'] = bssid
476 hostapd.add_ap(apdev[0]['ifname'], params)
477
478 dev[0].request("SET ignore_old_scan_res 1")
479 dev[0].hs20_enable()
480 values = { 'realm': "example.com",
481 'username': "hs20-test",
482 'password': "password",
483 'domain': "example.com" }
484 id = dev[0].add_cred_values(values)
485
486 logger.info("Verify disallow_aps bssid")
487 dev[0].request("SET disallow_aps bssid " + bssid.translate(None, ':'))
488 dev[0].request("INTERWORKING_SELECT auto")
489 ev = dev[0].wait_event(["INTERWORKING-NO-MATCH"], timeout=15)
490 if ev is None:
491 raise Exception("Network selection timed out")
492 dev[0].dump_monitor()
493
494 logger.info("Verify disallow_aps ssid")
495 dev[0].request("SET disallow_aps ssid 746573742d68733230")
496 dev[0].request("INTERWORKING_SELECT auto")
497 ev = dev[0].wait_event(["INTERWORKING-NO-MATCH"], timeout=15)
498 if ev is None:
499 raise Exception("Network selection timed out")
500 dev[0].dump_monitor()
501
502 logger.info("Verify disallow_aps clear")
503 dev[0].request("SET disallow_aps ")
504 interworking_select(dev[0], bssid, "home")
505
506 dev[0].request("SET disallow_aps bssid " + bssid.translate(None, ':'))
507 ret = dev[0].request("INTERWORKING_CONNECT " + bssid)
508 if "FAIL" not in ret:
509 raise Exception("INTERWORKING_CONNECT to disallowed BSS not rejected")
510
511 def policy_test(dev, ap, values, only_one=True):
512 dev.dump_monitor()
513 logger.info("Verify network selection to AP " + ap['ifname'])
514 bssid = ap['bssid']
515 dev.request("SET ignore_old_scan_res 1")
516 dev.hs20_enable()
517 id = dev.add_cred_values(values)
518 dev.request("INTERWORKING_SELECT auto")
519 while True:
520 ev = dev.wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH",
521 "CTRL-EVENT-CONNECTED"], timeout=15)
522 if ev is None:
523 raise Exception("Connection timed out")
524 if "INTERWORKING-NO-MATCH" in ev:
525 raise Exception("Matching AP not found")
526 if only_one and "INTERWORKING-AP" in ev and bssid not in ev:
527 raise Exception("Unexpected AP claimed acceptable")
528 if "CTRL-EVENT-CONNECTED" in ev:
529 if bssid not in ev:
530 raise Exception("Connected to incorrect BSS")
531 break
532
533 conn_bssid = dev.get_status_field("bssid")
534 if conn_bssid != bssid:
535 raise Exception("bssid information points to incorrect BSS")
536
537 dev.remove_cred(id)
538 dev.dump_monitor()
539
540 def default_cred():
541 return { 'realm': "example.com",
542 'username': "hs20-test",
543 'password': "password" }
544
545 def test_ap_hs20_req_roaming_consortium(dev, apdev):
546 """Hotspot 2.0 required roaming consortium"""
547 params = hs20_ap_params()
548 hostapd.add_ap(apdev[0]['ifname'], params)
549
550 params = hs20_ap_params()
551 params['ssid'] = "test-hs20-other"
552 params['roaming_consortium'] = [ "223344" ]
553 hostapd.add_ap(apdev[1]['ifname'], params)
554
555 values = default_cred()
556 values['required_roaming_consortium'] = "223344"
557 policy_test(dev[0], apdev[1], values)
558 values['required_roaming_consortium'] = "112233"
559 policy_test(dev[0], apdev[0], values)
560
561 def test_ap_hs20_excluded_ssid(dev, apdev):
562 """Hotspot 2.0 exclusion based on SSID"""
563 params = hs20_ap_params()
564 hostapd.add_ap(apdev[0]['ifname'], params)
565
566 params = hs20_ap_params()
567 params['ssid'] = "test-hs20-other"
568 params['roaming_consortium'] = [ "223344" ]
569 hostapd.add_ap(apdev[1]['ifname'], params)
570
571 values = default_cred()
572 values['excluded_ssid'] = "test-hs20"
573 policy_test(dev[0], apdev[1], values)
574 values['excluded_ssid'] = "test-hs20-other"
575 policy_test(dev[0], apdev[0], values)