* [PATCH 1/6] doc: Document new BSS affinity method calls
@ 2024-07-29 12:44 James Prestwood
2024-07-29 12:44 ` [PATCH 2/6] netdev: store signal threshold in netdev object, not globally James Prestwood
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: James Prestwood @ 2024-07-29 12:44 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
This documents new DBus methods that expose a bit more control to how
IWD roams. The new methods are:
SetConnectedBssAffinity()
UnsetConnectedBssAffinity()
Setting the affinity on the connected BSS effectively "locks" IWD to
that BSS (except at critical RSSI levels, explained below). This can
be useful for clients that have access to more information about the
environment than IWD. For example, if a client is stationary there
is likely no point in trying to roam until it has moved elsewhere.
A new main.conf option would also be added:
[General].CriticalRoamThreshold
This would be the new roam threshold set after
SetConnectedBssAffinity() is called. If the RSSI continues to drop
below this level IWD would (attempt to) roam.
---
doc/station-api.txt | 22 ++++++++++++++++++++++
src/iwd.config.rst | 8 ++++++++
2 files changed, 30 insertions(+)
diff --git a/doc/station-api.txt b/doc/station-api.txt
index 05dd137e..3dc2376b 100644
--- a/doc/station-api.txt
+++ b/doc/station-api.txt
@@ -135,6 +135,28 @@ Methods void Scan()
Possible Errors: [service].Error.InvalidArguments
[service].Error.NotFound
+ void SetConnectedBssAffinity()
+
+ Sets a high affinity/preference to the currently
+ connected BSS. Specifically, calling this will reduce
+ the roam threshold down to a critical level set by
+ [General].CriticalRoamThreshold. This effectively locks
+ the client onto the current BSS unless the signal
+ quality becomes extremely poor.
+
+ If IWD roams or disconnects the affinity will be reset.
+
+ Possible Errors: net.connman.iwd.NotConnected
+
+ void UnsetConnectedBssAffinity()
+
+ Unset a prior call to SetConnectedBssAffinity(). This
+ moves the roaming threshold back up to the default level
+ (RoamThreshold/RoamThreshold5GHz).
+
+ Possible Errors: net.connman.iwd.NotConnected
+ net.connman.iwd.NotConfigured
+
Properties string State [readonly]
Reflects the general network connection state. One of:
diff --git a/src/iwd.config.rst b/src/iwd.config.rst
index d9c94e01..31f43bc6 100644
--- a/src/iwd.config.rst
+++ b/src/iwd.config.rst
@@ -130,6 +130,14 @@ The group ``[General]`` contains general settings.
This value can be used to control how aggressively **iwd** roams when
connected to a 5GHz access point.
+ * - CriticalRoamThreshold
+ - Value: rssi dBm value, from -100 to 1, default: **-82**
+
+ The threshold at which IWD will roam regardless of the affinity set to
+ the current BSS. This is only used if the SetConnectedBssAffinity() DBus
+ method is called, at which point this value becomes the new roam
+ threshold.
+
* - RoamRetryInterval
- Value: unsigned int value in seconds (default: **60**)
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/6] netdev: store signal threshold in netdev object, not globally
2024-07-29 12:44 [PATCH 1/6] doc: Document new BSS affinity method calls James Prestwood
@ 2024-07-29 12:44 ` James Prestwood
2024-07-29 12:44 ` [PATCH 3/6] netdev: add critical signal threshold level James Prestwood
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: James Prestwood @ 2024-07-29 12:44 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
This prepares for the ability to toggle between two signal
thresholds in netdev. Since each netdev may not need/want the
same threshold store it in the netdev object rather than globally.
---
src/netdev.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/src/netdev.c b/src/netdev.c
index 2d70fc38..db1ad380 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -172,6 +172,9 @@ struct netdev {
struct netdev_ext_key_info *ext_key_info;
+ int low_signal_threshold;
+ int low_signal_threshold_5ghz;
+
bool connected : 1;
bool associated : 1;
bool operational : 1;
@@ -206,6 +209,9 @@ static struct l_genl_family *nl80211;
static struct l_queue *netdev_list;
static struct watchlist netdev_watches;
static bool mac_per_ssid;
+/* Threshold RSSI for roaming to trigger, configurable in main.conf */
+static int LOW_SIGNAL_THRESHOLD;
+static int LOW_SIGNAL_THRESHOLD_5GHZ;
static unsigned int iov_ie_append(struct iovec *iov,
unsigned int n_iov, unsigned int c,
@@ -1058,10 +1064,6 @@ struct netdev *netdev_find(int ifindex)
return l_queue_find(netdev_list, netdev_match, L_UINT_TO_PTR(ifindex));
}
-/* Threshold RSSI for roaming to trigger, configurable in main.conf */
-static int LOW_SIGNAL_THRESHOLD;
-static int LOW_SIGNAL_THRESHOLD_5GHZ;
-
static void netdev_cqm_event_rssi_threshold(struct netdev *netdev,
uint32_t rssi_event)
{
@@ -1089,8 +1091,9 @@ static void netdev_cqm_event_rssi_value(struct netdev *netdev, int rssi_val)
{
bool new_rssi_low;
uint8_t prev_rssi_level_idx = netdev->cur_rssi_level_idx;
- int threshold = netdev->frequency > 4000 ? LOW_SIGNAL_THRESHOLD_5GHZ :
- LOW_SIGNAL_THRESHOLD;
+ int threshold = netdev->frequency > 4000 ?
+ netdev->low_signal_threshold_5ghz :
+ netdev->low_signal_threshold;
if (!netdev->connected)
return;
@@ -3585,8 +3588,9 @@ static struct l_genl_msg *netdev_build_cmd_cqm_rssi_update(
uint32_t hyst = 5;
int thold_count;
int32_t thold_list[levels_num + 2];
- int threshold = netdev->frequency > 4000 ? LOW_SIGNAL_THRESHOLD_5GHZ :
- LOW_SIGNAL_THRESHOLD;
+ int threshold = netdev->frequency > 4000 ?
+ netdev->low_signal_threshold_5ghz :
+ netdev->low_signal_threshold;
if (levels_num == 0) {
thold_list[0] = threshold;
@@ -6164,6 +6168,8 @@ struct netdev *netdev_create_from_genl(struct l_genl_msg *msg,
l_strlcpy(netdev->name, ifname, IFNAMSIZ);
netdev->wiphy = wiphy;
netdev->pae_over_nl80211 = pae_io == NULL;
+ netdev->low_signal_threshold = LOW_SIGNAL_THRESHOLD;
+ netdev->low_signal_threshold_5ghz = LOW_SIGNAL_THRESHOLD_5GHZ;
if (set_mac)
memcpy(netdev->set_mac_once, set_mac, 6);
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/6] netdev: add critical signal threshold level
2024-07-29 12:44 [PATCH 1/6] doc: Document new BSS affinity method calls James Prestwood
2024-07-29 12:44 ` [PATCH 2/6] netdev: store signal threshold in netdev object, not globally James Prestwood
@ 2024-07-29 12:44 ` James Prestwood
2024-07-29 12:44 ` [PATCH 4/6] station: add {Set,Unset}ConnectedBssAffinity DBus methods James Prestwood
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: James Prestwood @ 2024-07-29 12:44 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
This adds a secondary set of signal thresholds. The purpose of these
are to provide more flexibility in how IWD roams. The critical
threshold is intended to be temporary and is automatically reset
upon any connection changes: disconnects, roams, or new connections.
---
src/netdev.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/netdev.h | 2 ++
2 files changed, 53 insertions(+)
diff --git a/src/netdev.c b/src/netdev.c
index db1ad380..852ad569 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -212,6 +212,8 @@ static bool mac_per_ssid;
/* Threshold RSSI for roaming to trigger, configurable in main.conf */
static int LOW_SIGNAL_THRESHOLD;
static int LOW_SIGNAL_THRESHOLD_5GHZ;
+static int CRITICAL_SIGNAL_THRESHOLD;
+static int CRITICAL_SIGNAL_THRESHOLD_5GHZ;
static unsigned int iov_ie_append(struct iovec *iov,
unsigned int n_iov, unsigned int c,
@@ -841,6 +843,10 @@ static void netdev_connect_free(struct netdev *netdev)
l_genl_family_cancel(nl80211, netdev->get_oci_cmd_id);
netdev->get_oci_cmd_id = 0;
}
+
+ /* Reset thresholds back to default */
+ netdev->low_signal_threshold = LOW_SIGNAL_THRESHOLD;
+ netdev->low_signal_threshold_5ghz = LOW_SIGNAL_THRESHOLD_5GHZ;
}
static void netdev_connect_failed(struct netdev *netdev,
@@ -3674,6 +3680,39 @@ static int netdev_cqm_rssi_update(struct netdev *netdev)
return 0;
}
+static int netdev_set_signal_thresholds(struct netdev *netdev, int threshold,
+ int threshold_5ghz)
+{
+ int current = netdev->frequency > 4000 ?
+ netdev->low_signal_threshold_5ghz :
+ netdev->low_signal_threshold;
+ int new = netdev->frequency > 4000 ? threshold_5ghz : threshold;
+
+ if (current == new)
+ return -EALREADY;
+
+ l_debug("changing low signal threshold to %d", new);
+
+ netdev->low_signal_threshold = threshold;
+ netdev->low_signal_threshold_5ghz = threshold_5ghz;
+
+ netdev_cqm_rssi_update(netdev);
+
+ return 0;
+}
+
+int netdev_lower_signal_threshold(struct netdev *netdev)
+{
+ return netdev_set_signal_thresholds(netdev, CRITICAL_SIGNAL_THRESHOLD,
+ CRITICAL_SIGNAL_THRESHOLD_5GHZ);
+}
+
+int netdev_raise_signal_threshold(struct netdev *netdev)
+{
+ return netdev_set_signal_thresholds(netdev, LOW_SIGNAL_THRESHOLD,
+ LOW_SIGNAL_THRESHOLD_5GHZ);
+}
+
static bool netdev_connection_work_ready(struct wiphy_radio_work_item *item)
{
struct netdev *netdev = l_container_of(item, struct netdev, work);
@@ -3884,6 +3923,8 @@ build_cmd_connect:
netdev->handshake = hs;
netdev->sm = sm;
netdev->cur_rssi = bss->signal_strength / 100;
+ netdev->low_signal_threshold = LOW_SIGNAL_THRESHOLD;
+ netdev->low_signal_threshold_5ghz = LOW_SIGNAL_THRESHOLD_5GHZ;
if (netdev->rssi_levels_num)
netdev_set_rssi_level_idx(netdev);
@@ -4257,6 +4298,8 @@ int netdev_ft_reassociate(struct netdev *netdev,
netdev->event_filter = event_filter;
netdev->connect_cb = cb;
netdev->user_data = user_data;
+ netdev->low_signal_threshold = LOW_SIGNAL_THRESHOLD;
+ netdev->low_signal_threshold_5ghz = LOW_SIGNAL_THRESHOLD_5GHZ;
/*
* Cancel commands that could be running because of EAPoL activity
@@ -6279,6 +6322,14 @@ static int netdev_init(void)
&LOW_SIGNAL_THRESHOLD_5GHZ))
LOW_SIGNAL_THRESHOLD_5GHZ = -76;
+ if (!l_settings_get_int(settings, "General", "CriticalRoamThreshold",
+ &CRITICAL_SIGNAL_THRESHOLD))
+ CRITICAL_SIGNAL_THRESHOLD = -80;
+
+ if (!l_settings_get_int(settings, "General", "CriticalRoamThreshold5G",
+ &CRITICAL_SIGNAL_THRESHOLD_5GHZ))
+ CRITICAL_SIGNAL_THRESHOLD_5GHZ = -82;
+
rand_addr_str = l_settings_get_value(settings, "General",
"AddressRandomization");
if (rand_addr_str && !strcmp(rand_addr_str, "network"))
diff --git a/src/netdev.h b/src/netdev.h
index db0440d0..3a772256 100644
--- a/src/netdev.h
+++ b/src/netdev.h
@@ -197,6 +197,8 @@ int netdev_neighbor_report_req(struct netdev *netdev,
int netdev_set_rssi_report_levels(struct netdev *netdev, const int8_t *levels,
size_t levels_num);
+int netdev_lower_signal_threshold(struct netdev *netdev);
+int netdev_raise_signal_threshold(struct netdev *netdev);
int netdev_get_station(struct netdev *netdev, const uint8_t *mac,
netdev_get_station_cb_t cb, void *user_data,
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 4/6] station: add {Set,Unset}ConnectedBssAffinity DBus methods
2024-07-29 12:44 [PATCH 1/6] doc: Document new BSS affinity method calls James Prestwood
2024-07-29 12:44 ` [PATCH 2/6] netdev: store signal threshold in netdev object, not globally James Prestwood
2024-07-29 12:44 ` [PATCH 3/6] netdev: add critical signal threshold level James Prestwood
@ 2024-07-29 12:44 ` James Prestwood
2024-07-29 12:44 ` [PATCH 5/6] auto-t: add set/unset affinity python methods James Prestwood
2024-07-29 12:44 ` [PATCH 6/6] auto-t: add tests for set/unset affinity James Prestwood
4 siblings, 0 replies; 6+ messages in thread
From: James Prestwood @ 2024-07-29 12:44 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
These methods can be used to loosly lock IWD to its current BSS.
Calling SetConnectedBssAffinity() will lower the roaming threshold
to a critical level ([General].CriticalRoamThreshold) thereby
preventing IWD from trying to roam unless the RSSI reaches this
level.
The motivation for this is a use case in which an external
client/daemon has additional information about the environment and
can use this to make an informed decision about roaming. For example
if the device is stationary its unlikely IWD will find a better roam
target.
---
src/station.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 96 insertions(+)
diff --git a/src/station.c b/src/station.c
index db069981..dc07bacc 100644
--- a/src/station.c
+++ b/src/station.c
@@ -128,6 +128,8 @@ struct station {
uint64_t last_roam_scan;
+ unsigned int affinity_watch;
+
bool preparing_roam : 1;
bool roam_scan_full : 1;
bool signal_low : 1;
@@ -1660,6 +1662,13 @@ static void station_enter_state(struct station *station,
station_set_evict_nocarrier(station, true);
station_set_drop_neighbor_discovery(station, false);
station_set_drop_unicast_l2_multicast(station, false);
+
+ if (station->affinity_watch) {
+ l_dbus_remove_watch(dbus_get_bus(),
+ station->affinity_watch);
+ station->affinity_watch = 0;
+ }
+
break;
case STATION_STATE_DISCONNECTING:
case STATION_STATE_NETCONFIG:
@@ -1668,6 +1677,13 @@ static void station_enter_state(struct station *station,
case STATION_STATE_FT_ROAMING:
case STATION_STATE_FW_ROAMING:
station_set_evict_nocarrier(station, false);
+
+ if (station->affinity_watch) {
+ l_dbus_remove_watch(dbus_get_bus(),
+ station->affinity_watch);
+ station->affinity_watch = 0;
+ }
+
break;
}
@@ -4328,6 +4344,79 @@ static struct l_dbus_message *station_dbus_signal_agent_unregister(
return l_dbus_message_new_method_return(message);
}
+static int station_change_affinity(struct station *station, bool lower)
+{
+ if (!station->connected_network)
+ return -ENOTCONN;
+
+ if (lower)
+ return netdev_lower_signal_threshold(station->netdev);
+ else
+ return netdev_raise_signal_threshold(station->netdev);
+}
+
+static void station_affinity_disconnected_cb(struct l_dbus *dbus,
+ void *user_data)
+{
+ struct station *station = user_data;
+
+ l_debug("client that set affinity has disconnected, setting default");
+
+ /* The client who set the affinity disconnected, raise the threshold */
+ station_change_affinity(station, false);
+
+ station->affinity_watch = 0;
+}
+
+static struct l_dbus_message *station_dbus_set_affinity(
+ struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ void *user_data)
+{
+ struct station *station = user_data;
+ int ret;
+
+ if (station->affinity_watch) {
+ l_warn("A client already set the affinity!");
+ return dbus_error_already_exists(message);
+ }
+
+ ret = station_change_affinity(station, true);
+ if (ret < 0)
+ return dbus_error_from_errno(ret, message);
+
+ station->affinity_watch = l_dbus_add_disconnect_watch(dbus,
+ l_dbus_message_get_sender(message),
+ station_affinity_disconnected_cb,
+ station,
+ NULL);
+
+ return l_dbus_message_new_method_return(message);
+}
+
+static struct l_dbus_message *station_dbus_unset_affinity(
+ struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ void *user_data)
+{
+ struct station *station = user_data;
+ int ret;
+
+ if (!station->affinity_watch) {
+ l_warn("A client has not set the affinity!");
+ return dbus_error_not_configured(message);
+ }
+
+ ret = station_change_affinity(station, false);
+ if (ret < 0)
+ return dbus_error_from_errno(ret, message);
+
+ l_dbus_remove_watch(dbus, station->affinity_watch);
+ station->affinity_watch = 0;
+
+ return l_dbus_message_new_method_return(message);
+}
+
static bool station_property_get_connected_network(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
@@ -4722,6 +4811,9 @@ static void station_free(struct station *station)
l_queue_destroy(station->roam_bss_list, l_free);
+ if (station->affinity_watch)
+ l_dbus_remove_watch(dbus_get_bus(), station->affinity_watch);
+
l_free(station);
}
@@ -4747,6 +4839,10 @@ static void station_setup_interface(struct l_dbus_interface *interface)
l_dbus_interface_method(interface, "UnregisterSignalLevelAgent", 0,
station_dbus_signal_agent_unregister,
"", "o", "path");
+ l_dbus_interface_method(interface, "SetConnectedBssAffinity", 0,
+ station_dbus_set_affinity, "", "");
+ l_dbus_interface_method(interface, "UnsetConnectedBssAffinity", 0,
+ station_dbus_unset_affinity, "", "");
l_dbus_interface_property(interface, "ConnectedNetwork", 0, "o",
station_property_get_connected_network,
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 5/6] auto-t: add set/unset affinity python methods
2024-07-29 12:44 [PATCH 1/6] doc: Document new BSS affinity method calls James Prestwood
` (2 preceding siblings ...)
2024-07-29 12:44 ` [PATCH 4/6] station: add {Set,Unset}ConnectedBssAffinity DBus methods James Prestwood
@ 2024-07-29 12:44 ` James Prestwood
2024-07-29 12:44 ` [PATCH 6/6] auto-t: add tests for set/unset affinity James Prestwood
4 siblings, 0 replies; 6+ messages in thread
From: James Prestwood @ 2024-07-29 12:44 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
---
autotests/util/iwd.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/autotests/util/iwd.py b/autotests/util/iwd.py
index 1d4a5472..211f3777 100755
--- a/autotests/util/iwd.py
+++ b/autotests/util/iwd.py
@@ -940,6 +940,16 @@ class Device(IWDDBusAbstract):
return ret
+ def set_connected_bss_affinity(self):
+ self._station.SetConnectedBssAffinity(reply_handler=self._success,
+ error_handler=self._failure)
+ self._wait_for_async_op()
+
+ def unset_connected_bss_affinity(self):
+ self._station.UnsetConnectedBssAffinity(reply_handler=self._success,
+ error_handler=self._failure)
+ self._wait_for_async_op()
+
def __str__(self, prefix = ''):
s = prefix + 'Device: ' + self.device_path + '\n'\
+ prefix + '\tName:\t\t' + self.name + '\n'\
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 6/6] auto-t: add tests for set/unset affinity
2024-07-29 12:44 [PATCH 1/6] doc: Document new BSS affinity method calls James Prestwood
` (3 preceding siblings ...)
2024-07-29 12:44 ` [PATCH 5/6] auto-t: add set/unset affinity python methods James Prestwood
@ 2024-07-29 12:44 ` James Prestwood
4 siblings, 0 replies; 6+ messages in thread
From: James Prestwood @ 2024-07-29 12:44 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
---
autotests/testSetAffinity/TestFT.psk | 2 +
autotests/testSetAffinity/ft-psk-ccmp-1.conf | 41 ++++
autotests/testSetAffinity/ft-psk-ccmp-2.conf | 41 ++++
autotests/testSetAffinity/hw.conf | 8 +
autotests/testSetAffinity/main.conf | 5 +
.../testSetAffinity/test_set_affinity.py | 224 ++++++++++++++++++
6 files changed, 321 insertions(+)
create mode 100644 autotests/testSetAffinity/TestFT.psk
create mode 100644 autotests/testSetAffinity/ft-psk-ccmp-1.conf
create mode 100644 autotests/testSetAffinity/ft-psk-ccmp-2.conf
create mode 100644 autotests/testSetAffinity/hw.conf
create mode 100644 autotests/testSetAffinity/main.conf
create mode 100644 autotests/testSetAffinity/test_set_affinity.py
diff --git a/autotests/testSetAffinity/TestFT.psk b/autotests/testSetAffinity/TestFT.psk
new file mode 100644
index 00000000..e82d1295
--- /dev/null
+++ b/autotests/testSetAffinity/TestFT.psk
@@ -0,0 +1,2 @@
+[Security]
+Passphrase=EasilyGuessedPassword
diff --git a/autotests/testSetAffinity/ft-psk-ccmp-1.conf b/autotests/testSetAffinity/ft-psk-ccmp-1.conf
new file mode 100644
index 00000000..839eb496
--- /dev/null
+++ b/autotests/testSetAffinity/ft-psk-ccmp-1.conf
@@ -0,0 +1,41 @@
+hw_mode=g
+channel=1
+ssid=TestFT
+utf8_ssid=1
+ctrl_interface=/var/run/hostapd
+
+r1_key_holder=120000000001
+nas_identifier=dummy1
+
+wpa=2
+# Can support WPA-PSK and FT-PSK (space separated list) and/or EAP at the same
+# time but we want to force FT
+wpa_key_mgmt=FT-PSK
+wpa_pairwise=CCMP
+wpa_passphrase=EasilyGuessedPassword
+ieee80211w=0
+rsn_preauth=1
+rsn_preauth_interfaces=lo
+disable_pmksa_caching=0
+# Allow PMK cache to be shared opportunistically among configured interfaces
+# and BSSes (i.e., all configurations within a single hostapd process).
+okc=1
+mobility_domain=1234
+reassociation_deadline=60000
+r0kh=12:00:00:00:00:01 dummy1 000102030405060708090a0b0c0d0e0f
+r0kh=12:00:00:00:00:02 dummy2 000102030405060708090a0b0c0d0e0f
+r1kh=12:00:00:00:00:01 00:00:00:00:00:01 000102030405060708090a0b0c0d0e0f
+r1kh=12:00:00:00:00:02 00:00:00:00:00:02 000102030405060708090a0b0c0d0e0f
+# Push mode only needed for 8021x, not PSK mode since msk already known
+pmk_r1_push=0
+# Allow locally generated FT response so we don't have to configure push/pull
+# between BSSes running as separate hostapd processes as in the test-runner
+# case. Only works with FT-PSK, otherwise brctl needs to be installed and
+# CONFIG_BRIDGE enabled in the kernel.
+ft_psk_generate_local=1
+rkh_pull_timeout=50
+ft_over_ds=0
+ap_table_expiration_time=36000
+ap_table_max_size=10
+rrm_neighbor_report=1
+ocv=1
diff --git a/autotests/testSetAffinity/ft-psk-ccmp-2.conf b/autotests/testSetAffinity/ft-psk-ccmp-2.conf
new file mode 100644
index 00000000..2ffd7262
--- /dev/null
+++ b/autotests/testSetAffinity/ft-psk-ccmp-2.conf
@@ -0,0 +1,41 @@
+hw_mode=g
+channel=2
+ssid=TestFT
+utf8_ssid=1
+ctrl_interface=/var/run/hostapd
+
+r1_key_holder=120000000002
+nas_identifier=dummy2
+
+wpa=2
+# Can support WPA-PSK and FT-PSK (space separated list) and/or EAP at the same
+# time but we want to force FT
+wpa_key_mgmt=FT-PSK
+wpa_pairwise=CCMP
+wpa_passphrase=EasilyGuessedPassword
+ieee80211w=0
+rsn_preauth=1
+rsn_preauth_interfaces=lo
+disable_pmksa_caching=0
+# Allow PMK cache to be shared opportunistically among configured interfaces
+# and BSSes (i.e., all configurations within a single hostapd process).
+okc=1
+mobility_domain=1234
+reassociation_deadline=60000
+r0kh=12:00:00:00:00:01 dummy1 000102030405060708090a0b0c0d0e0f
+r0kh=12:00:00:00:00:02 dummy2 000102030405060708090a0b0c0d0e0f
+r1kh=12:00:00:00:00:01 00:00:00:00:00:01 000102030405060708090a0b0c0d0e0f
+r1kh=12:00:00:00:00:02 00:00:00:00:00:02 000102030405060708090a0b0c0d0e0f
+# Push mode only needed for 8021x, not PSK mode since msk already known
+pmk_r1_push=0
+# Allow locally generated FT response so we don't have to configure push/pull
+# between BSSes running as separate hostapd processes as in the test-runner
+# case. Only works with FT-PSK, otherwise brctl needs to be installed and
+# CONFIG_BRIDGE enabled in the kernel.
+ft_psk_generate_local=1
+rkh_pull_timeout=50
+ft_over_ds=0
+ap_table_expiration_time=36000
+ap_table_max_size=10
+rrm_neighbor_report=1
+ocv=1
diff --git a/autotests/testSetAffinity/hw.conf b/autotests/testSetAffinity/hw.conf
new file mode 100644
index 00000000..c2b35d6e
--- /dev/null
+++ b/autotests/testSetAffinity/hw.conf
@@ -0,0 +1,8 @@
+[SETUP]
+num_radios=3
+start_iwd=0
+hwsim_medium=yes
+
+[HOSTAPD]
+rad0=ft-psk-ccmp-1.conf
+rad1=ft-psk-ccmp-2.conf
diff --git a/autotests/testSetAffinity/main.conf b/autotests/testSetAffinity/main.conf
new file mode 100644
index 00000000..3d93ff57
--- /dev/null
+++ b/autotests/testSetAffinity/main.conf
@@ -0,0 +1,5 @@
+[Scan]
+DisableMacAddressRandomization=true
+
+[General]
+RoamRetryInterval=1
diff --git a/autotests/testSetAffinity/test_set_affinity.py b/autotests/testSetAffinity/test_set_affinity.py
new file mode 100644
index 00000000..8a0fdf21
--- /dev/null
+++ b/autotests/testSetAffinity/test_set_affinity.py
@@ -0,0 +1,224 @@
+#! /usr/bin/python3
+
+import unittest
+import sys, os
+import dbus
+
+sys.path.append('../util')
+from config import ctx
+import iwd
+from iwd import IWD, IWDDBusAbstract
+from iwd import NetworkType
+from hwsim import Hwsim
+from hostapd import HostapdCLI
+
+#
+# Separate client used to test DBus disconnects so we don't bring down the
+# entire IWD python library
+#
+class AffinityClient(IWDDBusAbstract):
+ def __init__(self, device_path):
+ self._bus = dbus.bus.BusConnection(address_or_type=ctx.dbus_address)
+ self._station_if = dbus.Interface(self._bus.get_object(iwd.IWD_SERVICE,
+ device_path), iwd.IWD_STATION_INTERFACE)
+
+ def set(self):
+ self._station_if.SetConnectedBssAffinity(reply_handler=self._success,
+ error_handler=self._failure)
+ self._wait_for_async_op()
+
+ def unset(self):
+ self._station_if.UnsetConnectedBssAffinity(reply_handler=self._success,
+ error_handler=self._failure)
+ self._wait_for_async_op()
+
+ def close(self):
+ self._bus.close()
+
+class Test(unittest.TestCase):
+ def connect(self, device, hapd):
+ ordered_network = device.get_ordered_network('TestFT', full_scan=True)
+
+ self.assertEqual(ordered_network.type, NetworkType.psk)
+
+ condition = 'not obj.connected'
+ self.wd.wait_for_object_condition(ordered_network.network_object, condition)
+
+ device.connect_bssid(hapd.bssid)
+
+ condition = 'obj.state == DeviceState.connected'
+ self.wd.wait_for_object_condition(device, condition)
+
+ def test_set_affinity(self):
+ device = self.wd.list_devices(1)[0]
+
+ self.connect(device, self.bss_hostapd[0])
+
+ device.set_connected_bss_affinity()
+
+ # IWD should not attempt to roam
+ with self.assertRaises(TimeoutError):
+ device.wait_for_event("roam-scan-triggered")
+
+ device.unset_connected_bss_affinity()
+ device.wait_for_event("roam-scan-triggered")
+
+ def test_roam_below_critical(self):
+ device = self.wd.list_devices(1)[0]
+
+ self.connect(device, self.bss_hostapd[0])
+
+ device.set_connected_bss_affinity()
+
+ # IWD should not attempt to roam
+ with self.assertRaises(TimeoutError):
+ device.wait_for_event("roam-scan-triggered")
+
+ # Lower signal past critical level
+ self.bss0_rule.signal = -9000
+
+ device.wait_for_event("roam-scan-triggered")
+
+ def test_affinity_reset_after_roam(self):
+ device = self.wd.list_devices(1)[0]
+
+ self.connect(device, self.bss_hostapd[0])
+
+ device.set_connected_bss_affinity()
+
+ # Lower signal past critical level
+ self.bss0_rule.signal = -9000
+
+ device.wait_for_event("roam-scan-triggered")
+ device.wait_for_event("ft-authenticating")
+ device.wait_for_event("associating")
+ device.wait_for_event("connected")
+
+ # Affinity should be reset, and IWD should be trying to roam
+ device.wait_for_event("roam-scan-triggered")
+
+ def test_error_conditions(self):
+ device = self.wd.list_devices(1)[0]
+
+ # Calling set while disconnected should fail
+ with self.assertRaises(iwd.NotConnectedEx):
+ device.set_connected_bss_affinity()
+
+ # Calling unset prior to a successful set should fail
+ with self.assertRaises(iwd.NotConfiguredEx):
+ device.unset_connected_bss_affinity()
+
+ self.connect(device, self.bss_hostapd[0])
+
+ device.set_connected_bss_affinity()
+
+ # Multiple calls to set should fail
+ with self.assertRaises(iwd.AlreadyExistsEx):
+ device.set_connected_bss_affinity()
+
+ def test_affinity_client_disconnect(self):
+ device = self.wd.list_devices(1)[0]
+
+ client = AffinityClient(device.device_path)
+
+ self.connect(device, self.bss_hostapd[0])
+
+ client.set()
+
+ with self.assertRaises(TimeoutError):
+ device.wait_for_event("roam-scan-triggered")
+
+ client._bus.close()
+
+ device.wait_for_event("roam-scan-triggered")
+
+ def test_affinity_client_reconnect_during_roam(self):
+ device = self.wd.list_devices(1)[0]
+
+ client = AffinityClient(device.device_path)
+
+ self.connect(device, self.bss_hostapd[0])
+
+ client.set()
+
+ # Lower signal past critical level
+ self.bss0_rule.signal = -9000
+
+ device.wait_for_event("roam-scan-triggered")
+
+ client.close()
+ del client
+ client = AffinityClient(device.device_path)
+ # setting here should get cleared after connecting
+ client.set()
+
+ device.wait_for_event("ft-authenticating")
+ device.wait_for_event("associating")
+ device.wait_for_event("connected")
+
+ # Affinity should be reset, and IWD should be trying to roam
+ device.wait_for_event("roam-scan-triggered")
+
+ def test_cleanup_with_connected_client(self):
+ device = self.wd.list_devices(1)[0]
+
+ client = AffinityClient(device.device_path)
+
+ self.connect(device, self.bss_hostapd[0])
+
+ client.set()
+ self.wd.stop()
+
+ def tearDown(self):
+ os.system('ip link set "' + self.bss_hostapd[0].ifname + '" down')
+ os.system('ip link set "' + self.bss_hostapd[1].ifname + '" down')
+ os.system('ip link set "' + self.bss_hostapd[0].ifname + '" up')
+ os.system('ip link set "' + self.bss_hostapd[1].ifname + '" up')
+
+ self.wd.stop()
+ self.wd = None
+
+ def setUp(self):
+ self.bss0_rule.signal = -8000
+ self.bss1_rule.signal = -8000
+
+ self.wd = IWD(True)
+
+ @classmethod
+ def setUpClass(cls):
+ hwsim = Hwsim()
+
+ IWD.copy_to_storage('TestFT.psk')
+
+ cls.bss_hostapd = [ HostapdCLI(config='ft-psk-ccmp-1.conf'),
+ HostapdCLI(config='ft-psk-ccmp-2.conf') ]
+
+ rad0 = hwsim.get_radio('rad0')
+ rad1 = hwsim.get_radio('rad1')
+
+ cls.bss0_rule = hwsim.rules.create()
+ cls.bss0_rule.source = rad0.addresses[0]
+ cls.bss0_rule.bidirectional = True
+ cls.bss0_rule.signal = -8000
+ cls.bss0_rule.enabled = True
+
+ cls.bss1_rule = hwsim.rules.create()
+ cls.bss1_rule.source = rad1.addresses[0]
+ cls.bss1_rule.bidirectional = True
+ cls.bss1_rule.signal = -8000
+ cls.bss1_rule.enabled = True
+
+ cls.bss_hostapd[0].set_address('12:00:00:00:00:01')
+ cls.bss_hostapd[1].set_address('12:00:00:00:00:02')
+
+ HostapdCLI.group_neighbors(*cls.bss_hostapd)
+
+ @classmethod
+ def tearDownClass(cls):
+ IWD.clear_storage()
+ cls.bss_hostapd = None
+ cls.bss0_rule.remove()
+ cls.bss1_rule.remove()
+
+if __name__ == '__main__':
+ unittest.main(exit=True)
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2024-07-29 12:44 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-29 12:44 [PATCH 1/6] doc: Document new BSS affinity method calls James Prestwood
2024-07-29 12:44 ` [PATCH 2/6] netdev: store signal threshold in netdev object, not globally James Prestwood
2024-07-29 12:44 ` [PATCH 3/6] netdev: add critical signal threshold level James Prestwood
2024-07-29 12:44 ` [PATCH 4/6] station: add {Set,Unset}ConnectedBssAffinity DBus methods James Prestwood
2024-07-29 12:44 ` [PATCH 5/6] auto-t: add set/unset affinity python methods James Prestwood
2024-07-29 12:44 ` [PATCH 6/6] auto-t: add tests for set/unset affinity James Prestwood
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox