1 # P2P helper functions
2 # Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
3 #
4 # This software may be distributed under the terms of the BSD license.
5 # See README for more details.
6 
7 import logging
8 logger = logging.getLogger()
9 import threading
10 import time
11 try:
12     from Queue import Queue
13 except ImportError:
14     from queue import Queue
15 
16 import hwsim_utils
17 
18 MGMT_SUBTYPE_PROBE_REQ = 4
19 MGMT_SUBTYPE_ACTION = 13
20 ACTION_CATEG_PUBLIC = 4
21 
22 P2P_GO_NEG_REQ = 0
23 P2P_GO_NEG_RESP = 1
24 P2P_GO_NEG_CONF = 2
25 P2P_INVITATION_REQ = 3
26 P2P_INVITATION_RESP = 4
27 P2P_DEV_DISC_REQ = 5
28 P2P_DEV_DISC_RESP = 6
29 P2P_PROV_DISC_REQ = 7
30 P2P_PROV_DISC_RESP = 8
31 
32 P2P_ATTR_STATUS = 0
33 P2P_ATTR_MINOR_REASON_CODE = 1
34 P2P_ATTR_CAPABILITY = 2
35 P2P_ATTR_DEVICE_ID = 3
36 P2P_ATTR_GROUP_OWNER_INTENT = 4
37 P2P_ATTR_CONFIGURATION_TIMEOUT = 5
38 P2P_ATTR_LISTEN_CHANNEL = 6
39 P2P_ATTR_GROUP_BSSID = 7
40 P2P_ATTR_EXT_LISTEN_TIMING = 8
41 P2P_ATTR_INTENDED_INTERFACE_ADDR = 9
42 P2P_ATTR_MANAGEABILITY = 10
43 P2P_ATTR_CHANNEL_LIST = 11
44 P2P_ATTR_NOTICE_OF_ABSENCE = 12
45 P2P_ATTR_DEVICE_INFO = 13
46 P2P_ATTR_GROUP_INFO = 14
47 P2P_ATTR_GROUP_ID = 15
48 P2P_ATTR_INTERFACE = 16
49 P2P_ATTR_OPERATING_CHANNEL = 17
50 P2P_ATTR_INVITATION_FLAGS = 18
51 P2P_ATTR_OOB_GO_NEG_CHANNEL = 19
52 P2P_ATTR_SERVICE_HASH = 21
53 P2P_ATTR_SESSION_INFORMATION_DATA = 22
54 P2P_ATTR_CONNECTION_CAPABILITY = 23
55 P2P_ATTR_ADVERTISEMENT_ID = 24
56 P2P_ATTR_ADVERTISED_SERVICE = 25
57 P2P_ATTR_SESSION_ID = 26
58 P2P_ATTR_FEATURE_CAPABILITY = 27
59 P2P_ATTR_PERSISTENT_GROUP = 28
60 P2P_ATTR_VENDOR_SPECIFIC = 221
61 
62 P2P_SC_SUCCESS = 0
63 P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1
64 P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2
65 P2P_SC_FAIL_LIMIT_REACHED = 3
66 P2P_SC_FAIL_INVALID_PARAMS = 4
67 P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5
68 P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6
69 P2P_SC_FAIL_NO_COMMON_CHANNELS = 7
70 P2P_SC_FAIL_UNKNOWN_GROUP = 8
71 P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9
72 P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10
73 P2P_SC_FAIL_REJECTED_BY_USER = 11
74 
75 WSC_ATTR_CONFIG_METHODS = 0x1008
76 
77 WLAN_EID_SSID = 0
78 WLAN_EID_SUPP_RATES = 1
79 WLAN_EID_VENDOR_SPECIFIC = 221
80 
81 def go_neg_pin_authorized_persistent(i_dev, r_dev, i_intent=None, r_intent=None,
82                                      i_method='enter', r_method='display',
83                                      test_data=True, r_listen=True):
84     if r_listen:
85         r_dev.p2p_listen()
86     i_dev.p2p_listen()
87     pin = r_dev.wps_read_pin()
88     logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
89     r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), pin, r_method,
90                           go_intent=r_intent, persistent=True)
91     if r_listen:
92         r_dev.p2p_listen()
93     i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method,
94                                   timeout=20, go_intent=i_intent,
95                                   persistent=True)
96     r_res = r_dev.p2p_go_neg_auth_result()
97     if i_res and r_res and \
98        i_res['result'] == 'success' and r_res['result'] == 'success':
99         if i_res['role'] == 'GO':
100             i_dev.wait_sta(addr=r_dev.p2p_interface_addr())
101         if r_res['role'] == 'GO':
102             r_dev.wait_sta(addr=i_dev.p2p_interface_addr())
103     logger.debug("i_res: " + str(i_res))
104     logger.debug("r_res: " + str(r_res))
105     r_dev.dump_monitor()
106     i_dev.dump_monitor()
107     logger.info("Group formed")
108     if test_data:
109         hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
110     return [i_res, r_res]
111 
112 def terminate_group(go, cli):
113     logger.info("Terminate persistent group")
114     cli.close_monitor_group()
115     go.remove_group()
116     cli.wait_go_ending_session()
117 
118 def invite(inv, resp, extra=None, persistent_reconnect=True, use_listen=True):
119     addr = resp.p2p_dev_addr()
120     if persistent_reconnect:
121         resp.global_request("SET persistent_reconnect 1")
122     else:
123         resp.global_request("SET persistent_reconnect 0")
124     if use_listen:
125         resp.p2p_listen()
126     else:
127         resp.p2p_find(social=True)
128     if not inv.discover_peer(addr, social=True):
129         raise Exception("Peer " + addr + " not found")
130     inv.dump_monitor()
131     peer = inv.get_peer(addr)
132     cmd = "P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr
133     if extra:
134         cmd = cmd + " " + extra
135     inv.global_request(cmd)
136 
137 def check_result(go, cli):
138     ev = go.wait_global_event(["P2P-GROUP-STARTED",
139                                "Failed to start AP functionality"], timeout=30)
140     if ev is None:
141         raise Exception("Timeout on group re-invocation (on GO)")
142     if "P2P-GROUP-STARTED" not in ev:
143         raise Exception("GO failed to start the group for re-invocation")
144     if "[PERSISTENT]" not in ev:
145         raise Exception("Re-invoked group not marked persistent")
146     go_res = go.group_form_result(ev)
147     if go_res['role'] != 'GO':
148         raise Exception("Persistent group GO did not become GO")
149     if not go_res['persistent']:
150         raise Exception("Persistent group not re-invoked as persistent (GO)")
151     ev = cli.wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
152     if ev is None:
153         raise Exception("Timeout on group re-invocation (on client)")
154     if "[PERSISTENT]" not in ev:
155         raise Exception("Re-invoked group not marked persistent")
156     cli_res = cli.group_form_result(ev)
157     if cli_res['role'] != 'client':
158         raise Exception("Persistent group client did not become client")
159     if not cli_res['persistent']:
160         raise Exception("Persistent group not re-invoked as persistent (cli)")
161     return [go_res, cli_res]
162 
163 def form(go, cli, test_data=True, reverse_init=False, r_listen=True):
164     logger.info("Form a persistent group")
165     if reverse_init:
166         [i_res, r_res] = go_neg_pin_authorized_persistent(i_dev=cli, i_intent=0,
167                                                           r_dev=go, r_intent=15,
168                                                           test_data=test_data,
169                                                           r_listen=r_listen)
170     else:
171         [i_res, r_res] = go_neg_pin_authorized_persistent(i_dev=go, i_intent=15,
172                                                           r_dev=cli, r_intent=0,
173                                                           test_data=test_data,
174                                                           r_listen=r_listen)
175     if not i_res['persistent'] or not r_res['persistent']:
176         raise Exception("Formed group was not persistent")
177     terminate_group(go, cli)
178     if reverse_init:
179         return r_res
180     else:
181         return i_res
182 
183 def invite_from_cli(go, cli, terminate=True):
184     logger.info("Re-invoke persistent group from client")
185     invite(cli, go)
186     [go_res, cli_res] = check_result(go, cli)
187     hwsim_utils.test_connectivity_p2p(go, cli)
188     if terminate:
189         terminate_group(go, cli)
190     return [go_res, cli_res]
191 
192 def invite_from_go(go, cli, terminate=True, extra=None):
193     logger.info("Re-invoke persistent group from GO")
194     invite(go, cli, extra=extra)
195     [go_res, cli_res] = check_result(go, cli)
196     hwsim_utils.test_connectivity_p2p(go, cli)
197     if terminate:
198         terminate_group(go, cli)
199     return [go_res, cli_res]
200 
201 def autogo(go, freq=None, persistent=None):
202     logger.info("Start autonomous GO " + go.ifname)
203     res = go.p2p_start_go(freq=freq, persistent=persistent)
204     logger.debug("res: " + str(res))
205     return res
206 
207 def connect_cli(go, client, social=False, freq=None):
208     logger.info("Try to connect the client to the GO")
209     pin = client.wps_read_pin()
210     go.p2p_go_authorize_client(pin)
211     res = client.p2p_connect_group(go.p2p_dev_addr(), pin, timeout=60,
212                                    social=social, freq=freq)
213     logger.info("Client connected")
214     go.wait_sta(client.p2p_interface_addr())
215     hwsim_utils.test_connectivity_p2p(go, client)
216     return res
217 
218 def check_grpform_results(i_res, r_res):
219     if i_res['result'] != 'success' or r_res['result'] != 'success':
220         raise Exception("Failed group formation")
221     if i_res['ssid'] != r_res['ssid']:
222         raise Exception("SSID mismatch")
223     if i_res['freq'] != r_res['freq']:
224         raise Exception("freq mismatch")
225     if 'go_neg_freq' in r_res and i_res['go_neg_freq'] != r_res['go_neg_freq']:
226         raise Exception("go_neg_freq mismatch")
227     if i_res['freq'] != i_res['go_neg_freq']:
228         raise Exception("freq/go_neg_freq mismatch")
229     if i_res['role'] != i_res['go_neg_role']:
230         raise Exception("role/go_neg_role mismatch")
231     if 'go_neg_role' in r_res and r_res['role'] != r_res['go_neg_role']:
232         raise Exception("role/go_neg_role mismatch")
233     if i_res['go_dev_addr'] != r_res['go_dev_addr']:
234         raise Exception("GO Device Address mismatch")
235 
236 def go_neg_init(i_dev, r_dev, pin, i_method, i_intent, res):
237     logger.debug("Initiate GO Negotiation from i_dev")
238     try:
239         i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method, timeout=20, go_intent=i_intent)
240         logger.debug("i_res: " + str(i_res))
241     except Exception as e:
242         i_res = None
243         logger.info("go_neg_init thread caught an exception from p2p_go_neg_init: " + str(e))
244     res.put(i_res)
245 
246 def go_neg_pin(i_dev, r_dev, i_intent=None, r_intent=None, i_method='enter', r_method='display'):
247     r_dev.p2p_listen()
248     i_dev.p2p_listen()
249     pin = r_dev.wps_read_pin()
250     logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
251     r_dev.dump_monitor()
252     res = Queue()
253     t = threading.Thread(target=go_neg_init, args=(i_dev, r_dev, pin, i_method, i_intent, res))
254     t.start()
255     logger.debug("Wait for GO Negotiation Request on r_dev")
256     ev = r_dev.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
257     if ev is None:
258         t.join()
259         raise Exception("GO Negotiation timed out")
260     r_dev.dump_monitor()
261     logger.debug("Re-initiate GO Negotiation from r_dev")
262     try:
263         r_res = r_dev.p2p_go_neg_init(i_dev.p2p_dev_addr(), pin, r_method,
264                                       go_intent=r_intent, timeout=20)
265     except Exception as e:
266         logger.info("go_neg_pin - r_dev.p2p_go_neg_init() exception: " + str(e))
267         t.join()
268         raise
269     logger.debug("r_res: " + str(r_res))
270     r_dev.dump_monitor()
271     t.join()
272     i_res = res.get()
273     if i_res is None:
274         raise Exception("go_neg_init thread failed")
275     logger.debug("i_res: " + str(i_res))
276     logger.info("Group formed")
277     hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
278     i_dev.dump_monitor()
279     return [i_res, r_res]
280 
281 def go_neg_pin_authorized(i_dev, r_dev, i_intent=None, r_intent=None,
282                           expect_failure=False, i_go_neg_status=None,
283                           i_method='enter', r_method='display', test_data=True,
284                           i_freq=None, r_freq=None,
285                           i_freq2=None, r_freq2=None,
286                           i_max_oper_chwidth=None, r_max_oper_chwidth=None,
287                           i_ht40=False, i_vht=False, r_ht40=False, r_vht=False):
288     i_dev.p2p_listen()
289     pin = r_dev.wps_read_pin()
290     logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
291     r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), pin, r_method,
292                           go_intent=r_intent, freq=r_freq, freq2=r_freq2,
293                           max_oper_chwidth=r_max_oper_chwidth, ht40=r_ht40,
294                           vht=r_vht)
295     r_dev.p2p_listen()
296     i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method,
297                                   timeout=20, go_intent=i_intent,
298                                   expect_failure=expect_failure, freq=i_freq,
299                                   freq2=i_freq2,
300                                   max_oper_chwidth=i_max_oper_chwidth,
301                                   ht40=i_ht40, vht=i_vht)
302     r_res = r_dev.p2p_go_neg_auth_result(expect_failure=expect_failure)
303     logger.debug("i_res: " + str(i_res))
304     logger.debug("r_res: " + str(r_res))
305     if not expect_failure and i_res and r_res and \
306        i_res['result'] == 'success' and r_res['result'] == 'success':
307         if i_res['role'] == 'GO':
308             i_dev.wait_sta(addr=r_dev.p2p_interface_addr())
309         if r_res['role'] == 'GO':
310             r_dev.wait_sta(addr=i_dev.p2p_interface_addr())
311     r_dev.dump_monitor()
312     i_dev.dump_monitor()
313     if i_go_neg_status:
314         if i_res['result'] != 'go-neg-failed':
315             raise Exception("Expected GO Negotiation failure not reported")
316         if i_res['status'] != i_go_neg_status:
317             raise Exception("Expected GO Negotiation status not seen")
318     if expect_failure:
319         return
320     logger.info("Group formed")
321     if test_data:
322         hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
323     return [i_res, r_res]
324 
325 def go_neg_init_pbc(i_dev, r_dev, i_intent, res, freq, provdisc, timeout=20):
326     logger.debug("Initiate GO Negotiation from i_dev")
327     try:
328         i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), None, "pbc",
329                                       timeout=timeout, go_intent=i_intent,
330                                       freq=freq, provdisc=provdisc)
331         logger.debug("i_res: " + str(i_res))
332     except Exception as e:
333         i_res = None
334         logger.info("go_neg_init_pbc thread caught an exception from p2p_go_neg_init: " + str(e))
335     res.put(i_res)
336 
337 def go_neg_pbc(i_dev, r_dev, i_intent=None, r_intent=None, i_freq=None,
338                r_freq=None, provdisc=False, r_listen=False, timeout=30):
339     if r_listen:
340         r_dev.p2p_listen()
341     else:
342         r_dev.p2p_find(social=True)
343     i_dev.p2p_find(social=True)
344     logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
345     r_dev.dump_monitor()
346     res = Queue()
347     t = threading.Thread(target=go_neg_init_pbc, args=(i_dev, r_dev, i_intent,
348                                                        res, i_freq, provdisc,
349                                                        timeout))
350     t.start()
351     logger.debug("Wait for GO Negotiation Request on r_dev")
352     ev = r_dev.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=timeout - 5)
353     if ev is None:
354         logger.debug("Wait for P2P-GO-NEG-REQUEST timed out on r_dev - wait for i_dev thread to complete")
355         t.join()
356         raise Exception("GO Negotiation timed out")
357     r_dev.dump_monitor()
358     # Allow some time for the GO Neg Resp to go out before initializing new
359     # GO Negotiation.
360     time.sleep(0.2)
361     logger.debug("Re-initiate GO Negotiation from r_dev")
362     try:
363         r_res = r_dev.p2p_go_neg_init(i_dev.p2p_dev_addr(), None, "pbc",
364                                       go_intent=r_intent, timeout=timeout,
365                                       freq=r_freq)
366     except Exception as e:
367         logger.info("go_neg_pbc - r_dev.p2p_go_neg_init() exception: " + str(e))
368         t.join()
369         raise
370     logger.debug("r_res: " + str(r_res))
371     r_dev.dump_monitor()
372     t.join()
373     i_res = res.get()
374     if i_res is None:
375         raise Exception("go_neg_init_pbc thread failed")
376     logger.debug("i_res: " + str(i_res))
377     logger.info("Group formed")
378     time.sleep(0.1)
379     hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
380     i_dev.dump_monitor()
381     return [i_res, r_res]
382 
383 def go_neg_pbc_authorized(i_dev, r_dev, i_intent=None, r_intent=None,
384                           expect_failure=False, i_freq=None, r_freq=None):
385     i_dev.p2p_listen()
386     logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
387     r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), None, "pbc",
388                           go_intent=r_intent, freq=r_freq)
389     r_dev.p2p_listen()
390     i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), None, "pbc", timeout=20,
391                                   go_intent=i_intent,
392                                   expect_failure=expect_failure, freq=i_freq)
393     r_res = r_dev.p2p_go_neg_auth_result(expect_failure=expect_failure)
394     logger.debug("i_res: " + str(i_res))
395     logger.debug("r_res: " + str(r_res))
396     r_dev.dump_monitor()
397     i_dev.dump_monitor()
398     if expect_failure:
399         return
400     logger.info("Group formed")
401     return [i_res, r_res]
402 
403 def remove_group(dev1, dev2, allow_failure=False):
404     try:
405         dev1.remove_group()
406     except:
407         if not allow_failure:
408             raise
409     try:
410         dev2.remove_group()
411     except:
412         pass
413