6cd59688b8
Some of the error paths in go_neg_pbc() and go_neg_pin() did not wait for the helper thread to complete processing. This could result in unexpected behavior when the test case could have exited while the thread was still performing tasks for the GO Negotiation. This could result in getting stuck in one of the following test cases with "go_neg_init_pbc thread caught an exception from p2p_go_neg_init: Group formation timed out" showing up in the log. This was hit, e.g., with the following test sequence: no_go_freq p2p_channel_drv_pref_autogo Signed-off-by: Jouni Malinen <j@w1.fi>
394 lines
15 KiB
Python
394 lines
15 KiB
Python
# P2P helper functions
|
|
# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
|
|
#
|
|
# This software may be distributed under the terms of the BSD license.
|
|
# See README for more details.
|
|
|
|
import logging
|
|
logger = logging.getLogger()
|
|
import threading
|
|
import time
|
|
try:
|
|
from Queue import Queue
|
|
except ImportError:
|
|
from queue import Queue
|
|
|
|
import hwsim_utils
|
|
|
|
MGMT_SUBTYPE_PROBE_REQ = 4
|
|
MGMT_SUBTYPE_ACTION = 13
|
|
ACTION_CATEG_PUBLIC = 4
|
|
|
|
P2P_GO_NEG_REQ = 0
|
|
P2P_GO_NEG_RESP = 1
|
|
P2P_GO_NEG_CONF = 2
|
|
P2P_INVITATION_REQ = 3
|
|
P2P_INVITATION_RESP = 4
|
|
P2P_DEV_DISC_REQ = 5
|
|
P2P_DEV_DISC_RESP = 6
|
|
P2P_PROV_DISC_REQ = 7
|
|
P2P_PROV_DISC_RESP = 8
|
|
|
|
P2P_ATTR_STATUS = 0
|
|
P2P_ATTR_MINOR_REASON_CODE = 1
|
|
P2P_ATTR_CAPABILITY = 2
|
|
P2P_ATTR_DEVICE_ID = 3
|
|
P2P_ATTR_GROUP_OWNER_INTENT = 4
|
|
P2P_ATTR_CONFIGURATION_TIMEOUT = 5
|
|
P2P_ATTR_LISTEN_CHANNEL = 6
|
|
P2P_ATTR_GROUP_BSSID = 7
|
|
P2P_ATTR_EXT_LISTEN_TIMING = 8
|
|
P2P_ATTR_INTENDED_INTERFACE_ADDR = 9
|
|
P2P_ATTR_MANAGEABILITY = 10
|
|
P2P_ATTR_CHANNEL_LIST = 11
|
|
P2P_ATTR_NOTICE_OF_ABSENCE = 12
|
|
P2P_ATTR_DEVICE_INFO = 13
|
|
P2P_ATTR_GROUP_INFO = 14
|
|
P2P_ATTR_GROUP_ID = 15
|
|
P2P_ATTR_INTERFACE = 16
|
|
P2P_ATTR_OPERATING_CHANNEL = 17
|
|
P2P_ATTR_INVITATION_FLAGS = 18
|
|
P2P_ATTR_OOB_GO_NEG_CHANNEL = 19
|
|
P2P_ATTR_SERVICE_HASH = 21
|
|
P2P_ATTR_SESSION_INFORMATION_DATA = 22
|
|
P2P_ATTR_CONNECTION_CAPABILITY = 23
|
|
P2P_ATTR_ADVERTISEMENT_ID = 24
|
|
P2P_ATTR_ADVERTISED_SERVICE = 25
|
|
P2P_ATTR_SESSION_ID = 26
|
|
P2P_ATTR_FEATURE_CAPABILITY = 27
|
|
P2P_ATTR_PERSISTENT_GROUP = 28
|
|
P2P_ATTR_VENDOR_SPECIFIC = 221
|
|
|
|
P2P_SC_SUCCESS = 0
|
|
P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1
|
|
P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2
|
|
P2P_SC_FAIL_LIMIT_REACHED = 3
|
|
P2P_SC_FAIL_INVALID_PARAMS = 4
|
|
P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5
|
|
P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6
|
|
P2P_SC_FAIL_NO_COMMON_CHANNELS = 7
|
|
P2P_SC_FAIL_UNKNOWN_GROUP = 8
|
|
P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9
|
|
P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10
|
|
P2P_SC_FAIL_REJECTED_BY_USER = 11
|
|
|
|
WSC_ATTR_CONFIG_METHODS = 0x1008
|
|
|
|
WLAN_EID_SSID = 0
|
|
WLAN_EID_SUPP_RATES = 1
|
|
WLAN_EID_VENDOR_SPECIFIC = 221
|
|
|
|
def go_neg_pin_authorized_persistent(i_dev, r_dev, i_intent=None, r_intent=None,
|
|
i_method='enter', r_method='display',
|
|
test_data=True, r_listen=True):
|
|
if r_listen:
|
|
r_dev.p2p_listen()
|
|
i_dev.p2p_listen()
|
|
pin = r_dev.wps_read_pin()
|
|
logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
|
|
r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), pin, r_method,
|
|
go_intent=r_intent, persistent=True)
|
|
if r_listen:
|
|
r_dev.p2p_listen()
|
|
i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method,
|
|
timeout=20, go_intent=i_intent,
|
|
persistent=True)
|
|
r_res = r_dev.p2p_go_neg_auth_result()
|
|
logger.debug("i_res: " + str(i_res))
|
|
logger.debug("r_res: " + str(r_res))
|
|
r_dev.dump_monitor()
|
|
i_dev.dump_monitor()
|
|
logger.info("Group formed")
|
|
if test_data:
|
|
hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
|
|
return [i_res, r_res]
|
|
|
|
def terminate_group(go, cli):
|
|
logger.info("Terminate persistent group")
|
|
cli.close_monitor_group()
|
|
go.remove_group()
|
|
cli.wait_go_ending_session()
|
|
|
|
def invite(inv, resp, extra=None, persistent_reconnect=True, use_listen=True):
|
|
addr = resp.p2p_dev_addr()
|
|
if persistent_reconnect:
|
|
resp.global_request("SET persistent_reconnect 1")
|
|
else:
|
|
resp.global_request("SET persistent_reconnect 0")
|
|
if use_listen:
|
|
resp.p2p_listen()
|
|
else:
|
|
resp.p2p_find(social=True)
|
|
if not inv.discover_peer(addr, social=True):
|
|
raise Exception("Peer " + addr + " not found")
|
|
inv.dump_monitor()
|
|
peer = inv.get_peer(addr)
|
|
cmd = "P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr
|
|
if extra:
|
|
cmd = cmd + " " + extra
|
|
inv.global_request(cmd)
|
|
|
|
def check_result(go, cli):
|
|
ev = go.wait_global_event(["P2P-GROUP-STARTED",
|
|
"Failed to start AP functionality"], timeout=30)
|
|
if ev is None:
|
|
raise Exception("Timeout on group re-invocation (on GO)")
|
|
if "P2P-GROUP-STARTED" not in ev:
|
|
raise Exception("GO failed to start the group for re-invocation")
|
|
if "[PERSISTENT]" not in ev:
|
|
raise Exception("Re-invoked group not marked persistent")
|
|
go_res = go.group_form_result(ev)
|
|
if go_res['role'] != 'GO':
|
|
raise Exception("Persistent group GO did not become GO")
|
|
if not go_res['persistent']:
|
|
raise Exception("Persistent group not re-invoked as persistent (GO)")
|
|
ev = cli.wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
|
|
if ev is None:
|
|
raise Exception("Timeout on group re-invocation (on client)")
|
|
if "[PERSISTENT]" not in ev:
|
|
raise Exception("Re-invoked group not marked persistent")
|
|
cli_res = cli.group_form_result(ev)
|
|
if cli_res['role'] != 'client':
|
|
raise Exception("Persistent group client did not become client")
|
|
if not cli_res['persistent']:
|
|
raise Exception("Persistent group not re-invoked as persistent (cli)")
|
|
return [go_res, cli_res]
|
|
|
|
def form(go, cli, test_data=True, reverse_init=False, r_listen=True):
|
|
logger.info("Form a persistent group")
|
|
if reverse_init:
|
|
[i_res, r_res] = go_neg_pin_authorized_persistent(i_dev=cli, i_intent=0,
|
|
r_dev=go, r_intent=15,
|
|
test_data=test_data,
|
|
r_listen=r_listen)
|
|
else:
|
|
[i_res, r_res] = go_neg_pin_authorized_persistent(i_dev=go, i_intent=15,
|
|
r_dev=cli, r_intent=0,
|
|
test_data=test_data,
|
|
r_listen=r_listen)
|
|
if not i_res['persistent'] or not r_res['persistent']:
|
|
raise Exception("Formed group was not persistent")
|
|
terminate_group(go, cli)
|
|
if reverse_init:
|
|
return r_res
|
|
else:
|
|
return i_res
|
|
|
|
def invite_from_cli(go, cli, terminate=True):
|
|
logger.info("Re-invoke persistent group from client")
|
|
invite(cli, go)
|
|
[go_res, cli_res] = check_result(go, cli)
|
|
hwsim_utils.test_connectivity_p2p(go, cli)
|
|
if terminate:
|
|
terminate_group(go, cli)
|
|
return [go_res, cli_res]
|
|
|
|
def invite_from_go(go, cli, terminate=True, extra=None):
|
|
logger.info("Re-invoke persistent group from GO")
|
|
invite(go, cli, extra=extra)
|
|
[go_res, cli_res] = check_result(go, cli)
|
|
hwsim_utils.test_connectivity_p2p(go, cli)
|
|
if terminate:
|
|
terminate_group(go, cli)
|
|
return [go_res, cli_res]
|
|
|
|
def autogo(go, freq=None, persistent=None):
|
|
logger.info("Start autonomous GO " + go.ifname)
|
|
res = go.p2p_start_go(freq=freq, persistent=persistent)
|
|
logger.debug("res: " + str(res))
|
|
return res
|
|
|
|
def connect_cli(go, client, social=False, freq=None):
|
|
logger.info("Try to connect the client to the GO")
|
|
pin = client.wps_read_pin()
|
|
go.p2p_go_authorize_client(pin)
|
|
res = client.p2p_connect_group(go.p2p_dev_addr(), pin, timeout=60,
|
|
social=social, freq=freq)
|
|
logger.info("Client connected")
|
|
hwsim_utils.test_connectivity_p2p(go, client)
|
|
return res
|
|
|
|
def check_grpform_results(i_res, r_res):
|
|
if i_res['result'] != 'success' or r_res['result'] != 'success':
|
|
raise Exception("Failed group formation")
|
|
if i_res['ssid'] != r_res['ssid']:
|
|
raise Exception("SSID mismatch")
|
|
if i_res['freq'] != r_res['freq']:
|
|
raise Exception("freq mismatch")
|
|
if 'go_neg_freq' in r_res and i_res['go_neg_freq'] != r_res['go_neg_freq']:
|
|
raise Exception("go_neg_freq mismatch")
|
|
if i_res['freq'] != i_res['go_neg_freq']:
|
|
raise Exception("freq/go_neg_freq mismatch")
|
|
if i_res['role'] != i_res['go_neg_role']:
|
|
raise Exception("role/go_neg_role mismatch")
|
|
if 'go_neg_role' in r_res and r_res['role'] != r_res['go_neg_role']:
|
|
raise Exception("role/go_neg_role mismatch")
|
|
if i_res['go_dev_addr'] != r_res['go_dev_addr']:
|
|
raise Exception("GO Device Address mismatch")
|
|
|
|
def go_neg_init(i_dev, r_dev, pin, i_method, i_intent, res):
|
|
logger.debug("Initiate GO Negotiation from i_dev")
|
|
try:
|
|
i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method, timeout=20, go_intent=i_intent)
|
|
logger.debug("i_res: " + str(i_res))
|
|
except Exception as e:
|
|
i_res = None
|
|
logger.info("go_neg_init thread caught an exception from p2p_go_neg_init: " + str(e))
|
|
res.put(i_res)
|
|
|
|
def go_neg_pin(i_dev, r_dev, i_intent=None, r_intent=None, i_method='enter', r_method='display'):
|
|
r_dev.p2p_listen()
|
|
i_dev.p2p_listen()
|
|
pin = r_dev.wps_read_pin()
|
|
logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
|
|
r_dev.dump_monitor()
|
|
res = Queue()
|
|
t = threading.Thread(target=go_neg_init, args=(i_dev, r_dev, pin, i_method, i_intent, res))
|
|
t.start()
|
|
logger.debug("Wait for GO Negotiation Request on r_dev")
|
|
ev = r_dev.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
|
|
if ev is None:
|
|
t.join()
|
|
raise Exception("GO Negotiation timed out")
|
|
r_dev.dump_monitor()
|
|
logger.debug("Re-initiate GO Negotiation from r_dev")
|
|
try:
|
|
r_res = r_dev.p2p_go_neg_init(i_dev.p2p_dev_addr(), pin, r_method,
|
|
go_intent=r_intent, timeout=20)
|
|
except Exception as e:
|
|
logger.info("go_neg_pin - r_dev.p2p_go_neg_init() exception: " + str(e))
|
|
t.join()
|
|
raise
|
|
logger.debug("r_res: " + str(r_res))
|
|
r_dev.dump_monitor()
|
|
t.join()
|
|
i_res = res.get()
|
|
if i_res is None:
|
|
raise Exception("go_neg_init thread failed")
|
|
logger.debug("i_res: " + str(i_res))
|
|
logger.info("Group formed")
|
|
hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
|
|
i_dev.dump_monitor()
|
|
return [i_res, r_res]
|
|
|
|
def go_neg_pin_authorized(i_dev, r_dev, i_intent=None, r_intent=None,
|
|
expect_failure=False, i_go_neg_status=None,
|
|
i_method='enter', r_method='display', test_data=True,
|
|
i_freq=None, r_freq=None,
|
|
i_freq2=None, r_freq2=None,
|
|
i_max_oper_chwidth=None, r_max_oper_chwidth=None,
|
|
i_ht40=False, i_vht=False, r_ht40=False, r_vht=False):
|
|
i_dev.p2p_listen()
|
|
pin = r_dev.wps_read_pin()
|
|
logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
|
|
r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), pin, r_method,
|
|
go_intent=r_intent, freq=r_freq, freq2=r_freq2,
|
|
max_oper_chwidth=r_max_oper_chwidth, ht40=r_ht40,
|
|
vht=r_vht)
|
|
r_dev.p2p_listen()
|
|
i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method,
|
|
timeout=20, go_intent=i_intent,
|
|
expect_failure=expect_failure, freq=i_freq,
|
|
freq2=i_freq2,
|
|
max_oper_chwidth=i_max_oper_chwidth,
|
|
ht40=i_ht40, vht=i_vht)
|
|
r_res = r_dev.p2p_go_neg_auth_result(expect_failure=expect_failure)
|
|
logger.debug("i_res: " + str(i_res))
|
|
logger.debug("r_res: " + str(r_res))
|
|
r_dev.dump_monitor()
|
|
i_dev.dump_monitor()
|
|
if i_go_neg_status:
|
|
if i_res['result'] != 'go-neg-failed':
|
|
raise Exception("Expected GO Negotiation failure not reported")
|
|
if i_res['status'] != i_go_neg_status:
|
|
raise Exception("Expected GO Negotiation status not seen")
|
|
if expect_failure:
|
|
return
|
|
logger.info("Group formed")
|
|
if test_data:
|
|
hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
|
|
return [i_res, r_res]
|
|
|
|
def go_neg_init_pbc(i_dev, r_dev, i_intent, res, freq, provdisc):
|
|
logger.debug("Initiate GO Negotiation from i_dev")
|
|
try:
|
|
i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), None, "pbc",
|
|
timeout=20, go_intent=i_intent, freq=freq,
|
|
provdisc=provdisc)
|
|
logger.debug("i_res: " + str(i_res))
|
|
except Exception as e:
|
|
i_res = None
|
|
logger.info("go_neg_init_pbc thread caught an exception from p2p_go_neg_init: " + str(e))
|
|
res.put(i_res)
|
|
|
|
def go_neg_pbc(i_dev, r_dev, i_intent=None, r_intent=None, i_freq=None, r_freq=None, provdisc=False, r_listen=False):
|
|
if r_listen:
|
|
r_dev.p2p_listen()
|
|
else:
|
|
r_dev.p2p_find(social=True)
|
|
i_dev.p2p_find(social=True)
|
|
logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
|
|
r_dev.dump_monitor()
|
|
res = Queue()
|
|
t = threading.Thread(target=go_neg_init_pbc, args=(i_dev, r_dev, i_intent, res, i_freq, provdisc))
|
|
t.start()
|
|
logger.debug("Wait for GO Negotiation Request on r_dev")
|
|
ev = r_dev.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
|
|
if ev is None:
|
|
t.join()
|
|
raise Exception("GO Negotiation timed out")
|
|
r_dev.dump_monitor()
|
|
# Allow some time for the GO Neg Resp to go out before initializing new
|
|
# GO Negotiation.
|
|
time.sleep(0.2)
|
|
logger.debug("Re-initiate GO Negotiation from r_dev")
|
|
try:
|
|
r_res = r_dev.p2p_go_neg_init(i_dev.p2p_dev_addr(), None, "pbc",
|
|
go_intent=r_intent, timeout=20,
|
|
freq=r_freq)
|
|
except Exception as e:
|
|
logger.info("go_neg_pbc - r_dev.p2p_go_neg_init() exception: " + str(e))
|
|
t.join()
|
|
raise
|
|
logger.debug("r_res: " + str(r_res))
|
|
r_dev.dump_monitor()
|
|
t.join()
|
|
i_res = res.get()
|
|
if i_res is None:
|
|
raise Exception("go_neg_init_pbc thread failed")
|
|
logger.debug("i_res: " + str(i_res))
|
|
logger.info("Group formed")
|
|
hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
|
|
i_dev.dump_monitor()
|
|
return [i_res, r_res]
|
|
|
|
def go_neg_pbc_authorized(i_dev, r_dev, i_intent=None, r_intent=None,
|
|
expect_failure=False, i_freq=None, r_freq=None):
|
|
i_dev.p2p_listen()
|
|
logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
|
|
r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), None, "pbc",
|
|
go_intent=r_intent, freq=r_freq)
|
|
r_dev.p2p_listen()
|
|
i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), None, "pbc", timeout=20,
|
|
go_intent=i_intent,
|
|
expect_failure=expect_failure, freq=i_freq)
|
|
r_res = r_dev.p2p_go_neg_auth_result(expect_failure=expect_failure)
|
|
logger.debug("i_res: " + str(i_res))
|
|
logger.debug("r_res: " + str(r_res))
|
|
r_dev.dump_monitor()
|
|
i_dev.dump_monitor()
|
|
if expect_failure:
|
|
return
|
|
logger.info("Group formed")
|
|
return [i_res, r_res]
|
|
|
|
def remove_group(dev1, dev2, allow_failure=False):
|
|
try:
|
|
dev1.remove_group()
|
|
except:
|
|
if not allow_failure:
|
|
raise
|
|
try:
|
|
dev2.remove_group()
|
|
except:
|
|
pass
|