* [PATCH 1/5] test-runner: Reserve radios for wpa_supplicant
@ 2020-10-09 18:08 Andrew Zaborowski
2020-10-09 18:08 ` [PATCH 2/5] autotests: Basic P2P python API Andrew Zaborowski
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Andrew Zaborowski @ 2020-10-09 18:08 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 3076 bytes --]
Add support for a WPA_SUPPLICANT section in hw.conf where
'radN=<config_path>' lines will only reserve radios and create
interfaces for the autotest to be able to start wpa_supplicant on them,
i.e. this prevents iwd or hostapd from being started on them but doesn't
start a wpa_supplicant instance by itself.
---
tools/test-runner | 24 +++++++++++++++++-------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/tools/test-runner b/tools/test-runner
index 3b9c1854..dfad98f6 100755
--- a/tools/test-runner
+++ b/tools/test-runner
@@ -248,10 +248,11 @@ class Process:
raise Exception("Timed out waiting for socket")
class Interface:
- def __init__(self, name, config):
+ def __init__(self, name, config, radio):
self.name = name
self.ctrl_interface = '/var/run/hostapd/' + name
self.config = config
+ self.radio = radio
def __del__(self):
Process(['iw', 'dev', self.name, 'del'], True)
@@ -270,17 +271,15 @@ class Radio:
print("Removing radio %s" % self.name)
self.interface = None
- def create_interface(self, hapd):
+ def create_interface(self, config, use):
global intf_id
ifname = 'wln%s' % intf_id
intf_id += 1
- self.interface = Interface(ifname, hapd.config)
- # IWD does not use interfaces in test-runner so any created
- # interface is assumed to be used by hostapd.
- self.use = 'hostapd'
+ self.interface = Interface(ifname, config, self)
+ self.use = use
Process(['iw', 'phy', self.name, 'interface', 'add', ifname,
'type', 'managed'], True)
@@ -358,7 +357,7 @@ class HostapdInstance:
self.radio = radio
self.config = config
- self.intf = radio.create_interface(self)
+ self.intf = radio.create_interface(self.config, 'hostapd')
self.intf.set_interface_state('up')
def __del__(self):
@@ -473,6 +472,7 @@ class TestContext:
self.args = args
self.hw_config = None
self.hostapd = None
+ self.wpas_interfaces = None
self.cur_radio_id = 0
self.cur_iface_id = 0
self.radios = []
@@ -623,6 +623,14 @@ class TestContext:
self.hostapd = Hostapd(self, hapd_radios, hapd_configs, radius_config)
+ def start_wpas_interfaces(self):
+ if 'WPA_SUPPLICANT' not in self.hw_config:
+ return
+
+ settings = self.hw_config['WPA_SUPPLICANT']
+ wpas_radios = [rad for rad in self.radios if rad.name in settings]
+ self.wpas_interfaces = [rad.create_interface(settings[rad.name], 'wpas') for rad in wpas_radios]
+
def start_ofono(self):
sim_keys = self.hw_config['SETUP'].get('sim_keys', None)
if not sim_keys:
@@ -690,6 +698,7 @@ class TestContext:
def stop_test_processes(self):
self.radios = []
self.hostapd = None
+ self.wpas_interfaces = None
self.iwd_extra_options = None
for p in [p for p in self.processes if p.multi_test is False]:
@@ -884,6 +893,7 @@ def pre_test(ctx, test):
ctx.start_dbus_monitor()
ctx.start_radios()
ctx.start_hostapd()
+ ctx.start_wpas_interfaces()
ctx.start_ofono()
if ctx.args.log:
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/5] autotests: Basic P2P python API
2020-10-09 18:08 [PATCH 1/5] test-runner: Reserve radios for wpa_supplicant Andrew Zaborowski
@ 2020-10-09 18:08 ` Andrew Zaborowski
2020-10-09 18:08 ` [PATCH 3/5] autotests: Add basic wpa_supplicant P2P python wrapper Andrew Zaborowski
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Andrew Zaborowski @ 2020-10-09 18:08 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 6512 bytes --]
Add a basic wrapper for the P2P-related DBus interfaces similar to the
existing classes in iwd.py. WFD is not included.
---
autotests/util/iwd.py | 121 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 118 insertions(+), 3 deletions(-)
diff --git a/autotests/util/iwd.py b/autotests/util/iwd.py
index ef843a95..7e3676d9 100755
--- a/autotests/util/iwd.py
+++ b/autotests/util/iwd.py
@@ -35,6 +35,10 @@ IWD_SIGNAL_AGENT_INTERFACE = 'net.connman.iwd.SignalLevelAgent'
IWD_AP_INTERFACE = 'net.connman.iwd.AccessPoint'
IWD_ADHOC_INTERFACE = 'net.connman.iwd.AdHoc'
IWD_STATION_INTERFACE = 'net.connman.iwd.Station'
+IWD_P2P_INTERFACE = 'net.connman.iwd.p2p.Device'
+IWD_P2P_PEER_INTERFACE = 'net.connman.iwd.p2p.Peer'
+IWD_P2P_SERVICE_MANAGER_INTERFACE = 'net.connman.iwd.p2p.ServiceManager'
+IWD_P2P_WFD_INTERFACE = 'net.connman.iwd.p2p.Display'
IWD_AGENT_MANAGER_PATH = '/net/connman/iwd'
IWD_TOP_LEVEL_PATH = '/'
@@ -767,9 +771,107 @@ class PSKAgent(dbus.service.Object):
return passwd
+class P2PDevice(IWDDBusAbstract):
+ _iface_name = IWD_P2P_INTERFACE
+
+ def __init__(self, *args, **kwargs):
+ self._discovery_request = False
+ self._peer_dict = {}
+ IWDDBusAbstract.__init__(self, *args, **kwargs)
+
+ @property
+ def name(self):
+ return str(self._properties['Name'])
+
+ @name.setter
+ def name(self, name):
+ self._prop_proxy.Set(self._iface_name, 'Name', name)
+
+ @property
+ def enabled(self):
+ return bool(self._properties['Enabled'])
+
+ @enabled.setter
+ def enabled(self, enabled):
+ self._prop_proxy.Set(self._iface_name, 'Enabled', enabled)
+
+ @property
+ def discovery_request(self):
+ return self._discovery_request
+
+ @discovery_request.setter
+ def discovery_request(self, req):
+ if self._discovery_request == bool(req):
+ return
+
+ if bool(req):
+ self._iface.RequestDiscovery()
+ else:
+ self._iface.ReleaseDiscovery()
+
+ self._discovery_request = bool(req)
+
+ def get_peers(self):
+ old_dict = self._peer_dict
+ self._peer_dict = {}
+
+ for path, rssi in self._iface.GetPeers():
+ self._peer_dict[path] = old_dict[path] if path in old_dict else P2PPeer(path)
+ self._peer_dict[path].rssi = rssi
+
+ return self._peer_dict
+
+
+class P2PPeer(IWDDBusAbstract):
+ _iface_name = IWD_P2P_PEER_INTERFACE
+
+ @property
+ def name(self):
+ return str(self._properties['Name'])
+
+ @property
+ def category(self):
+ return str(self._properties['DeviceCategory'])
+
+ @property
+ def subcategory(self):
+ return str(self._properties['DeviceSubcategory'])
+
+ @property
+ def connected(self):
+ return bool(self._properties['Connected'])
+
+ @property
+ def connected_interface(self):
+ return str(self._properties['ConnectedInterface'])
+
+ @property
+ def connected_ip(self):
+ return str(self._properties['ConnectedIP'])
+
+ def connect(self, wait=True, pin=None):
+ if pin is None:
+ self._iface.PushButton(dbus_interface=IWD_WSC_INTERFACE,
+ reply_handler=self._success,
+ error_handler=self._failure)
+ else:
+ self._iface.StartPin(pin,
+ dbus_interface=IWD_WSC_INTERFACE,
+ reply_handler=self._success,
+ error_handler=self._failure)
+
+ if wait:
+ self._wait_for_async_op()
+ return (self.connected_interface, self.connected_ip)
+
+ def disconnect(self):
+ self._iface.Disconnect()
+
+
class DeviceList(collections.Mapping):
def __init__(self, iwd):
self._dict = {}
+ self._p2p_dict = {}
iwd._object_manager.connect_to_signal("InterfacesAdded",
self._interfaces_added_handler)
@@ -782,6 +884,8 @@ class DeviceList(collections.Mapping):
for interface in objects[path]:
if interface == IWD_DEVICE_INTERFACE:
self._dict[path] = Device(path, objects[path][interface])
+ elif interface == IWD_P2P_INTERFACE:
+ self._p2p_dict[path] = P2PDevice(path, objects[path][interface])
def __getitem__(self, key):
return self._dict.__getitem__(key)
@@ -798,10 +902,18 @@ class DeviceList(collections.Mapping):
def _interfaces_added_handler(self, path, interfaces):
if IWD_DEVICE_INTERFACE in interfaces:
self._dict[path] = Device(path, interfaces[IWD_DEVICE_INTERFACE])
+ elif IWD_P2P_INTERFACE in interfaces:
+ self._p2p_dict[path] = P2PDevice(path, interfaces[IWD_P2P_INTERFACE])
def _interfaces_removed_handler(self, path, interfaces):
if IWD_DEVICE_INTERFACE in interfaces:
del self._dict[path]
+ elif IWD_P2P_INTERFACE in interfaces:
+ del self._p2p_dict[path]
+
+ @property
+ def p2p_dict(self):
+ return self._p2p_dict
class IWD(AsyncOpAbstract):
@@ -943,9 +1055,9 @@ class IWD(AsyncOpAbstract):
def remove_from_storage(file_name):
os.system('rm -rf ' + IWD_STORAGE_DIR + '/\'' + file_name + '\'')
- def list_devices(self, wait_to_appear = 0, max_wait = 50):
+ def list_devices(self, wait_to_appear = 0, max_wait = 50, p2p = False):
if not wait_to_appear:
- return list(self._devices.values())
+ return list(self._devices.values() if not p2p else self._devices.p2p_dict.values())
self._wait_timed_out = False
def wait_timeout_cb():
@@ -963,7 +1075,10 @@ class IWD(AsyncOpAbstract):
if not self._wait_timed_out:
GLib.source_remove(timeout)
- return list(self._devices.values())
+ return list(self._devices.values() if not p2p else self._devices.p2p_dict.values())
+
+ def list_p2p_devices(self, *args, **kwargs):
+ return self.list_devices(*args, **kwargs, p2p=True)
def list_known_networks(self):
'''Returns the list of KnownNetwork objects.'''
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/5] autotests: Add basic wpa_supplicant P2P python wrapper
2020-10-09 18:08 [PATCH 1/5] test-runner: Reserve radios for wpa_supplicant Andrew Zaborowski
2020-10-09 18:08 ` [PATCH 2/5] autotests: Basic P2P python API Andrew Zaborowski
@ 2020-10-09 18:08 ` Andrew Zaborowski
2020-10-09 18:08 ` [PATCH 4/5] autotests: Move some variables from IWD class to instance Andrew Zaborowski
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Andrew Zaborowski @ 2020-10-09 18:08 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 9727 bytes --]
Add the new wpas.Wpas class roughly based on hostapd.HostapdCLI but only
adding methods for the P2P-related stuff.
Adding "wpa_supplicant" to -v will enable output from the wpa_supplicant
process to be printed and "wpa_supplicant-dbg" will make it more verbose
("wpa_supplicant" is not needed because it seems to be automatically
enabled because of the glob matching in ctx.is_verbose)
---
autotests/util/wpas.py | 242 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 242 insertions(+)
create mode 100644 autotests/util/wpas.py
diff --git a/autotests/util/wpas.py b/autotests/util/wpas.py
new file mode 100644
index 00000000..ec19b101
--- /dev/null
+++ b/autotests/util/wpas.py
@@ -0,0 +1,242 @@
+#!/usr/bin/python3
+import os
+import socket
+from gi.repository import GLib
+from config import ctx
+
+ctrl_count = 0
+
+class Wpas:
+ def _start_wpas(self, config_name=None, p2p=False):
+ global ctrl_count
+
+ main_interface = None
+ for interface in ctx.wpas_interfaces:
+ if config_name is None or interface.config == config_name:
+ if main_interface is not None:
+ raise Exception('More than was wpa_supplicant interface matches given config')
+ main_interface = interface
+
+ if main_interface is None:
+ raise Exception('No matching wpa_supplicant interface')
+
+ ifname = main_interface.name
+ if p2p:
+ ifname = 'p2p-dev-' + ifname
+
+ self.interface = main_interface
+ self.ifname = ifname
+ self.config_path = '/tmp/' + self.interface.config
+ self.config = self._get_config()
+ self.socket_path = self.config['ctrl_interface']
+ self.io_watch = None
+
+ cmd = ['wpa_supplicant', '-i', self.interface.name, '-c', self.config_path]
+ if ctx.is_verbose('wpa_supplicant-dbg'):
+ cmd += ['-d']
+
+ self.wpa_supplicant = ctx.start_process(cmd)
+
+ self.local_ctrl = '/tmp/wpas_' + str(os.getpid()) + '_' + str(ctrl_count)
+ ctrl_count = ctrl_count + 1
+ self.ctrl_sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ self.ctrl_sock.bind(self.local_ctrl)
+
+ self.remote_ctrl = self.socket_path + '/' + self.ifname
+ self.wpa_supplicant.wait_for_socket(self.remote_ctrl, 2)
+ self.ctrl_sock.connect(self.remote_ctrl)
+ self.io_watch = GLib.io_add_watch(self.ctrl_sock, GLib.IO_IN, self._handle_data_in)
+
+ self.p2p_peers = {}
+ self.p2p_go_neg_requests = {}
+ self.p2p_clients = {}
+ self.p2p_group = None
+
+ self._rx_data = []
+ self._ctrl_request('ATTACH')
+ self.wait_for_event('OK')
+
+ def __init__(self, *args, **kwargs):
+ self._start_wpas(*args, **kwargs)
+
+ def _get_config(self):
+ f = open(self.config_path)
+ lines = f.readlines()
+ f.close()
+ return dict([[v.strip() for v in kv] for kv in [l.split('#', 1)[0].split('=', 1) for l in lines] if len(kv) == 2])
+
+ def wait_for_event(self, event, timeout=10):
+ self._wait_timed_out = False
+
+ def wait_timeout_cb():
+ self._wait_timed_out = True
+ return False
+
+ timeout = GLib.timeout_add_seconds(timeout, wait_timeout_cb)
+ context = ctx.mainloop.get_context()
+
+ while True:
+ context.iteration(may_block=True)
+
+ if event in self._rx_data:
+ GLib.source_remove(timeout)
+ return self._rx_data
+
+ if self._wait_timed_out:
+ raise TimeoutError('waiting for wpas event timed out')
+
+ def _event_parse(self, line):
+ # Unescape event parameter values in '', other escaping rules not implemented
+ key = None
+ value = ''
+ count = 0
+ quoted = False
+ event = {}
+
+ def handle_eow():
+ nonlocal key, value, count, event
+ if count == 0:
+ if key is not None or not value:
+ raise Exception('Bad event name')
+ key = 'event'
+ elif key is None:
+ if not value:
+ return
+ key = 'arg' + str(count)
+ event[key] = value
+ key = None
+ value = ''
+ count += 1
+
+ for ch in line:
+ if ch == '\'':
+ quoted = not quoted
+ elif quoted:
+ value += ch
+ elif ch == '=' and key is None:
+ key = value
+ value = ''
+ elif ch in ' \n':
+ handle_eow()
+ else:
+ value += ch
+ handle_eow()
+ return event
+
+ def _handle_data_in(self, sock, *args):
+ newdata = sock.recv(4096)
+ if len(newdata) == 0:
+ raise Exception('Wpa_s control socket error')
+
+ decoded = newdata.decode('utf-8')
+ if len(decoded) >= 3 and decoded[0] == '<' and decoded[2] == '>':
+ decoded = decoded[3:]
+ while len(decoded) and decoded[-1] == '\n':
+ decoded = decoded[:-1]
+
+ self._rx_data.append(decoded)
+
+ event = self._event_parse(decoded)
+ if event['event'] == 'P2P-DEVICE-FOUND':
+ event.pop('event')
+ event.pop('arg1')
+ self.p2p_peers[event['p2p_dev_addr']] = event
+ elif event['event'] == 'P2P-DEVICE-LOST':
+ del self.p2p_peers[event['p2p_dev_addr']]
+ elif event['event'] == 'P2P-GO-NEG-REQUEST':
+ event.pop('event')
+ event['p2p_dev_addr'] = event.pop('arg1')
+ self.p2p_go_neg_requests[event['p2p_dev_addr']] = event
+ elif event['event'] == 'P2P-GO-NEG-SUCCESS':
+ event.pop('event')
+ addr = event.pop('peer_dev')
+ event['success'] = True
+ event['p2p_dev_addr'] = addr
+
+ if addr in self.p2p_go_neg_requests:
+ self.p2p_go_neg_requests[addr].update(event)
+ else:
+ self.p2p_go_neg_requests[addr] = event
+ elif event['event'] == 'AP-STA-CONNECTED':
+ event.pop('event')
+ addr = event.pop('arg1')
+ self.p2p_clients[addr] = event
+ elif event['event'] == 'AP-STA-DISCONNECTED':
+ addr = event.pop('arg1')
+ del self.p2p_clients[addr]
+ elif event['event'] == 'P2P-GROUP-STARTED':
+ event.pop('event')
+ event['ifname'] = event.pop('arg1')
+ self.p2p_group = event
+ elif event['event'] == 'P2P-GROUP-REMOVED':
+ self.p2p_group = None
+
+ return True
+
+ def _ctrl_request(self, command, timeout=10):
+ if type(command) is str:
+ command = str.encode(command)
+
+ self.ctrl_sock.send(bytes(command))
+
+ # Normal find phase with listen and active scan states
+ def p2p_find(self):
+ self._rx_data = []
+ self._ctrl_request('P2P_SET disc_int 2 3 300')
+ self.wait_for_event('OK')
+ self._rx_data = []
+ self._ctrl_request('P2P_FIND type=social')
+ self.wait_for_event('OK')
+
+ # Like p2p_find but uses only listen states
+ def p2p_listen(self):
+ self._rx_data = []
+ self._ctrl_request('P2P_LISTEN')
+ self.wait_for_event('OK')
+
+ # Stop a p2p_find or p2p_listen
+ def p2p_stop_find_listen(self):
+ self._rx_data = []
+ self._ctrl_request('P2P_STOP_FIND')
+ self.wait_for_event('OK')
+
+ def p2p_connect(self, peer, pin=None, go_intent=None):
+ self._rx_data = []
+ self._ctrl_request('P2P_CONNECT ' + peer['p2p_dev_addr'] + ' ' + ('pbc' if pin is None else pin) +
+ ('' if go_intent is None else 'go_intent=' + str(go_intent)))
+ self.wait_for_event('OK')
+
+ def p2p_accept_go_neg_request(self, request, pin=None, go_intent=None):
+ self._rx_data = []
+ self._ctrl_request('P2P_CONNECT ' + request['p2p_dev_addr'] + ' ' + ('pbc' if pin is None else pin) +
+ ('' if go_intent is None else 'go_intent=' + str(go_intent)))
+ self.wait_for_event('OK')
+
+ # Pre-accept the next GO Negotiation Request from this peer to avoid the extra Respone + Request frames
+ def p2p_authorize(self, peer, pin=None, go_intent=None):
+ self._rx_data = []
+ self._ctrl_request('P2P_CONNECT ' + peer['p2p_dev_addr'] + ' ' + ('pbc' if pin is None else pin) +
+ ('' if go_intent is None else 'go_intent=' + str(go_intent)) + ' auth')
+ self.wait_for_event('OK')
+
+ # Probably needed: remove references to self so that the GC can call __del__ automatically
+ def clean_up(self):
+ if self.io_watch is not None:
+ GLib.source_remove(self.io_watch)
+ self.io_watch = None
+ if self.wpa_supplicant is not None:
+ ctx.stop_process(self.wpa_supplicant)
+ self.wpa_supplicant = None
+
+ def _stop_wpas(self):
+ self.clean_up()
+ if self.ctrl_sock:
+ self.ctrl_sock.close()
+ self.ctrl_sock = None
+ if os.path.exists(self.remote_ctrl):
+ os.remove(self.remote_ctrl)
+ if os.path.exists(self.local_ctrl):
+ os.remove(self.local_ctrl)
+
+ def __del__(self):
+ self._stop_wpas()
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 4/5] autotests: Move some variables from IWD class to instance
2020-10-09 18:08 [PATCH 1/5] test-runner: Reserve radios for wpa_supplicant Andrew Zaborowski
2020-10-09 18:08 ` [PATCH 2/5] autotests: Basic P2P python API Andrew Zaborowski
2020-10-09 18:08 ` [PATCH 3/5] autotests: Add basic wpa_supplicant P2P python wrapper Andrew Zaborowski
@ 2020-10-09 18:08 ` Andrew Zaborowski
2020-10-09 18:08 ` [PATCH 5/5] autotests: Add testP2P Andrew Zaborowski
2020-10-19 22:22 ` [PATCH 1/5] test-runner: Reserve radios for wpa_supplicant Denis Kenzior
4 siblings, 0 replies; 6+ messages in thread
From: Andrew Zaborowski @ 2020-10-09 18:08 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 830 bytes --]
These variables were probably meant as instance members and not class
members.
---
autotests/util/iwd.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/autotests/util/iwd.py b/autotests/util/iwd.py
index 7e3676d9..94a118ce 100755
--- a/autotests/util/iwd.py
+++ b/autotests/util/iwd.py
@@ -231,9 +231,12 @@ class Device(IWDDBusAbstract):
with its properties and methods
'''
_iface_name = IWD_DEVICE_INTERFACE
- _wps_manager_if = None
- _station_if = None
- _station_props = None
+
+ def __init__(self, *args, **kwargs):
+ self._wps_manager_if = None
+ self._station_if = None
+ self._station_props = None
+ IWDDBusAbstract.__init__(self, *args, **kwargs)
@property
def _wps_manager(self):
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 5/5] autotests: Add testP2P
2020-10-09 18:08 [PATCH 1/5] test-runner: Reserve radios for wpa_supplicant Andrew Zaborowski
` (2 preceding siblings ...)
2020-10-09 18:08 ` [PATCH 4/5] autotests: Move some variables from IWD class to instance Andrew Zaborowski
@ 2020-10-09 18:08 ` Andrew Zaborowski
2020-10-19 22:22 ` [PATCH 1/5] test-runner: Reserve radios for wpa_supplicant Denis Kenzior
4 siblings, 0 replies; 6+ messages in thread
From: Andrew Zaborowski @ 2020-10-09 18:08 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 6248 bytes --]
Add two P2P client connection scenarios.
---
autotests/testP2P/client_test.py | 106 +++++++++++++++++++++++++++++++
autotests/testP2P/dhcpd.conf | 4 ++
autotests/testP2P/hw.conf | 6 ++
autotests/testP2P/main.conf | 6 ++
autotests/testP2P/rad1-p2p.conf | 10 +++
5 files changed, 132 insertions(+)
create mode 100644 autotests/testP2P/client_test.py
create mode 100644 autotests/testP2P/dhcpd.conf
create mode 100644 autotests/testP2P/hw.conf
create mode 100644 autotests/testP2P/main.conf
create mode 100644 autotests/testP2P/rad1-p2p.conf
diff --git a/autotests/testP2P/client_test.py b/autotests/testP2P/client_test.py
new file mode 100644
index 00000000..4994de76
--- /dev/null
+++ b/autotests/testP2P/client_test.py
@@ -0,0 +1,106 @@
+#!/usr/bin/python3
+
+import unittest
+import sys
+import netifaces
+import os
+
+import iwd
+from iwd import IWD
+import testutil
+from config import ctx
+from wpas import Wpas
+
+class Test(unittest.TestCase):
+ def test_1_client_go_neg_responder(self):
+ self.p2p_client_test(False)
+
+ def test_2_client_go_neg_initiator(self):
+ self.p2p_client_test(True)
+
+ def p2p_client_test(self, preauthorize):
+ wpas = Wpas(p2p=True)
+ wd = IWD()
+
+ # Not strictly necessary but prevents the station interface from queuing its scans
+ # in the wiphy radio work queue and delaying P2P scans.
+ wd.list_devices(1)[0].disconnect()
+
+ devices = wd.list_p2p_devices(1)
+ p2p = devices[0]
+ p2p.enabled = True
+ p2p.name = 'testdev1'
+
+ wpas.p2p_find()
+ p2p.discovery_request = True
+ wd.wait(5)
+ wd.wait_for_object_condition(wpas, 'len(obj.p2p_peers) == 1', max_wait=20)
+ p2p.discovery_request = False
+ wpas.p2p_listen()
+
+ peers = p2p.get_peers()
+ self.assertEqual(len(peers), 1)
+ peer = next(iter(peers.values()))
+ self.assertEqual(peer.name, wpas.config['device_name'])
+ self.assertEqual(peer.category, 'display')
+ self.assertEqual(peer.subcategory, 'monitor')
+
+ wpas_peer = next(iter(wpas.p2p_peers.values()))
+ self.assertEqual(wpas_peer['name'], p2p.name)
+ self.assertEqual(wpas_peer['pri_dev_type'], '1-0050F204-6') # 1 == Computer, 6 == Desktop
+ self.assertEqual(wpas_peer['config_methods'], '0x1080')
+
+ if preauthorize:
+ wpas.p2p_authorize(wpas_peer)
+
+ peer.connect(wait=False)
+
+ self.assertEqual(len(wpas.p2p_go_neg_requests), 0)
+ self.assertEqual(len(wpas.p2p_clients), 0)
+ wd.wait_for_object_condition(wpas, 'len(obj.p2p_go_neg_requests) == 1', max_wait=3)
+ request = wpas.p2p_go_neg_requests[wpas_peer['p2p_dev_addr']]
+
+ if not preauthorize:
+ self.assertEqual(request['dev_passwd_id'], '4')
+ self.assertEqual(request['go_intent'], '2') # Hardcoded in src/p2p.c
+
+ wpas.p2p_accept_go_neg_request(request)
+
+ wd.wait_for_object_condition(request, '\'success\' in obj', max_wait=3)
+ self.assertEqual(request['success'], True)
+ self.assertEqual(request['role'], 'GO')
+ self.assertEqual(request['wps_method'], 'PBC')
+ self.assertEqual(request['p2p_dev_addr'], wpas_peer['p2p_dev_addr'])
+
+ wd.wait_for_object_condition(wpas, 'obj.p2p_group is not None', max_wait=3)
+ go_ifname = wpas.p2p_group['ifname']
+ ctx.start_process(['ifconfig', go_ifname, '192.168.1.20', 'netmask', '255.255.255.0'], wait=True)
+ os.system('> /tmp/dhcpd.leases')
+ dhcpd = ctx.start_process(['dhcpd', '-f', '-cf', '/tmp/dhcpd.conf', '-lf', '/tmp/dhcpd.leases', go_ifname])
+
+ wd.wait_for_object_condition(wpas, 'len(obj.p2p_clients) == 1', max_wait=3)
+ client = wpas.p2p_clients[request['peer_iface']]
+ self.assertEqual(client['p2p_dev_addr'], wpas_peer['p2p_dev_addr'])
+
+ wd.wait_for_object_condition(peer, 'obj.connected', max_wait=15)
+ our_ip = netifaces.ifaddresses(peer.connected_interface)[netifaces.AF_INET][0]['addr']
+ self.assertEqual(peer.connected_ip, '192.168.1.20')
+ self.assertEqual(our_ip, '192.168.1.30')
+
+ testutil.test_iface_operstate(peer.connected_interface)
+ testutil.test_ifaces_connected(peer.connected_interface, go_ifname)
+
+ peer.disconnect()
+ wd.wait_for_object_condition(wpas, 'len(obj.p2p_clients) == 0', max_wait=3)
+ self.assertEqual(peer.connected, False)
+
+ p2p.enabled = False
+ ctx.stop_process(dhcpd)
+ wpas.clean_up()
+
+ @classmethod
+ def tearDownClass(cls):
+ IWD.clear_storage()
+
+if __name__ == '__main__':
+ unittest.main(exit=True)
diff --git a/autotests/testP2P/dhcpd.conf b/autotests/testP2P/dhcpd.conf
new file mode 100644
index 00000000..a5ef0b4a
--- /dev/null
+++ b/autotests/testP2P/dhcpd.conf
@@ -0,0 +1,4 @@
+subnet 192.168.1.0 netmask 255.255.255.0
+ {
+ range 192.168.1.30;
+ }
diff --git a/autotests/testP2P/hw.conf b/autotests/testP2P/hw.conf
new file mode 100644
index 00000000..d8f213f7
--- /dev/null
+++ b/autotests/testP2P/hw.conf
@@ -0,0 +1,6 @@
+[SETUP]
+num_radios=2
+hwsim_medium=no
+
+[WPA_SUPPLICANT]
+rad1=rad1-p2p.conf
diff --git a/autotests/testP2P/main.conf b/autotests/testP2P/main.conf
new file mode 100644
index 00000000..75040f78
--- /dev/null
+++ b/autotests/testP2P/main.conf
@@ -0,0 +1,6 @@
+[General]
+EnableNetworkConfiguration=true
+
+[P2P]
+# Something different from the default of 'pc' for validation
+DeviceType=desktop
diff --git a/autotests/testP2P/rad1-p2p.conf b/autotests/testP2P/rad1-p2p.conf
new file mode 100644
index 00000000..a34f3d91
--- /dev/null
+++ b/autotests/testP2P/rad1-p2p.conf
@@ -0,0 +1,10 @@
+ctrl_interface=/tmp/rad1-p2p-wpas
+update_config=0
+device_name=testdev2
+device_type=7-0050F204-4 # 7 == Display, 4 == Monitor
+config_methods=virtual_push_button
+p2p_listen_reg_class=81
+p2p_listen_channel=11
+p2p_oper_reg_class=81
+p2p_oper_channel=11
+p2p_go_intent=10
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 1/5] test-runner: Reserve radios for wpa_supplicant
2020-10-09 18:08 [PATCH 1/5] test-runner: Reserve radios for wpa_supplicant Andrew Zaborowski
` (3 preceding siblings ...)
2020-10-09 18:08 ` [PATCH 5/5] autotests: Add testP2P Andrew Zaborowski
@ 2020-10-19 22:22 ` Denis Kenzior
4 siblings, 0 replies; 6+ messages in thread
From: Denis Kenzior @ 2020-10-19 22:22 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 547 bytes --]
Hi Andrew,
On 10/9/20 1:08 PM, Andrew Zaborowski wrote:
> Add support for a WPA_SUPPLICANT section in hw.conf where
> 'radN=<config_path>' lines will only reserve radios and create
> interfaces for the autotest to be able to start wpa_supplicant on them,
> i.e. this prevents iwd or hostapd from being started on them but doesn't
> start a wpa_supplicant instance by itself.
> ---
> tools/test-runner | 24 +++++++++++++++++-------
> 1 file changed, 17 insertions(+), 7 deletions(-)
>
All applied, thanks.
Regards,
-Denis
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2020-10-19 22:22 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-09 18:08 [PATCH 1/5] test-runner: Reserve radios for wpa_supplicant Andrew Zaborowski
2020-10-09 18:08 ` [PATCH 2/5] autotests: Basic P2P python API Andrew Zaborowski
2020-10-09 18:08 ` [PATCH 3/5] autotests: Add basic wpa_supplicant P2P python wrapper Andrew Zaborowski
2020-10-09 18:08 ` [PATCH 4/5] autotests: Move some variables from IWD class to instance Andrew Zaborowski
2020-10-09 18:08 ` [PATCH 5/5] autotests: Add testP2P Andrew Zaborowski
2020-10-19 22:22 ` [PATCH 1/5] test-runner: Reserve radios for wpa_supplicant Denis Kenzior
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.