]>
Commit | Line | Data |
---|---|---|
93a06242 JM |
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 | |
c9aa4308 | 12 | logger = logging.getLogger() |
efd43d85 JM |
13 | import os.path |
14 | import subprocess | |
93a06242 JM |
15 | |
16 | import hostapd | |
17 | ||
18 | def hs20_ap_params(): | |
19 | params = hostapd.wpa2_params(ssid="test-hs20") | |
20 | params['wpa_key_mgmt'] = "WPA-EAP" | |
21 | params['ieee80211w'] = "1" | |
22 | params['ieee8021x'] = "1" | |
23 | params['auth_server_addr'] = "127.0.0.1" | |
24 | params['auth_server_port'] = "1812" | |
25 | params['auth_server_shared_secret'] = "radius" | |
26 | params['interworking'] = "1" | |
27 | params['access_network_type'] = "14" | |
28 | params['internet'] = "1" | |
29 | params['asra'] = "0" | |
30 | params['esr'] = "0" | |
31 | params['uesa'] = "0" | |
32 | params['venue_group'] = "7" | |
33 | params['venue_type'] = "1" | |
34 | params['venue_name'] = [ "eng:Example venue", "fin:Esimerkkipaikka" ] | |
35 | params['roaming_consortium'] = [ "112233", "1020304050", "010203040506", | |
36 | "fedcba" ] | |
37 | params['domain_name'] = "example.com,another.example.com" | |
38 | params['nai_realm'] = [ "0,example.com,13[5:6],21[2:4][5:7]", | |
39 | "0,another.example.com" ] | |
40 | params['hs20'] = "1" | |
41 | params['hs20_wan_metrics'] = "01:8000:1000:80:240:3000" | |
42 | params['hs20_conn_capab'] = [ "1:0:2", "6:22:1", "17:5060:0" ] | |
43 | params['hs20_operating_class'] = "5173" | |
44 | params['anqp_3gpp_cell_net'] = "244,91" | |
45 | return params | |
46 | ||
bbe86767 JM |
47 | def interworking_select(dev, bssid, type=None, no_match=False): |
48 | dev.dump_monitor() | |
49 | dev.request("INTERWORKING_SELECT") | |
50 | ev = dev.wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH"], | |
51 | timeout=15) | |
93a06242 JM |
52 | if ev is None: |
53 | raise Exception("Network selection timed out"); | |
bbe86767 JM |
54 | if no_match: |
55 | if "INTERWORKING-NO-MATCH" not in ev: | |
56 | raise Exception("Unexpected network match") | |
57 | return | |
93a06242 JM |
58 | if "INTERWORKING-NO-MATCH" in ev: |
59 | raise Exception("Matching network not found") | |
60 | if bssid not in ev: | |
61 | raise Exception("Unexpected BSSID in match") | |
bbe86767 JM |
62 | if type and "type=" + type not in ev: |
63 | raise Exception("Network type not recognized correctly") | |
93a06242 | 64 | |
bbe86767 JM |
65 | def check_sp_type(dev, sp_type): |
66 | type = dev.get_status_field("sp_type") | |
67 | if type is None: | |
68 | raise Exception("sp_type not available") | |
69 | if type != sp_type: | |
70 | raise Exception("sp_type did not indicate home network") | |
efd43d85 | 71 | |
bbe86767 | 72 | def hlr_auc_gw_available(): |
efd43d85 JM |
73 | if not os.path.exists("/tmp/hlr_auc_gw.sock"): |
74 | logger.info("No hlr_auc_gw available"); | |
bbe86767 | 75 | return False |
efd43d85 JM |
76 | if not os.path.exists("../../hostapd/hlr_auc_gw"): |
77 | logger.info("No hlr_auc_gw available"); | |
bbe86767 JM |
78 | return False |
79 | return True | |
efd43d85 | 80 | |
bbe86767 JM |
81 | def interworking_ext_sim_connect(dev, bssid, method): |
82 | dev.request("INTERWORKING_CONNECT " + bssid) | |
efd43d85 | 83 | |
bbe86767 | 84 | ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15) |
efd43d85 JM |
85 | if ev is None: |
86 | raise Exception("Network connected timed out") | |
bbe86767 | 87 | if "(" + method + ")" not in ev: |
efd43d85 JM |
88 | raise Exception("Unexpected EAP method selection") |
89 | ||
bbe86767 | 90 | ev = dev.wait_event(["CTRL-REQ-SIM"], timeout=15) |
efd43d85 JM |
91 | if ev is None: |
92 | raise Exception("Wait for external SIM processing request timed out") | |
93 | p = ev.split(':', 2) | |
94 | if p[1] != "GSM-AUTH": | |
95 | raise Exception("Unexpected CTRL-REQ-SIM type") | |
96 | id = p[0].split('-')[3] | |
97 | rand = p[2].split(' ')[0] | |
98 | ||
99 | res = subprocess.check_output(["../../hostapd/hlr_auc_gw", | |
100 | "-m", | |
101 | "auth_serv/hlr_auc_gw.milenage_db", | |
102 | "GSM-AUTH-REQ 232010000000000 " + rand]) | |
103 | if "GSM-AUTH-RESP" not in res: | |
104 | raise Exception("Unexpected hlr_auc_gw response") | |
105 | resp = res.split(' ')[2].rstrip() | |
106 | ||
bbe86767 JM |
107 | dev.request("CTRL-RSP-SIM-" + id + ":GSM-AUTH:" + resp) |
108 | ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=15) | |
efd43d85 JM |
109 | if ev is None: |
110 | raise Exception("Connection timed out") | |
f4defd91 | 111 | |
8fba2e5d JM |
112 | def interworking_connect(dev, bssid, method): |
113 | dev.request("INTERWORKING_CONNECT " + bssid) | |
114 | ||
115 | ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15) | |
116 | if ev is None: | |
117 | raise Exception("Network connected timed out") | |
118 | if "(" + method + ")" not in ev: | |
119 | raise Exception("Unexpected EAP method selection") | |
120 | ||
121 | ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=15) | |
122 | if ev is None: | |
123 | raise Exception("Connection timed out") | |
124 | ||
bbe86767 JM |
125 | def test_ap_hs20_select(dev, apdev): |
126 | """Hotspot 2.0 network selection""" | |
127 | bssid = apdev[0]['bssid'] | |
128 | params = hs20_ap_params() | |
129 | params['hessid'] = bssid | |
130 | hostapd.add_ap(apdev[0]['ifname'], params) | |
131 | ||
132 | dev[0].hs20_enable() | |
2232edf8 JM |
133 | id = dev[0].add_cred_values({ 'realm': "example.com", 'username': "test", |
134 | 'password': "secret", | |
135 | 'domain': "example.com" }) | |
bbe86767 JM |
136 | interworking_select(dev[0], bssid, "home") |
137 | ||
138 | dev[0].remove_cred(id) | |
2232edf8 JM |
139 | id = dev[0].add_cred_values({ 'realm': "example.com", 'username': "test", |
140 | 'password': "secret", | |
141 | 'domain': "no.match.example.com" }) | |
bbe86767 JM |
142 | interworking_select(dev[0], bssid, "roaming") |
143 | ||
144 | dev[0].set_cred_quoted(id, "realm", "no.match.example.com"); | |
145 | interworking_select(dev[0], bssid, no_match=True) | |
146 | ||
147 | def test_ap_hs20_ext_sim(dev, apdev): | |
148 | """Hotspot 2.0 with external SIM processing""" | |
149 | if not hlr_auc_gw_available(): | |
150 | return "skip" | |
151 | bssid = apdev[0]['bssid'] | |
152 | params = hs20_ap_params() | |
153 | params['hessid'] = bssid | |
154 | params['anqp_3gpp_cell_net'] = "232,01" | |
155 | params['domain_name'] = "wlan.mnc001.mcc232.3gppnetwork.org" | |
156 | hostapd.add_ap(apdev[0]['ifname'], params) | |
157 | ||
158 | dev[0].hs20_enable() | |
159 | dev[0].request("SET external_sim 1") | |
2232edf8 | 160 | dev[0].add_cred_values({ 'imsi': "23201-0000000000", 'eap': "SIM" }) |
bbe86767 JM |
161 | interworking_select(dev[0], "home") |
162 | interworking_ext_sim_connect(dev[0], bssid, "SIM") | |
163 | check_sp_type(dev[0], "home") | |
59f8a3c6 JM |
164 | |
165 | def test_ap_hs20_ext_sim_roaming(dev, apdev): | |
166 | """Hotspot 2.0 with external SIM processing in roaming network""" | |
167 | if not hlr_auc_gw_available(): | |
168 | return "skip" | |
169 | bssid = apdev[0]['bssid'] | |
170 | params = hs20_ap_params() | |
171 | params['hessid'] = bssid | |
172 | params['anqp_3gpp_cell_net'] = "244,91;310,026;232,01;234,56" | |
173 | params['domain_name'] = "wlan.mnc091.mcc244.3gppnetwork.org" | |
174 | hostapd.add_ap(apdev[0]['ifname'], params) | |
175 | ||
176 | dev[0].hs20_enable() | |
177 | dev[0].request("SET external_sim 1") | |
2232edf8 | 178 | dev[0].add_cred_values({ 'imsi': "23201-0000000000", 'eap': "SIM" }) |
59f8a3c6 JM |
179 | interworking_select(dev[0], "roaming") |
180 | interworking_ext_sim_connect(dev[0], bssid, "SIM") | |
181 | check_sp_type(dev[0], "roaming") | |
8fba2e5d JM |
182 | |
183 | def test_ap_hs20_username(dev, apdev): | |
184 | """Hotspot 2.0 connection in username/password credential""" | |
8fba2e5d JM |
185 | bssid = apdev[0]['bssid'] |
186 | params = hs20_ap_params() | |
187 | params['hessid'] = bssid | |
188 | hostapd.add_ap(apdev[0]['ifname'], params) | |
189 | ||
190 | dev[0].hs20_enable() | |
2232edf8 JM |
191 | id = dev[0].add_cred_values({ 'realm': "example.com", |
192 | 'username': "hs20-test", | |
193 | 'password': "password", | |
194 | 'domain': "example.com" }) | |
8fba2e5d JM |
195 | interworking_select(dev[0], bssid, "home") |
196 | interworking_connect(dev[0], bssid, "TTLS") | |
197 | check_sp_type(dev[0], "home") | |
198 | ||
199 | def test_ap_hs20_username_roaming(dev, apdev): | |
200 | """Hotspot 2.0 connection in username/password credential (roaming)""" | |
8fba2e5d JM |
201 | bssid = apdev[0]['bssid'] |
202 | params = hs20_ap_params() | |
203 | params['nai_realm'] = [ "0,example.com,13[5:6],21[2:4][5:7]", | |
204 | "0,roaming.example.com,21[2:4][5:7]", | |
205 | "0,another.example.com" ] | |
206 | params['domain_name'] = "another.example.com" | |
207 | params['hessid'] = bssid | |
208 | hostapd.add_ap(apdev[0]['ifname'], params) | |
209 | ||
210 | dev[0].hs20_enable() | |
2232edf8 JM |
211 | id = dev[0].add_cred_values({ 'realm': "roaming.example.com", |
212 | 'username': "hs20-test", | |
213 | 'password': "password", | |
214 | 'domain': "example.com" }) | |
8fba2e5d JM |
215 | interworking_select(dev[0], bssid, "roaming") |
216 | interworking_connect(dev[0], bssid, "TTLS") | |
217 | check_sp_type(dev[0], "roaming") | |
218 | ||
219 | def test_ap_hs20_username_unknown(dev, apdev): | |
220 | """Hotspot 2.0 connection in username/password credential (no domain in cred)""" | |
8fba2e5d JM |
221 | bssid = apdev[0]['bssid'] |
222 | params = hs20_ap_params() | |
223 | params['hessid'] = bssid | |
224 | hostapd.add_ap(apdev[0]['ifname'], params) | |
225 | ||
226 | dev[0].hs20_enable() | |
2232edf8 JM |
227 | id = dev[0].add_cred_values({ 'realm': "example.com", |
228 | 'username': "hs20-test", | |
229 | 'password': "password" }) | |
8fba2e5d JM |
230 | interworking_select(dev[0], bssid, "unknown") |
231 | interworking_connect(dev[0], bssid, "TTLS") | |
232 | check_sp_type(dev[0], "unknown") | |
233 | ||
234 | def test_ap_hs20_username_unknown2(dev, apdev): | |
235 | """Hotspot 2.0 connection in username/password credential (no domain advertized)""" | |
8fba2e5d JM |
236 | bssid = apdev[0]['bssid'] |
237 | params = hs20_ap_params() | |
238 | params['hessid'] = bssid | |
239 | del params['domain_name'] | |
240 | hostapd.add_ap(apdev[0]['ifname'], params) | |
241 | ||
242 | dev[0].hs20_enable() | |
2232edf8 JM |
243 | id = dev[0].add_cred_values({ 'realm': "example.com", |
244 | 'username': "hs20-test", | |
245 | 'password': "password", | |
246 | 'domain': "example.com" }) | |
8fba2e5d JM |
247 | interworking_select(dev[0], bssid, "unknown") |
248 | interworking_connect(dev[0], bssid, "TTLS") | |
249 | check_sp_type(dev[0], "unknown") | |
d1ba402f | 250 | |
6a0b4002 JM |
251 | def test_ap_hs20_multiple_connects(dev, apdev): |
252 | """Hotspot 2.0 connection through multiple network selections""" | |
253 | bssid = apdev[0]['bssid'] | |
254 | params = hs20_ap_params() | |
255 | params['hessid'] = bssid | |
256 | hostapd.add_ap(apdev[0]['ifname'], params) | |
257 | ||
258 | dev[0].hs20_enable() | |
259 | values = { 'realm': "example.com", | |
260 | 'username': "hs20-test", | |
261 | 'password': "password", | |
262 | 'domain': "example.com" } | |
263 | id = dev[0].add_cred_values(values) | |
264 | ||
265 | for i in range(0, 3): | |
266 | logger.info("Starting Interworking network selection") | |
267 | dev[0].request("INTERWORKING_SELECT auto") | |
268 | while True: | |
269 | ev = dev[0].wait_event(["INTERWORKING-NO-MATCH", | |
270 | "INTERWORKING-ALREADY-CONNECTED", | |
271 | "CTRL-EVENT-CONNECTED"], timeout=15) | |
272 | if ev is None: | |
273 | raise Exception("Connection timed out") | |
274 | if "INTERWORKING-NO-MATCH" in ev: | |
275 | raise Exception("Matching AP not found") | |
276 | if "CTRL-EVENT-CONNECTED" in ev: | |
277 | break | |
278 | if i == 2 and "INTERWORKING-ALREADY-CONNECTED" in ev: | |
279 | break | |
280 | if i == 0: | |
281 | dev[0].request("DISCONNECT") | |
282 | dev[0].dump_monitor() | |
283 | ||
284 | networks = dev[0].list_networks() | |
285 | if len(networks) > 1: | |
286 | raise Exception("Duplicated network block detected") | |
287 | ||
d1ba402f JM |
288 | def policy_test(dev, ap, values, only_one=True): |
289 | dev.dump_monitor() | |
290 | logger.info("Verify network selection to AP " + ap['ifname']) | |
291 | bssid = ap['bssid'] | |
292 | dev.hs20_enable() | |
293 | id = dev.add_cred_values(values) | |
294 | dev.request("INTERWORKING_SELECT auto") | |
295 | while True: | |
296 | ev = dev.wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH", | |
297 | "CTRL-EVENT-CONNECTED"], timeout=15) | |
298 | if ev is None: | |
299 | raise Exception("Connection timed out") | |
300 | if "INTERWORKING-NO-MATCH" in ev: | |
301 | raise Exception("Matching AP not found") | |
302 | if only_one and "INTERWORKING-AP" in ev and bssid not in ev: | |
303 | raise Exception("Unexpected AP claimed acceptable") | |
304 | if "CTRL-EVENT-CONNECTED" in ev: | |
305 | if bssid not in ev: | |
306 | raise Exception("Connected to incorrect BSS") | |
307 | break | |
308 | ||
309 | conn_bssid = dev.get_status_field("bssid") | |
310 | if conn_bssid != bssid: | |
311 | raise Exception("bssid information points to incorrect BSS") | |
312 | ||
313 | dev.remove_cred(id) | |
314 | dev.dump_monitor() | |
315 | ||
d355372c JM |
316 | def default_cred(): |
317 | return { 'realm': "example.com", | |
318 | 'username': "hs20-test", | |
319 | 'password': "password" } | |
320 | ||
d1ba402f JM |
321 | def test_ap_hs20_req_roaming_consortium(dev, apdev): |
322 | """Hotspot 2.0 required roaming consortium""" | |
323 | params = hs20_ap_params() | |
324 | hostapd.add_ap(apdev[0]['ifname'], params) | |
325 | ||
326 | params = hs20_ap_params() | |
327 | params['ssid'] = "test-hs20-other" | |
328 | params['roaming_consortium'] = [ "223344" ] | |
329 | hostapd.add_ap(apdev[1]['ifname'], params) | |
330 | ||
d355372c JM |
331 | values = default_cred() |
332 | values['required_roaming_consortium'] = "223344" | |
d1ba402f JM |
333 | policy_test(dev[0], apdev[1], values) |
334 | values['required_roaming_consortium'] = "112233" | |
335 | policy_test(dev[0], apdev[0], values) | |
d355372c JM |
336 | |
337 | def test_ap_hs20_excluded_ssid(dev, apdev): | |
338 | """Hotspot 2.0 exclusion based on SSID""" | |
339 | params = hs20_ap_params() | |
340 | hostapd.add_ap(apdev[0]['ifname'], params) | |
341 | ||
342 | params = hs20_ap_params() | |
343 | params['ssid'] = "test-hs20-other" | |
344 | params['roaming_consortium'] = [ "223344" ] | |
345 | hostapd.add_ap(apdev[1]['ifname'], params) | |
346 | ||
347 | values = default_cred() | |
348 | values['excluded_ssid'] = "test-hs20" | |
349 | policy_test(dev[0], apdev[1], values) | |
350 | values['excluded_ssid'] = "test-hs20-other" | |
351 | policy_test(dev[0], apdev[0], values) |