Skip to contentSkip to conversation

dbus: expose per-BSS security capabilities

RFC Superseded Алексей 2 participants 23 messages Jul 11 – Jul 28

lore
Share & tools
lore
#0 lore raw

Hi,

NetworkManager cannot currently distinguish WPA2-Personal, WPA3-Personal, and WPA2/WPA3 transition networks when using its iwd backend.

iwd has the required information internally. scan_bss stores the WPA and RSN information elements, and scan_bss_get_rsn_info() exposes the parsed AKM and cipher information. However, the stable D-Bus API reduces all of these personal network variants to Network.Type="psk". BasicServiceSet currently exports only Address.

This causes a concrete integration failure. NetworkManager maps an iwd Network with Type="psk" to NM_802_11_AP_SEC_KEY_MGMT_PSK. A saved NM profile with 802-11-wireless-security.key-mgmt="sae" then fails the normal AP/profile compatibility check and is removed from Device.AvailableConnections, even while iwd is successfully connected to the same network using SAE.

Observed with NetworkManager 1.56.1 and iwd 3.12 on a WPA2/WPA3 transition network:

NM profile key-mgmt: sae

  iwd negotiated security:  WPA3-Personal + FT
  NM AP RsnFlags:           0x188 (CCMP + PSK, no SAE)
  NM AvailableConnections:  empty

Would it be acceptable to extend net.connman.iwd.BasicServiceSet with stable WPA/RSN security capability information?

The information needs to include at least:

- advertised AKM suites (PSK, FT-PSK, SAE, FT-SAE, and so on)
- pairwise and group ciphers
- management frame protection capability and requirement

Per-BSS properties seem preferable to a Network-level value because BSSs in the same ESS may advertise different capabilities. A consumer such as NetworkManager can aggregate the capabilities when presenting the iwd Network as one logical AP while leaving BSS selection and roaming to iwd.

One possible API shape would be an extensible read-only RSN dictionary on BasicServiceSet, similar to:

  RSN = {
      "KeyManagement": ["psk", "sae", "ft-sae"],
      "Pairwise": ["ccmp"],
      "Group": "ccmp",
      "ManagementFrameProtection": "optional"
  }

The exact representation is the main subject of this RFC. Raw suite selectors, separate properties, or an aggregate Network property may fit iwd's API conventions better.

A corresponding NetworkManager change would consume these capabilities instead of synthesizing PSK-only RsnFlags from Network.Type. Separately, NM should preserve an SAE-only profile policy in the generated iwd profile by using:

[Settings]
TransitionDisable=true
DisabledTransitionModes=personal

Related reports and downstream investigation:

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1489
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1693
https://invent.kde.org/plasma/plasma-nm/-/merge_requests/571

There was prior discussion indicating that additional BasicServiceSet properties are welcome when the data already exists in scan_bss:

https://lore.kernel.org/iwd/[email protected]/

If this direction is acceptable, I can follow up with the D-Bus documentation, implementation, and tests after agreeing on the property format.

Regards,
Aleks

#1 lore raw

Hi,

Quoted 42 lines · Алексей

Hi,

NetworkManager cannot currently distinguish WPA2-Personal, WPA3-Personal, and WPA2/WPA3 transition networks when using its iwd backend.

iwd has the required information internally. scan_bss stores the WPA and RSN information elements, and scan_bss_get_rsn_info() exposes the parsed AKM and cipher information. However, the stable D-Bus API reduces all of these personal network variants to Network.Type="psk". BasicServiceSet currently exports only Address.

This causes a concrete integration failure. NetworkManager maps an iwd Network with Type="psk" to NM_802_11_AP_SEC_KEY_MGMT_PSK. A saved NM profile with 802-11-wireless-security.key-mgmt="sae" then fails the normal AP/profile compatibility check and is removed from Device.AvailableConnections, even while iwd is successfully connected to the same network using SAE.

Observed with NetworkManager 1.56.1 and iwd 3.12 on a WPA2/WPA3 transition network:

NM profile key-mgmt: sae

   iwd negotiated security:  WPA3-Personal + FT
   NM AP RsnFlags:           0x188 (CCMP + PSK, no SAE)
   NM AvailableConnections:  empty

Would it be acceptable to extend net.connman.iwd.BasicServiceSet with stable WPA/RSN security capability information?

The information needs to include at least:

- advertised AKM suites (PSK, FT-PSK, SAE, FT-SAE, and so on)
- pairwise and group ciphers
- management frame protection capability and requirement

Per-BSS properties seem preferable to a Network-level value because BSSs in the same ESS may advertise different capabilities. A consumer such as NetworkManager can aggregate the capabilities when presenting the iwd Network as one logical AP while leaving BSS selection and roaming to iwd.

One possible API shape would be an extensible read-only RSN dictionary on BasicServiceSet, similar to:

Quoted 6 lines
   RSN = {
       "KeyManagement": ["psk", "sae", "ft-sae"],
       "Pairwise": ["ccmp"],
       "Group": "ccmp",
       "ManagementFrameProtection": "optional"
   }

Extending the BasicServiceSet interface to include these properties seems perfectly reasonable to me.

Quoted 23 lines

The exact representation is the main subject of this RFC. Raw suite selectors, separate properties, or an aggregate Network property may fit iwd's API conventions better.

A corresponding NetworkManager change would consume these capabilities instead of synthesizing PSK-only RsnFlags from Network.Type. Separately, NM should preserve an SAE-only profile policy in the generated iwd profile by using:

[Settings]
TransitionDisable=true
DisabledTransitionModes=personal

Related reports and downstream investigation:

   https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1489
   https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1693
   https://invent.kde.org/plasma/plasma-nm/-/merge_requests/571

There was prior discussion indicating that additional BasicServiceSet properties are welcome when the data already exists in scan_bss:

Quoted 9 lines
   https://lore.kernel.org/iwd/[email protected]/

If this direction is acceptable, I can follow up with the D-Bus documentation, implementation, and tests after agreeing on the property format.

Regards,
Aleks

Thanks,

James

#2 lore raw

cover dbus: expose per-BSS RSN capabilities

James, thanks for confirming that extending BasicServiceSet is a reasonable approach.

Network.Type intentionally represents WPA2-Personal, WPA3-Personal, and transition networks as "psk". This series exposes the RSN information that iwd already retains for each BSS so consumers can distinguish those networks without changing the existing Network API.

The optional BasicServiceSet.RSN dictionary uses the same KeyMgmt, Pairwise, and Group fields and values as wpa_supplicant's BSS.RSN property. NetworkManager can therefore reuse its existing parser rather than add another translation layer. Older consumers ignore the optional property, and consumers can retain their Network.Type fallback for older iwd versions.

A corresponding NetworkManager series reads and combines the per-BSS capabilities for its synthetic ESS access point. It will be submitted separately after this API series.

Validation:

- iwd builds successfully with make -j4.
- On a physical WPA2/WPA3 transition network, an initial PSK connection was reported as WPA2. After changing the saved profile to SAE, a disconnect/reconnect through the stock Plasma applet completed without a password prompt and was reported as WPA3-SAE.
- Cycling Wi-Fi subsequently auto-connected the SAE profile and kept it visible as connected in the applet.

The new QEMU auto-t test was not run locally because /dev/kvm is not available on the test machine.

RFC:
https://lore.kernel.org/iwd/CAMq8zouhDMRW4Q7qSpMCFftp21wCzuC%[email protected]/

Related NetworkManager issues:
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1489
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1693

Aleks Man (3):
network: expose RSN capabilities on BSS
doc: document BasicServiceSet RSN property
auto-t: test BasicServiceSet RSN property

 .../basic_service_set_rsn_test.py             |  70 +++++++++++
 autotests/testBasicServiceSetRSN/hw.conf      |   9 ++
 autotests/testBasicServiceSetRSN/ssidSAE.conf |  11 ++
 .../ssidTransition.conf                       |  12 ++
 .../testBasicServiceSetRSN/ssidWPA2.conf      |   8 ++
 doc/basic-service-set.txt                     |  23 ++++
 src/network.c                                 | 110 ++++++++++++++++++
 7 files changed, 243 insertions(+)
 create mode 100644
autotests/testBasicServiceSetRSN/basic_service_set_rsn_test.py
 create mode 100644 autotests/testBasicServiceSetRSN/hw.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidSAE.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidTransition.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidWPA2.conf
Signature · 1 line
2.55.0
#3 lore raw

PATCH 1/3 network: expose RSN capabilities on BSS

Network.Type intentionally groups all personal networks as "psk". Consumers therefore cannot distinguish PSK, SAE, and transition BSSes even though scan results retain the RSN IE.

Add an optional RSN dictionary to BasicServiceSet with the advertised key management suites and pairwise and group ciphers. Use the same field and value names as wpa_supplicant's BSS.RSN property so consumers can share parsing logic.

Parse bss->rsne directly so legacy WPA capabilities are not exposed as RSN.

---
 src/network.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)
src/network.c +0 -0
index a5a2375a..aa9e1281 100644
@@ -2198,10 +2198,120 @@ static bool

network_bss_property_get_address(struct l_dbus *dbus,
return true;
}

+struct network_bss_suite_name {
+ uint32_t suite;
+ const char *name;
+};
+
+static const struct network_bss_suite_name network_bss_akm_names[] = {
+ { IE_RSN_AKM_SUITE_PSK, "wpa-psk" },
+ { IE_RSN_AKM_SUITE_FT_USING_PSK, "wpa-ft-psk" },
+ { IE_RSN_AKM_SUITE_PSK_SHA256, "wpa-psk-sha256" },
+ { IE_RSN_AKM_SUITE_8021X, "wpa-eap" },
+ { IE_RSN_AKM_SUITE_FT_OVER_8021X, "wpa-ft-eap" },
+ { IE_RSN_AKM_SUITE_8021X_SHA256, "wpa-eap-sha256" },
+ { IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA256, "wpa-eap-suite-b" },
+ { IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA384, "wpa-eap-suite-b-192" },
+ { IE_RSN_AKM_SUITE_FT_OVER_8021X_SHA384, "wpa-ft-eap-sha384" },
+ { IE_RSN_AKM_SUITE_FILS_SHA256, "wpa-fils-sha256" },
+ { IE_RSN_AKM_SUITE_FILS_SHA384, "wpa-fils-sha384" },
+ { IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256, "wpa-ft-fils-sha256" },
+ { IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384, "wpa-ft-fils-sha384" },
+ { IE_RSN_AKM_SUITE_SAE_SHA256, "sae" },
+ { IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256, "ft-sae" },
+ { IE_RSN_AKM_SUITE_OWE, "owe" },
+};
+
+static const struct network_bss_suite_name network_bss_pairwise_names[] = {
+ { IE_RSN_CIPHER_SUITE_TKIP, "tkip" },
+ { IE_RSN_CIPHER_SUITE_CCMP, "ccmp" },
+ { IE_RSN_CIPHER_SUITE_GCMP, "gcmp" },
+ { IE_RSN_CIPHER_SUITE_CCMP_256, "ccmp-256" },
+ { IE_RSN_CIPHER_SUITE_GCMP_256, "gcmp-256" },
+};
+
+static const struct network_bss_suite_name network_bss_group_names[] = {
+ { IE_RSN_CIPHER_SUITE_WEP40, "wep40" },
+ { IE_RSN_CIPHER_SUITE_WEP104, "wep104" },
+ { IE_RSN_CIPHER_SUITE_TKIP, "tkip" },
+ { IE_RSN_CIPHER_SUITE_CCMP, "ccmp" },
+ { IE_RSN_CIPHER_SUITE_GCMP, "gcmp" },
+ { IE_RSN_CIPHER_SUITE_CCMP_256, "ccmp-256" },
+ { IE_RSN_CIPHER_SUITE_GCMP_256, "gcmp-256" },
+};
+
+static void network_bss_append_suite_names(
+ struct l_dbus_message_builder *builder,
+ const char *name, uint32_t suites,
+ const struct network_bss_suite_name *names,
+ size_t names_len)
+{
+ size_t i;
+
+ l_dbus_message_builder_enter_dict(builder, "sv");
+ l_dbus_message_builder_append_basic(builder, 's', name);
+ l_dbus_message_builder_enter_variant(builder, "as");
+ l_dbus_message_builder_enter_array(builder, "s");
+
+ for (i = 0; i < names_len; i++)
+ if (suites & names[i].suite)
+ l_dbus_message_builder_append_basic(builder, 's',
+ names[i].name);
+
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_leave_variant(builder);
+ l_dbus_message_builder_leave_dict(builder);
+}
+
+static const char *network_bss_group_cipher_name(
+ enum ie_rsn_cipher_suite cipher)
+{
+ size_t i;
+
+ for (i = 0; i < L_ARRAY_SIZE(network_bss_group_names); i++)
+ if (network_bss_group_names[i].suite == cipher)
+ return network_bss_group_names[i].name;
+
+ return "";
+}
+
+static bool network_bss_property_get_rsn(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ struct l_dbus_message_builder *builder,
+ void *user_data)
+{
+ struct scan_bss *bss = user_data;
+ struct ie_rsn_info info;
+ const char *group;
+
+ if (!bss->rsne || ie_parse_rsne_from_data(bss->rsne, bss->rsne[1] + 2,
+ &info) < 0)
+ return false;
+
+ l_dbus_message_builder_enter_array(builder, "{sv}");
+
+ network_bss_append_suite_names(builder, "KeyMgmt", info.akm_suites,
+ network_bss_akm_names,
+ L_ARRAY_SIZE(network_bss_akm_names));
+ network_bss_append_suite_names(builder, "Pairwise",
+ info.pairwise_ciphers,
+ network_bss_pairwise_names,
+ L_ARRAY_SIZE(network_bss_pairwise_names));
+
+ group = network_bss_group_cipher_name(info.group_cipher);
+ dbus_append_dict_basic(builder, "Group", 's', group);
+
+ l_dbus_message_builder_leave_array(builder);
+
+ return true;
+}
+
static void setup_bss_interface(struct l_dbus_interface *interface) {
l_dbus_interface_property(interface, "Address", 0, "s", network_bss_property_get_address, NULL);
+ l_dbus_interface_property(interface, "RSN", 0, "a{sv}",
+ network_bss_property_get_rsn, NULL);
}

static int network_init(void)

Signature · 1 line
2.55.0
#4 lore raw

PATCH 2/3 doc: document BasicServiceSet RSN property

In reply to Алексей

Describe the optional RSN dictionary and its KeyMgmt, Pairwise, and Group entries.

---
 doc/basic-service-set.txt | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
doc/basic-service-set.txt +23 -0
index f827ca02..b4f2486f 100644
@@ -8,3 +8,26 @@ Object path /net/connman/iwd/{phy0,phy1,...}/{1,2,...}/Xxx
 Properties string Address [readonly]
 
  MAC address of BSS
+
+ dict RSN [readonly, optional]
+
+ Security capabilities advertised in the RSN information
+ element. The dictionary contains the following entries:
+
+ array(string) KeyMgmt
+
+ Key management suites. Values use the same names as
+ the wpa_supplicant D-Bus BSS.RSN property, such as
+ "wpa-psk", "wpa-ft-psk", "sae", "ft-sae",
+ "wpa-eap", and "owe".
+
+ array(string) Pairwise
+
+ Pairwise ciphers. Possible values are "tkip",
+ "ccmp", "gcmp", "ccmp-256", and "gcmp-256".
+
+ string Group
+
+ Group cipher. Possible values are "wep40", "wep104",
+ "tkip", "ccmp", "gcmp", "ccmp-256", and
+ "gcmp-256".
Signature · 1 line
2.55.0
#5 lore raw

PATCH 3/3 auto-t: test BasicServiceSet RSN property

In reply to Алексей

Verify the exact D-Bus key management and cipher capabilities for WPA2-PSK, SAE, and transition BSSes.

---
 .../basic_service_set_rsn_test.py             | 70 +++++++++++++++++++
 autotests/testBasicServiceSetRSN/hw.conf      |  9 +++
 autotests/testBasicServiceSetRSN/ssidSAE.conf | 11 +++
 .../ssidTransition.conf                       | 12 ++++
 .../testBasicServiceSetRSN/ssidWPA2.conf      |  8 +++
 5 files changed, 110 insertions(+)
 create mode 100644
autotests/testBasicServiceSetRSN/basic_service_set_rsn_test.py
 create mode 100644 autotests/testBasicServiceSetRSN/hw.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidSAE.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidTransition.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidWPA2.conf
(quoted diff) +0 -0

b/autotests/testBasicServiceSetRSN/basic_service_set_rsn_test.py new file mode 100644
index 00000000..20e0b62c

autotests/testBasicServiceSetRSN/basic_service_set_rsn_test.py new file +70 -0

70 lines not on this page · raw message

(quoted diff) +0 -0

b/autotests/testBasicServiceSetRSN/hw.conf
new file mode 100644
index 00000000..a43062d9

autotests/testBasicServiceSetRSN/hw.conf new file +9 -0
@@ -0,0 +1,9 @@
+[SETUP]
+num_radios=4
+hwsim_medium=yes
+start_iwd=no
+
+[HOSTAPD]
+rad0=ssidWPA2.conf
+rad1=ssidSAE.conf
+rad2=ssidTransition.conf
(quoted diff) +0 -0

b/autotests/testBasicServiceSetRSN/ssidSAE.conf
new file mode 100644
index 00000000..2ec9ae61

autotests/testBasicServiceSetRSN/ssidSAE.conf new file +11 -0
@@ -0,0 +1,11 @@
+hw_mode=g
+channel=2
+ssid=ssidSAE
+
+wpa=2
+wpa_key_mgmt=SAE
+wpa_pairwise=CCMP
+sae_password=secret123
+sae_groups=19
+ieee80211w=2
+sae_pwe=0
(quoted diff) +0 -0

b/autotests/testBasicServiceSetRSN/ssidTransition.conf new file mode 100644
index 00000000..00170772

autotests/testBasicServiceSetRSN/ssidTransition.conf new file +12 -0
@@ -0,0 +1,12 @@
+hw_mode=g
+channel=3
+ssid=ssidTransition
+
+wpa=2
+wpa_key_mgmt=WPA-PSK SAE
+wpa_pairwise=CCMP
+wpa_passphrase=secret123
+sae_password=secret123
+sae_groups=19
+ieee80211w=1
+sae_pwe=0
(quoted diff) +0 -0

b/autotests/testBasicServiceSetRSN/ssidWPA2.conf
new file mode 100644
index 00000000..d706142b

autotests/testBasicServiceSetRSN/ssidWPA2.conf new file +8 -0
@@ -0,0 +1,8 @@
+hw_mode=g
+channel=1
+ssid=ssidWPA2
+
+wpa=2
+wpa_key_mgmt=WPA-PSK
+wpa_pairwise=CCMP
+wpa_passphrase=secret123
Signature · 1 line
2.55.0
author
#6 lore raw

v2 cover dbus: expose per-BSS RSN capabilities

In reply to James Prestwood

From: Aleks Man <[email protected]>

James, thanks for confirming that extending BasicServiceSet is a reasonable approach.

Network.Type intentionally represents WPA2-Personal, WPA3-Personal, and transition networks as "psk". This series exposes the RSN information that iwd already retains for each BSS so consumers can distinguish those networks without changing the existing Network API.

The optional BasicServiceSet.RSN dictionary uses the same KeyMgmt, Pairwise, and Group fields and values as wpa_supplicant's BSS.RSN property. NetworkManager can therefore reuse its existing parser rather than add another translation layer. Older consumers ignore the optional property, and consumers can retain their Network.Type fallback for older iwd versions.

A corresponding NetworkManager series reads and combines the per-BSS capabilities for its synthetic ESS access point. It will be submitted separately after this API series.

Validation:

- iwd builds successfully with make -j4.
- On a physical WPA2/WPA3 transition network, an initial PSK connection was reported as WPA2. After changing the saved profile to SAE, a disconnect/reconnect through the stock Plasma applet completed without a password prompt and was reported as WPA3-SAE.
- Cycling Wi-Fi subsequently auto-connected the SAE profile and kept it visible as connected in the applet.

The new QEMU auto-t test was not run locally because /dev/kvm is not available on the test machine.

Changes in v2:
- Resend with git-send-email after the Gmail web client corrupted the inline v1 patches. There are no code changes.

RFC:
https://lore.kernel.org/iwd/CAMq8zouhDMRW4Q7qSpMCFftp21wCzuC%[email protected]/

Related NetworkManager issues:
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1489
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1693

Aleks Man (3):
network: expose RSN capabilities on BSS
doc: document BasicServiceSet RSN property
auto-t: test BasicServiceSet RSN property

 .../basic_service_set_rsn_test.py             |  70 +++++++++++
 autotests/testBasicServiceSetRSN/hw.conf      |   9 ++
 autotests/testBasicServiceSetRSN/ssidSAE.conf |  11 ++
 .../ssidTransition.conf                       |  12 ++
 .../testBasicServiceSetRSN/ssidWPA2.conf      |   8 ++
 doc/basic-service-set.txt                     |  23 ++++
 src/network.c                                 | 110 ++++++++++++++++++
 7 files changed, 243 insertions(+)
 create mode 100644 autotests/testBasicServiceSetRSN/basic_service_set_rsn_test.py
 create mode 100644 autotests/testBasicServiceSetRSN/hw.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidSAE.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidTransition.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidWPA2.conf
Signature · 1 line
2.55.0
author
#7 lore raw

PATCH v2 1/3 network: expose RSN capabilities on BSS

From: Aleks Man <[email protected]>

Network.Type intentionally groups all personal networks as "psk". Consumers therefore cannot distinguish PSK, SAE, and transition BSSes even though scan results retain the RSN IE.

Add an optional RSN dictionary to BasicServiceSet with the advertised key management suites and pairwise and group ciphers. Use the same field and value names as wpa_supplicant's BSS.RSN property so consumers can share parsing logic.

Parse bss->rsne directly so legacy WPA capabilities are not exposed as RSN.

---
 src/network.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)
src/network.c +110 -0
index a5a2375a..aa9e1281 100644
@@ -2198,10 +2198,120 @@ static bool network_bss_property_get_address(struct l_dbus *dbus,
 	return true;
 }
 
+struct network_bss_suite_name {
+	uint32_t suite;
+	const char *name;
+};
+
+static const struct network_bss_suite_name network_bss_akm_names[] = {
+	{ IE_RSN_AKM_SUITE_PSK, "wpa-psk" },
+	{ IE_RSN_AKM_SUITE_FT_USING_PSK, "wpa-ft-psk" },
+	{ IE_RSN_AKM_SUITE_PSK_SHA256, "wpa-psk-sha256" },
+	{ IE_RSN_AKM_SUITE_8021X, "wpa-eap" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_8021X, "wpa-ft-eap" },
+	{ IE_RSN_AKM_SUITE_8021X_SHA256, "wpa-eap-sha256" },
+	{ IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA256, "wpa-eap-suite-b" },
+	{ IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA384, "wpa-eap-suite-b-192" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_8021X_SHA384, "wpa-ft-eap-sha384" },
+	{ IE_RSN_AKM_SUITE_FILS_SHA256, "wpa-fils-sha256" },
+	{ IE_RSN_AKM_SUITE_FILS_SHA384, "wpa-fils-sha384" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256, "wpa-ft-fils-sha256" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384, "wpa-ft-fils-sha384" },
+	{ IE_RSN_AKM_SUITE_SAE_SHA256, "sae" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256, "ft-sae" },
+	{ IE_RSN_AKM_SUITE_OWE, "owe" },
+};
+
+static const struct network_bss_suite_name network_bss_pairwise_names[] = {
+	{ IE_RSN_CIPHER_SUITE_TKIP, "tkip" },
+	{ IE_RSN_CIPHER_SUITE_CCMP, "ccmp" },
+	{ IE_RSN_CIPHER_SUITE_GCMP, "gcmp" },
+	{ IE_RSN_CIPHER_SUITE_CCMP_256, "ccmp-256" },
+	{ IE_RSN_CIPHER_SUITE_GCMP_256, "gcmp-256" },
+};
+
+static const struct network_bss_suite_name network_bss_group_names[] = {
+	{ IE_RSN_CIPHER_SUITE_WEP40, "wep40" },
+	{ IE_RSN_CIPHER_SUITE_WEP104, "wep104" },
+	{ IE_RSN_CIPHER_SUITE_TKIP, "tkip" },
+	{ IE_RSN_CIPHER_SUITE_CCMP, "ccmp" },
+	{ IE_RSN_CIPHER_SUITE_GCMP, "gcmp" },
+	{ IE_RSN_CIPHER_SUITE_CCMP_256, "ccmp-256" },
+	{ IE_RSN_CIPHER_SUITE_GCMP_256, "gcmp-256" },
+};
+
+static void network_bss_append_suite_names(
+					struct l_dbus_message_builder *builder,
+					const char *name, uint32_t suites,
+					const struct network_bss_suite_name *names,
+					size_t names_len)
+{
+	size_t i;
+
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', name);
+	l_dbus_message_builder_enter_variant(builder, "as");
+	l_dbus_message_builder_enter_array(builder, "s");
+
+	for (i = 0; i < names_len; i++)
+		if (suites & names[i].suite)
+			l_dbus_message_builder_append_basic(builder, 's',
+								names[i].name);
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+}
+
+static const char *network_bss_group_cipher_name(
+					enum ie_rsn_cipher_suite cipher)
+{
+	size_t i;
+
+	for (i = 0; i < L_ARRAY_SIZE(network_bss_group_names); i++)
+		if (network_bss_group_names[i].suite == cipher)
+			return network_bss_group_names[i].name;
+
+	return "";
+}
+
+static bool network_bss_property_get_rsn(struct l_dbus *dbus,
+					struct l_dbus_message *message,
+					struct l_dbus_message_builder *builder,
+					void *user_data)
+{
+	struct scan_bss *bss = user_data;
+	struct ie_rsn_info info;
+	const char *group;
+
+	if (!bss->rsne || ie_parse_rsne_from_data(bss->rsne, bss->rsne[1] + 2,
+								&info) < 0)
+		return false;
+
+	l_dbus_message_builder_enter_array(builder, "{sv}");
+
+	network_bss_append_suite_names(builder, "KeyMgmt", info.akm_suites,
+					network_bss_akm_names,
+					L_ARRAY_SIZE(network_bss_akm_names));
+	network_bss_append_suite_names(builder, "Pairwise",
+					info.pairwise_ciphers,
+					network_bss_pairwise_names,
+					L_ARRAY_SIZE(network_bss_pairwise_names));
+
+	group = network_bss_group_cipher_name(info.group_cipher);
+	dbus_append_dict_basic(builder, "Group", 's', group);
+
+	l_dbus_message_builder_leave_array(builder);
+
+	return true;
+}
+
 static void setup_bss_interface(struct l_dbus_interface *interface)
 {
 	l_dbus_interface_property(interface, "Address", 0, "s",
 					network_bss_property_get_address, NULL);
+	l_dbus_interface_property(interface, "RSN", 0, "a{sv}",
+					network_bss_property_get_rsn, NULL);
 }
 
 static int network_init(void)
Signature · 1 line
2.55.0
author
#8 lore raw

PATCH v2 2/3 doc: document BasicServiceSet RSN property

In reply to Aleksman4o

From: Aleks Man <[email protected]>

Describe the optional RSN dictionary and its KeyMgmt, Pairwise, and Group entries.

---
 doc/basic-service-set.txt | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
doc/basic-service-set.txt +23 -0
index f827ca02..b4f2486f 100644
@@ -8,3 +8,26 @@ Object path	/net/connman/iwd/{phy0,phy1,...}/{1,2,...}/Xxx
 Properties	string Address [readonly]
 
 			MAC address of BSS
+
+		dict RSN [readonly, optional]
+
+			Security capabilities advertised in the RSN information
+			element. The dictionary contains the following entries:
+
+			array(string) KeyMgmt
+
+				Key management suites. Values use the same names as
+				the wpa_supplicant D-Bus BSS.RSN property, such as
+				"wpa-psk", "wpa-ft-psk", "sae", "ft-sae",
+				"wpa-eap", and "owe".
+
+			array(string) Pairwise
+
+				Pairwise ciphers. Possible values are "tkip",
+				"ccmp", "gcmp", "ccmp-256", and "gcmp-256".
+
+			string Group
+
+				Group cipher. Possible values are "wep40", "wep104",
+				"tkip", "ccmp", "gcmp", "ccmp-256", and
+				"gcmp-256".
Signature · 1 line
2.55.0
author
#9 lore raw

PATCH v2 3/3 auto-t: test BasicServiceSet RSN property

In reply to Aleksman4o

From: Aleks Man <[email protected]>

Verify the exact D-Bus key management and cipher capabilities for WPA2-PSK, SAE, and transition BSSes.

---
 .../basic_service_set_rsn_test.py             | 70 +++++++++++++++++++
 autotests/testBasicServiceSetRSN/hw.conf      |  9 +++
 autotests/testBasicServiceSetRSN/ssidSAE.conf | 11 +++
 .../ssidTransition.conf                       | 12 ++++
 .../testBasicServiceSetRSN/ssidWPA2.conf      |  8 +++
 5 files changed, 110 insertions(+)
 create mode 100644 autotests/testBasicServiceSetRSN/basic_service_set_rsn_test.py
 create mode 100644 autotests/testBasicServiceSetRSN/hw.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidSAE.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidTransition.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidWPA2.conf
autotests/testBasicServiceSetRSN/basic_service_set_rsn_test.py new file +70 -0
new file mode 100644
index 00000000..20e0b62c
@@ -0,0 +1,70 @@
+#! /usr/bin/python3
+
+import unittest
+import sys
+
+import dbus
+
+sys.path.append('../util')
+import iwd
+from iwd import IWD
+from hwsim import Hwsim
+from hostapd import HostapdCLI
+
+BSS_INTERFACE = 'net.connman.iwd.BasicServiceSet'
+
+
+class Test(unittest.TestCase):
+    def _get_rsn(self, device, ssid):
+        ordered_network = device.get_ordered_network(ssid, full_scan=True)
+        network = ordered_network.network_object
+        self.assertEqual(len(network.extended_service_set), 1)
+
+        path = network.extended_service_set[0]
+        proxy = self.wd._bus.get_object(iwd.IWD_SERVICE, path)
+        properties = dbus.Interface(proxy, iwd.DBUS_PROPERTIES)
+
+        return properties.GetAll(BSS_INTERFACE)['RSN']
+
+    def test_rsn(self):
+        device = self.wd.list_devices(1)[0]
+
+        rsn = self._get_rsn(device, 'ssidWPA2')
+        self.assertEqual(set(map(str, rsn['KeyMgmt'])), {'wpa-psk'})
+        self.assertEqual(set(map(str, rsn['Pairwise'])), {'ccmp'})
+        self.assertEqual(str(rsn['Group']), 'ccmp')
+
+        rsn = self._get_rsn(device, 'ssidSAE')
+        self.assertEqual(set(map(str, rsn['KeyMgmt'])), {'sae'})
+        self.assertEqual(set(map(str, rsn['Pairwise'])), {'ccmp'})
+        self.assertEqual(str(rsn['Group']), 'ccmp')
+
+        rsn = self._get_rsn(device, 'ssidTransition')
+        self.assertEqual(set(map(str, rsn['KeyMgmt'])),
+                         {'wpa-psk', 'sae'})
+        self.assertEqual(set(map(str, rsn['Pairwise'])), {'ccmp'})
+        self.assertEqual(str(rsn['Group']), 'ccmp')
+
+    def setUp(self):
+        self.wd = IWD(True)
+
+    def tearDown(self):
+        self.wd.stop()
+        self.wd = None
+
+    @classmethod
+    def setUpClass(cls):
+        Hwsim()
+        cls.hostapd = [
+            HostapdCLI(config='ssidWPA2.conf'),
+            HostapdCLI(config='ssidSAE.conf'),
+            HostapdCLI(config='ssidTransition.conf'),
+        ]
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.hostapd = None
+
+
+if __name__ == '__main__':
+    unittest.main(exit=True)
autotests/testBasicServiceSetRSN/hw.conf new file +9 -0
new file mode 100644
index 00000000..a43062d9
@@ -0,0 +1,9 @@
+[SETUP]
+num_radios=4
+hwsim_medium=yes
+start_iwd=no
+
+[HOSTAPD]
+rad0=ssidWPA2.conf
+rad1=ssidSAE.conf
+rad2=ssidTransition.conf
autotests/testBasicServiceSetRSN/ssidSAE.conf new file +11 -0
new file mode 100644
index 00000000..2ec9ae61
@@ -0,0 +1,11 @@
+hw_mode=g
+channel=2
+ssid=ssidSAE
+
+wpa=2
+wpa_key_mgmt=SAE
+wpa_pairwise=CCMP
+sae_password=secret123
+sae_groups=19
+ieee80211w=2
+sae_pwe=0
autotests/testBasicServiceSetRSN/ssidTransition.conf new file +12 -0
new file mode 100644
index 00000000..00170772
@@ -0,0 +1,12 @@
+hw_mode=g
+channel=3
+ssid=ssidTransition
+
+wpa=2
+wpa_key_mgmt=WPA-PSK SAE
+wpa_pairwise=CCMP
+wpa_passphrase=secret123
+sae_password=secret123
+sae_groups=19
+ieee80211w=1
+sae_pwe=0
autotests/testBasicServiceSetRSN/ssidWPA2.conf new file +8 -0
new file mode 100644
index 00000000..d706142b
@@ -0,0 +1,8 @@
+hw_mode=g
+channel=1
+ssid=ssidWPA2
+
+wpa=2
+wpa_key_mgmt=WPA-PSK
+wpa_pairwise=CCMP
+wpa_passphrase=secret123
Signature · 1 line
2.55.0
author
#10 lore raw

v3 cover dbus: expose per-BSS RSN capabilities

In reply to Aleksman4o

From: Aleks Man <[email protected]>

James, thanks for confirming that extending BasicServiceSet is a reasonable approach.

Network.Type intentionally represents WPA2-Personal, WPA3-Personal, and transition networks as "psk". This series exposes the RSN information that iwd already retains for each BSS so consumers can distinguish those networks without changing the existing Network API.

The optional BasicServiceSet.RSN dictionary uses the same KeyMgmt, Pairwise, and Group fields and values as wpa_supplicant's BSS.RSN property. NetworkManager can therefore reuse its existing parser rather than add another translation layer. Older consumers ignore the optional property, and consumers can retain their Network.Type fallback for older iwd versions.

A corresponding NetworkManager series reads and combines the per-BSS capabilities for its synthetic ESS access point. It will be submitted separately after this API series.

Validation:

- iwd builds successfully with make -j4.
- On a physical WPA2/WPA3 transition network, an initial PSK connection was reported as WPA2. After changing the saved profile to SAE, a disconnect/reconnect through the stock Plasma applet completed without a password prompt and was reported as WPA3-SAE.
- Cycling Wi-Fi subsequently auto-connected the SAE profile and kept it visible as connected in the applet.
- The upstream CI auto-t runner passed the v2 series.

Changes in v3:
- Shorten the auto-t filename so the generated patch metadata complies with GitLint's 80-column limit. There are no code changes.

Changes in v2:
- Resend with git-send-email after the Gmail web client corrupted the inline v1 patches. There are no code changes.

RFC:
https://lore.kernel.org/iwd/CAMq8zouhDMRW4Q7qSpMCFftp21wCzuC%[email protected]/

Related NetworkManager issues:
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1489
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1693

Aleks Man (3):
network: expose RSN capabilities on BSS
doc: document BasicServiceSet RSN property
auto-t: test BasicServiceSet RSN property

 .../testBasicServiceSetRSN/bss_rsn_test.py    |  70 +++++++++++
 autotests/testBasicServiceSetRSN/hw.conf      |   9 ++
 autotests/testBasicServiceSetRSN/ssidSAE.conf |  11 ++
 .../ssidTransition.conf                       |  12 ++
 .../testBasicServiceSetRSN/ssidWPA2.conf      |   8 ++
 doc/basic-service-set.txt                     |  23 ++++
 src/network.c                                 | 110 ++++++++++++++++++
 7 files changed, 243 insertions(+)
 create mode 100644 autotests/testBasicServiceSetRSN/bss_rsn_test.py
 create mode 100644 autotests/testBasicServiceSetRSN/hw.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidSAE.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidTransition.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidWPA2.conf
Signature · 1 line
2.55.0
author
#11 lore raw

PATCH v3 1/3 network: expose RSN capabilities on BSS

From: Aleks Man <[email protected]>

Network.Type intentionally groups all personal networks as "psk". Consumers therefore cannot distinguish PSK, SAE, and transition BSSes even though scan results retain the RSN IE.

Add an optional RSN dictionary to BasicServiceSet with the advertised key management suites and pairwise and group ciphers. Use the same field and value names as wpa_supplicant's BSS.RSN property so consumers can share parsing logic.

Parse bss->rsne directly so legacy WPA capabilities are not exposed as RSN.

---
 src/network.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)
src/network.c +110 -0
index a5a2375a..aa9e1281 100644
@@ -2198,10 +2198,120 @@ static bool network_bss_property_get_address(struct l_dbus *dbus,
 	return true;
 }
 
+struct network_bss_suite_name {
+	uint32_t suite;
+	const char *name;
+};
+
+static const struct network_bss_suite_name network_bss_akm_names[] = {
+	{ IE_RSN_AKM_SUITE_PSK, "wpa-psk" },
+	{ IE_RSN_AKM_SUITE_FT_USING_PSK, "wpa-ft-psk" },
+	{ IE_RSN_AKM_SUITE_PSK_SHA256, "wpa-psk-sha256" },
+	{ IE_RSN_AKM_SUITE_8021X, "wpa-eap" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_8021X, "wpa-ft-eap" },
+	{ IE_RSN_AKM_SUITE_8021X_SHA256, "wpa-eap-sha256" },
+	{ IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA256, "wpa-eap-suite-b" },
+	{ IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA384, "wpa-eap-suite-b-192" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_8021X_SHA384, "wpa-ft-eap-sha384" },
+	{ IE_RSN_AKM_SUITE_FILS_SHA256, "wpa-fils-sha256" },
+	{ IE_RSN_AKM_SUITE_FILS_SHA384, "wpa-fils-sha384" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256, "wpa-ft-fils-sha256" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384, "wpa-ft-fils-sha384" },
+	{ IE_RSN_AKM_SUITE_SAE_SHA256, "sae" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256, "ft-sae" },
+	{ IE_RSN_AKM_SUITE_OWE, "owe" },
+};
+
+static const struct network_bss_suite_name network_bss_pairwise_names[] = {
+	{ IE_RSN_CIPHER_SUITE_TKIP, "tkip" },
+	{ IE_RSN_CIPHER_SUITE_CCMP, "ccmp" },
+	{ IE_RSN_CIPHER_SUITE_GCMP, "gcmp" },
+	{ IE_RSN_CIPHER_SUITE_CCMP_256, "ccmp-256" },
+	{ IE_RSN_CIPHER_SUITE_GCMP_256, "gcmp-256" },
+};
+
+static const struct network_bss_suite_name network_bss_group_names[] = {
+	{ IE_RSN_CIPHER_SUITE_WEP40, "wep40" },
+	{ IE_RSN_CIPHER_SUITE_WEP104, "wep104" },
+	{ IE_RSN_CIPHER_SUITE_TKIP, "tkip" },
+	{ IE_RSN_CIPHER_SUITE_CCMP, "ccmp" },
+	{ IE_RSN_CIPHER_SUITE_GCMP, "gcmp" },
+	{ IE_RSN_CIPHER_SUITE_CCMP_256, "ccmp-256" },
+	{ IE_RSN_CIPHER_SUITE_GCMP_256, "gcmp-256" },
+};
+
+static void network_bss_append_suite_names(
+					struct l_dbus_message_builder *builder,
+					const char *name, uint32_t suites,
+					const struct network_bss_suite_name *names,
+					size_t names_len)
+{
+	size_t i;
+
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', name);
+	l_dbus_message_builder_enter_variant(builder, "as");
+	l_dbus_message_builder_enter_array(builder, "s");
+
+	for (i = 0; i < names_len; i++)
+		if (suites & names[i].suite)
+			l_dbus_message_builder_append_basic(builder, 's',
+								names[i].name);
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+}
+
+static const char *network_bss_group_cipher_name(
+					enum ie_rsn_cipher_suite cipher)
+{
+	size_t i;
+
+	for (i = 0; i < L_ARRAY_SIZE(network_bss_group_names); i++)
+		if (network_bss_group_names[i].suite == cipher)
+			return network_bss_group_names[i].name;
+
+	return "";
+}
+
+static bool network_bss_property_get_rsn(struct l_dbus *dbus,
+					struct l_dbus_message *message,
+					struct l_dbus_message_builder *builder,
+					void *user_data)
+{
+	struct scan_bss *bss = user_data;
+	struct ie_rsn_info info;
+	const char *group;
+
+	if (!bss->rsne || ie_parse_rsne_from_data(bss->rsne, bss->rsne[1] + 2,
+								&info) < 0)
+		return false;
+
+	l_dbus_message_builder_enter_array(builder, "{sv}");
+
+	network_bss_append_suite_names(builder, "KeyMgmt", info.akm_suites,
+					network_bss_akm_names,
+					L_ARRAY_SIZE(network_bss_akm_names));
+	network_bss_append_suite_names(builder, "Pairwise",
+					info.pairwise_ciphers,
+					network_bss_pairwise_names,
+					L_ARRAY_SIZE(network_bss_pairwise_names));
+
+	group = network_bss_group_cipher_name(info.group_cipher);
+	dbus_append_dict_basic(builder, "Group", 's', group);
+
+	l_dbus_message_builder_leave_array(builder);
+
+	return true;
+}
+
 static void setup_bss_interface(struct l_dbus_interface *interface)
 {
 	l_dbus_interface_property(interface, "Address", 0, "s",
 					network_bss_property_get_address, NULL);
+	l_dbus_interface_property(interface, "RSN", 0, "a{sv}",
+					network_bss_property_get_rsn, NULL);
 }
 
 static int network_init(void)
Signature · 1 line
2.55.0
author
#12 lore raw

PATCH v3 2/3 doc: document BasicServiceSet RSN property

In reply to Aleksman4o

From: Aleks Man <[email protected]>

Describe the optional RSN dictionary and its KeyMgmt, Pairwise, and Group entries.

---
 doc/basic-service-set.txt | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
doc/basic-service-set.txt +23 -0
index f827ca02..b4f2486f 100644
@@ -8,3 +8,26 @@ Object path	/net/connman/iwd/{phy0,phy1,...}/{1,2,...}/Xxx
 Properties	string Address [readonly]
 
 			MAC address of BSS
+
+		dict RSN [readonly, optional]
+
+			Security capabilities advertised in the RSN information
+			element. The dictionary contains the following entries:
+
+			array(string) KeyMgmt
+
+				Key management suites. Values use the same names as
+				the wpa_supplicant D-Bus BSS.RSN property, such as
+				"wpa-psk", "wpa-ft-psk", "sae", "ft-sae",
+				"wpa-eap", and "owe".
+
+			array(string) Pairwise
+
+				Pairwise ciphers. Possible values are "tkip",
+				"ccmp", "gcmp", "ccmp-256", and "gcmp-256".
+
+			string Group
+
+				Group cipher. Possible values are "wep40", "wep104",
+				"tkip", "ccmp", "gcmp", "ccmp-256", and
+				"gcmp-256".
Signature · 1 line
2.55.0
author
#13 lore raw

PATCH v3 3/3 auto-t: test BasicServiceSet RSN property

In reply to Aleksman4o

From: Aleks Man <[email protected]>

Verify the exact D-Bus key management and cipher capabilities for WPA2-PSK, SAE, and transition BSSes.

---
 .../testBasicServiceSetRSN/bss_rsn_test.py    | 70 +++++++++++++++++++
 autotests/testBasicServiceSetRSN/hw.conf      |  9 +++
 autotests/testBasicServiceSetRSN/ssidSAE.conf | 11 +++
 .../ssidTransition.conf                       | 12 ++++
 .../testBasicServiceSetRSN/ssidWPA2.conf      |  8 +++
 5 files changed, 110 insertions(+)
 create mode 100644 autotests/testBasicServiceSetRSN/bss_rsn_test.py
 create mode 100644 autotests/testBasicServiceSetRSN/hw.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidSAE.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidTransition.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidWPA2.conf
autotests/testBasicServiceSetRSN/bss_rsn_test.py new file +70 -0
new file mode 100644
index 00000000..20e0b62c
@@ -0,0 +1,70 @@
+#! /usr/bin/python3
+
+import unittest
+import sys
+
+import dbus
+
+sys.path.append('../util')
+import iwd
+from iwd import IWD
+from hwsim import Hwsim
+from hostapd import HostapdCLI
+
+BSS_INTERFACE = 'net.connman.iwd.BasicServiceSet'
+
+
+class Test(unittest.TestCase):
+    def _get_rsn(self, device, ssid):
+        ordered_network = device.get_ordered_network(ssid, full_scan=True)
+        network = ordered_network.network_object
+        self.assertEqual(len(network.extended_service_set), 1)
+
+        path = network.extended_service_set[0]
+        proxy = self.wd._bus.get_object(iwd.IWD_SERVICE, path)
+        properties = dbus.Interface(proxy, iwd.DBUS_PROPERTIES)
+
+        return properties.GetAll(BSS_INTERFACE)['RSN']
+
+    def test_rsn(self):
+        device = self.wd.list_devices(1)[0]
+
+        rsn = self._get_rsn(device, 'ssidWPA2')
+        self.assertEqual(set(map(str, rsn['KeyMgmt'])), {'wpa-psk'})
+        self.assertEqual(set(map(str, rsn['Pairwise'])), {'ccmp'})
+        self.assertEqual(str(rsn['Group']), 'ccmp')
+
+        rsn = self._get_rsn(device, 'ssidSAE')
+        self.assertEqual(set(map(str, rsn['KeyMgmt'])), {'sae'})
+        self.assertEqual(set(map(str, rsn['Pairwise'])), {'ccmp'})
+        self.assertEqual(str(rsn['Group']), 'ccmp')
+
+        rsn = self._get_rsn(device, 'ssidTransition')
+        self.assertEqual(set(map(str, rsn['KeyMgmt'])),
+                         {'wpa-psk', 'sae'})
+        self.assertEqual(set(map(str, rsn['Pairwise'])), {'ccmp'})
+        self.assertEqual(str(rsn['Group']), 'ccmp')
+
+    def setUp(self):
+        self.wd = IWD(True)
+
+    def tearDown(self):
+        self.wd.stop()
+        self.wd = None
+
+    @classmethod
+    def setUpClass(cls):
+        Hwsim()
+        cls.hostapd = [
+            HostapdCLI(config='ssidWPA2.conf'),
+            HostapdCLI(config='ssidSAE.conf'),
+            HostapdCLI(config='ssidTransition.conf'),
+        ]
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.hostapd = None
+
+
+if __name__ == '__main__':
+    unittest.main(exit=True)
autotests/testBasicServiceSetRSN/hw.conf new file +9 -0
new file mode 100644
index 00000000..a43062d9
@@ -0,0 +1,9 @@
+[SETUP]
+num_radios=4
+hwsim_medium=yes
+start_iwd=no
+
+[HOSTAPD]
+rad0=ssidWPA2.conf
+rad1=ssidSAE.conf
+rad2=ssidTransition.conf
autotests/testBasicServiceSetRSN/ssidSAE.conf new file +11 -0
new file mode 100644
index 00000000..2ec9ae61
@@ -0,0 +1,11 @@
+hw_mode=g
+channel=2
+ssid=ssidSAE
+
+wpa=2
+wpa_key_mgmt=SAE
+wpa_pairwise=CCMP
+sae_password=secret123
+sae_groups=19
+ieee80211w=2
+sae_pwe=0
autotests/testBasicServiceSetRSN/ssidTransition.conf new file +12 -0
new file mode 100644
index 00000000..00170772
@@ -0,0 +1,12 @@
+hw_mode=g
+channel=3
+ssid=ssidTransition
+
+wpa=2
+wpa_key_mgmt=WPA-PSK SAE
+wpa_pairwise=CCMP
+wpa_passphrase=secret123
+sae_password=secret123
+sae_groups=19
+ieee80211w=1
+sae_pwe=0
autotests/testBasicServiceSetRSN/ssidWPA2.conf new file +8 -0
new file mode 100644
index 00000000..d706142b
@@ -0,0 +1,8 @@
+hw_mode=g
+channel=1
+ssid=ssidWPA2
+
+wpa=2
+wpa_key_mgmt=WPA-PSK
+wpa_pairwise=CCMP
+wpa_passphrase=secret123
Signature · 1 line
2.55.0
author
#14 lore raw

Re: [PATCH v3 0/3] dbus: expose per-BSS RSN capabilities

In reply to Aleksman4o

From: Aleks Man <[email protected]>

Hi James,

A gentle follow-up on this series, which has now been pending for two weeks. All iwd CI checks passed. The corresponding NetworkManager MR is currently blocked on the proposed iwd D-Bus API:

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2464

Would you have time to review the API approach, or is there anything else you would like changed or tested?

Thanks,
Aleks

#15 lore raw

Re: [PATCH v3 0/3] dbus: expose per-BSS RSN capabilities

In reply to Aleksman4o

Hi Aleks,

Quoted 60 lines · On 7/14/26 3:48 PM, Aleksman4o wrote:

From: Aleks Man <[email protected]>

James, thanks for confirming that extending BasicServiceSet is a reasonable approach.

Network.Type intentionally represents WPA2-Personal, WPA3-Personal, and transition networks as "psk". This series exposes the RSN information that iwd already retains for each BSS so consumers can distinguish those networks without changing the existing Network API.

The optional BasicServiceSet.RSN dictionary uses the same KeyMgmt, Pairwise, and Group fields and values as wpa_supplicant's BSS.RSN property. NetworkManager can therefore reuse its existing parser rather than add another translation layer. Older consumers ignore the optional property, and consumers can retain their Network.Type fallback for older iwd versions.

A corresponding NetworkManager series reads and combines the per-BSS capabilities for its synthetic ESS access point. It will be submitted separately after this API series.

Validation:

- iwd builds successfully with make -j4.
- On a physical WPA2/WPA3 transition network, an initial PSK connection was reported as WPA2. After changing the saved profile to SAE, a disconnect/reconnect through the stock Plasma applet completed without a password prompt and was reported as WPA3-SAE.
- Cycling Wi-Fi subsequently auto-connected the SAE profile and kept it visible as connected in the applet.
- The upstream CI auto-t runner passed the v2 series.

Changes in v3:
- Shorten the auto-t filename so the generated patch metadata complies with GitLint's 80-column limit. There are no code changes.

Changes in v2:
- Resend with git-send-email after the Gmail web client corrupted the inline v1 patches. There are no code changes.

RFC:
https://lore.kernel.org/iwd/CAMq8zouhDMRW4Q7qSpMCFftp21wCzuC%[email protected]/

Related NetworkManager issues:
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1489
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1693

Aleks Man (3):
network: expose RSN capabilities on BSS
doc: document BasicServiceSet RSN property
auto-t: test BasicServiceSet RSN property

  .../testBasicServiceSetRSN/bss_rsn_test.py    |  70 +++++++++++
  autotests/testBasicServiceSetRSN/hw.conf      |   9 ++
  autotests/testBasicServiceSetRSN/ssidSAE.conf |  11 ++
  .../ssidTransition.conf                       |  12 ++
  .../testBasicServiceSetRSN/ssidWPA2.conf      |   8 ++
  doc/basic-service-set.txt                     |  23 ++++
  src/network.c                                 | 110 ++++++++++++++++++
  7 files changed, 243 insertions(+)
Quoted 6 lines

create mode 100644 autotests/testBasicServiceSetRSN/bss_rsn_test.py create mode 100644 autotests/testBasicServiceSetRSN/hw.conf create mode 100644 autotests/testBasicServiceSetRSN/ssidSAE.conf create mode 100644 autotests/testBasicServiceSetRSN/ssidTransition.conf create mode 100644 autotests/testBasicServiceSetRSN/ssidWPA2.conf

Overall this looks good. It would be nice to have some support in iwctl to see these fields. Those properties and getters/updaters can be implemented here if possible:

https://git.kernel.org/pub/scm/network/wireless/iwd.git/tree/client/bss.c

We will need to wait for Denis to comment/merge though.

Thanks,

James

author
#16 lore raw

v4 cover dbus: expose per-BSS RSN capabilities

From: Aleks Man <[email protected]>

James, thanks for the review. This revision adds the requested iwctl support for displaying the new fields.

Network.Type intentionally represents WPA2-Personal, WPA3-Personal, and transition networks as "psk". This series exposes the RSN information that iwd already retains for each BSS so consumers can distinguish those networks without changing the existing Network API.

The optional BasicServiceSet.RSN dictionary uses the same KeyMgmt, Pairwise, and Group fields and values as wpa_supplicant's BSS.RSN property. NetworkManager can therefore reuse its existing parser rather than add another translation layer. Older consumers ignore the optional property, and consumers can retain their Network.Type fallback for older iwd versions.

The corresponding NetworkManager MR reads and combines the per-BSS capabilities for its synthetic ESS access point:
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2464

Validation:

- make check: 273 passed, 6 skipped, 0 failed.
- The installed patched iwd exposes the expected KeyMgmt, Pairwise, and Group values on a physical WPA2/WPA3 transition BSS.
- The new iwctl output displays the capabilities in the get-bsses table.
- An SAE profile auto-connects after restarting iwd and remains visible as connected in NetworkManager and the Plasma applet.
- A NetworkManager disconnect/reconnect completes without a password prompt and reconnects as WPA3-Personal with FT.
- The upstream CI suite passed all checks for v3.

Changes in v4:
- Add iwctl parsing and display of the BasicServiceSet RSN dictionary, as requested by James.
- Widen get-bsses property values so the capabilities remain readable while the table stays within 80 columns.
- No changes to patches 1-3.

Changes in v3:
- Shorten the auto-t filename so the generated patch metadata complies with GitLint's 80-column limit. There are no code changes.

Changes in v2:
- Resend with git-send-email after the Gmail web client corrupted the inline v1 patches. There are no code changes.

RFC:
https://lore.kernel.org/iwd/CAMq8zouhDMRW4Q7qSpMCFftp21wCzuC%[email protected]/

Related NetworkManager issues:
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1489
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1693

Aleks Man (5):
network: expose RSN capabilities on BSS
doc: document BasicServiceSet RSN property
auto-t: test BasicServiceSet RSN property
client: display BasicServiceSet RSN property
client: widen BSS property value column

 .../testBasicServiceSetRSN/bss_rsn_test.py    |  70 +++++++++++
 autotests/testBasicServiceSetRSN/hw.conf      |   9 ++
 autotests/testBasicServiceSetRSN/ssidSAE.conf |  11 ++
 .../ssidTransition.conf                       |  12 ++
 .../testBasicServiceSetRSN/ssidWPA2.conf      |   8 ++
 client/bss.c                                  |  76 ++++++++++++
 client/station.c                              |   4 +-
 doc/basic-service-set.txt                     |  23 ++++
 src/network.c                                 | 110 ++++++++++++++++++
 9 files changed, 321 insertions(+), 2 deletions(-)
 create mode 100644 autotests/testBasicServiceSetRSN/bss_rsn_test.py
 create mode 100644 autotests/testBasicServiceSetRSN/hw.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidSAE.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidTransition.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidWPA2.conf
Signature · 1 line
2.55.0
author
#17 lore raw

PATCH v4 1/5 network: expose RSN capabilities on BSS

From: Aleks Man <[email protected]>

Network.Type intentionally groups all personal networks as "psk". Consumers therefore cannot distinguish PSK, SAE, and transition BSSes even though scan results retain the RSN IE.

Add an optional RSN dictionary to BasicServiceSet with the advertised key management suites and pairwise and group ciphers. Use the same field and value names as wpa_supplicant's BSS.RSN property so consumers can share parsing logic.

Parse bss->rsne directly so legacy WPA capabilities are not exposed as RSN.

---
 src/network.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)
src/network.c +110 -0
index a5a2375a..aa9e1281 100644
@@ -2198,10 +2198,120 @@ static bool network_bss_property_get_address(struct l_dbus *dbus,
 	return true;
 }
 
+struct network_bss_suite_name {
+	uint32_t suite;
+	const char *name;
+};
+
+static const struct network_bss_suite_name network_bss_akm_names[] = {
+	{ IE_RSN_AKM_SUITE_PSK, "wpa-psk" },
+	{ IE_RSN_AKM_SUITE_FT_USING_PSK, "wpa-ft-psk" },
+	{ IE_RSN_AKM_SUITE_PSK_SHA256, "wpa-psk-sha256" },
+	{ IE_RSN_AKM_SUITE_8021X, "wpa-eap" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_8021X, "wpa-ft-eap" },
+	{ IE_RSN_AKM_SUITE_8021X_SHA256, "wpa-eap-sha256" },
+	{ IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA256, "wpa-eap-suite-b" },
+	{ IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA384, "wpa-eap-suite-b-192" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_8021X_SHA384, "wpa-ft-eap-sha384" },
+	{ IE_RSN_AKM_SUITE_FILS_SHA256, "wpa-fils-sha256" },
+	{ IE_RSN_AKM_SUITE_FILS_SHA384, "wpa-fils-sha384" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256, "wpa-ft-fils-sha256" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384, "wpa-ft-fils-sha384" },
+	{ IE_RSN_AKM_SUITE_SAE_SHA256, "sae" },
+	{ IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256, "ft-sae" },
+	{ IE_RSN_AKM_SUITE_OWE, "owe" },
+};
+
+static const struct network_bss_suite_name network_bss_pairwise_names[] = {
+	{ IE_RSN_CIPHER_SUITE_TKIP, "tkip" },
+	{ IE_RSN_CIPHER_SUITE_CCMP, "ccmp" },
+	{ IE_RSN_CIPHER_SUITE_GCMP, "gcmp" },
+	{ IE_RSN_CIPHER_SUITE_CCMP_256, "ccmp-256" },
+	{ IE_RSN_CIPHER_SUITE_GCMP_256, "gcmp-256" },
+};
+
+static const struct network_bss_suite_name network_bss_group_names[] = {
+	{ IE_RSN_CIPHER_SUITE_WEP40, "wep40" },
+	{ IE_RSN_CIPHER_SUITE_WEP104, "wep104" },
+	{ IE_RSN_CIPHER_SUITE_TKIP, "tkip" },
+	{ IE_RSN_CIPHER_SUITE_CCMP, "ccmp" },
+	{ IE_RSN_CIPHER_SUITE_GCMP, "gcmp" },
+	{ IE_RSN_CIPHER_SUITE_CCMP_256, "ccmp-256" },
+	{ IE_RSN_CIPHER_SUITE_GCMP_256, "gcmp-256" },
+};
+
+static void network_bss_append_suite_names(
+					struct l_dbus_message_builder *builder,
+					const char *name, uint32_t suites,
+					const struct network_bss_suite_name *names,
+					size_t names_len)
+{
+	size_t i;
+
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', name);
+	l_dbus_message_builder_enter_variant(builder, "as");
+	l_dbus_message_builder_enter_array(builder, "s");
+
+	for (i = 0; i < names_len; i++)
+		if (suites & names[i].suite)
+			l_dbus_message_builder_append_basic(builder, 's',
+								names[i].name);
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+}
+
+static const char *network_bss_group_cipher_name(
+					enum ie_rsn_cipher_suite cipher)
+{
+	size_t i;
+
+	for (i = 0; i < L_ARRAY_SIZE(network_bss_group_names); i++)
+		if (network_bss_group_names[i].suite == cipher)
+			return network_bss_group_names[i].name;
+
+	return "";
+}
+
+static bool network_bss_property_get_rsn(struct l_dbus *dbus,
+					struct l_dbus_message *message,
+					struct l_dbus_message_builder *builder,
+					void *user_data)
+{
+	struct scan_bss *bss = user_data;
+	struct ie_rsn_info info;
+	const char *group;
+
+	if (!bss->rsne || ie_parse_rsne_from_data(bss->rsne, bss->rsne[1] + 2,
+								&info) < 0)
+		return false;
+
+	l_dbus_message_builder_enter_array(builder, "{sv}");
+
+	network_bss_append_suite_names(builder, "KeyMgmt", info.akm_suites,
+					network_bss_akm_names,
+					L_ARRAY_SIZE(network_bss_akm_names));
+	network_bss_append_suite_names(builder, "Pairwise",
+					info.pairwise_ciphers,
+					network_bss_pairwise_names,
+					L_ARRAY_SIZE(network_bss_pairwise_names));
+
+	group = network_bss_group_cipher_name(info.group_cipher);
+	dbus_append_dict_basic(builder, "Group", 's', group);
+
+	l_dbus_message_builder_leave_array(builder);
+
+	return true;
+}
+
 static void setup_bss_interface(struct l_dbus_interface *interface)
 {
 	l_dbus_interface_property(interface, "Address", 0, "s",
 					network_bss_property_get_address, NULL);
+	l_dbus_interface_property(interface, "RSN", 0, "a{sv}",
+					network_bss_property_get_rsn, NULL);
 }
 
 static int network_init(void)
Signature · 1 line
2.55.0
author
#18 lore raw

PATCH v4 2/5 doc: document BasicServiceSet RSN property

In reply to Aleksman4o

From: Aleks Man <[email protected]>

Describe the optional RSN dictionary and its KeyMgmt, Pairwise, and Group entries.

---
 doc/basic-service-set.txt | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
doc/basic-service-set.txt +23 -0
index f827ca02..b4f2486f 100644
@@ -8,3 +8,26 @@ Object path	/net/connman/iwd/{phy0,phy1,...}/{1,2,...}/Xxx
 Properties	string Address [readonly]
 
 			MAC address of BSS
+
+		dict RSN [readonly, optional]
+
+			Security capabilities advertised in the RSN information
+			element. The dictionary contains the following entries:
+
+			array(string) KeyMgmt
+
+				Key management suites. Values use the same names as
+				the wpa_supplicant D-Bus BSS.RSN property, such as
+				"wpa-psk", "wpa-ft-psk", "sae", "ft-sae",
+				"wpa-eap", and "owe".
+
+			array(string) Pairwise
+
+				Pairwise ciphers. Possible values are "tkip",
+				"ccmp", "gcmp", "ccmp-256", and "gcmp-256".
+
+			string Group
+
+				Group cipher. Possible values are "wep40", "wep104",
+				"tkip", "ccmp", "gcmp", "ccmp-256", and
+				"gcmp-256".
Signature · 1 line
2.55.0
author
#19 lore raw

PATCH v4 3/5 auto-t: test BasicServiceSet RSN property

In reply to Aleksman4o

From: Aleks Man <[email protected]>

Verify the exact D-Bus key management and cipher capabilities for WPA2-PSK, SAE, and transition BSSes.

---
 .../testBasicServiceSetRSN/bss_rsn_test.py    | 70 +++++++++++++++++++
 autotests/testBasicServiceSetRSN/hw.conf      |  9 +++
 autotests/testBasicServiceSetRSN/ssidSAE.conf | 11 +++
 .../ssidTransition.conf                       | 12 ++++
 .../testBasicServiceSetRSN/ssidWPA2.conf      |  8 +++
 5 files changed, 110 insertions(+)
 create mode 100644 autotests/testBasicServiceSetRSN/bss_rsn_test.py
 create mode 100644 autotests/testBasicServiceSetRSN/hw.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidSAE.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidTransition.conf
 create mode 100644 autotests/testBasicServiceSetRSN/ssidWPA2.conf
autotests/testBasicServiceSetRSN/bss_rsn_test.py new file +70 -0
new file mode 100644
index 00000000..20e0b62c
@@ -0,0 +1,70 @@
+#! /usr/bin/python3
+
+import unittest
+import sys
+
+import dbus
+
+sys.path.append('../util')
+import iwd
+from iwd import IWD
+from hwsim import Hwsim
+from hostapd import HostapdCLI
+
+BSS_INTERFACE = 'net.connman.iwd.BasicServiceSet'
+
+
+class Test(unittest.TestCase):
+    def _get_rsn(self, device, ssid):
+        ordered_network = device.get_ordered_network(ssid, full_scan=True)
+        network = ordered_network.network_object
+        self.assertEqual(len(network.extended_service_set), 1)
+
+        path = network.extended_service_set[0]
+        proxy = self.wd._bus.get_object(iwd.IWD_SERVICE, path)
+        properties = dbus.Interface(proxy, iwd.DBUS_PROPERTIES)
+
+        return properties.GetAll(BSS_INTERFACE)['RSN']
+
+    def test_rsn(self):
+        device = self.wd.list_devices(1)[0]
+
+        rsn = self._get_rsn(device, 'ssidWPA2')
+        self.assertEqual(set(map(str, rsn['KeyMgmt'])), {'wpa-psk'})
+        self.assertEqual(set(map(str, rsn['Pairwise'])), {'ccmp'})
+        self.assertEqual(str(rsn['Group']), 'ccmp')
+
+        rsn = self._get_rsn(device, 'ssidSAE')
+        self.assertEqual(set(map(str, rsn['KeyMgmt'])), {'sae'})
+        self.assertEqual(set(map(str, rsn['Pairwise'])), {'ccmp'})
+        self.assertEqual(str(rsn['Group']), 'ccmp')
+
+        rsn = self._get_rsn(device, 'ssidTransition')
+        self.assertEqual(set(map(str, rsn['KeyMgmt'])),
+                         {'wpa-psk', 'sae'})
+        self.assertEqual(set(map(str, rsn['Pairwise'])), {'ccmp'})
+        self.assertEqual(str(rsn['Group']), 'ccmp')
+
+    def setUp(self):
+        self.wd = IWD(True)
+
+    def tearDown(self):
+        self.wd.stop()
+        self.wd = None
+
+    @classmethod
+    def setUpClass(cls):
+        Hwsim()
+        cls.hostapd = [
+            HostapdCLI(config='ssidWPA2.conf'),
+            HostapdCLI(config='ssidSAE.conf'),
+            HostapdCLI(config='ssidTransition.conf'),
+        ]
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.hostapd = None
+
+
+if __name__ == '__main__':
+    unittest.main(exit=True)
autotests/testBasicServiceSetRSN/hw.conf new file +9 -0
new file mode 100644
index 00000000..a43062d9
@@ -0,0 +1,9 @@
+[SETUP]
+num_radios=4
+hwsim_medium=yes
+start_iwd=no
+
+[HOSTAPD]
+rad0=ssidWPA2.conf
+rad1=ssidSAE.conf
+rad2=ssidTransition.conf
autotests/testBasicServiceSetRSN/ssidSAE.conf new file +11 -0
new file mode 100644
index 00000000..2ec9ae61
@@ -0,0 +1,11 @@
+hw_mode=g
+channel=2
+ssid=ssidSAE
+
+wpa=2
+wpa_key_mgmt=SAE
+wpa_pairwise=CCMP
+sae_password=secret123
+sae_groups=19
+ieee80211w=2
+sae_pwe=0
autotests/testBasicServiceSetRSN/ssidTransition.conf new file +12 -0
new file mode 100644
index 00000000..00170772
@@ -0,0 +1,12 @@
+hw_mode=g
+channel=3
+ssid=ssidTransition
+
+wpa=2
+wpa_key_mgmt=WPA-PSK SAE
+wpa_pairwise=CCMP
+wpa_passphrase=secret123
+sae_password=secret123
+sae_groups=19
+ieee80211w=1
+sae_pwe=0
autotests/testBasicServiceSetRSN/ssidWPA2.conf new file +8 -0
new file mode 100644
index 00000000..d706142b
@@ -0,0 +1,8 @@
+hw_mode=g
+channel=1
+ssid=ssidWPA2
+
+wpa=2
+wpa_key_mgmt=WPA-PSK
+wpa_pairwise=CCMP
+wpa_passphrase=secret123
Signature · 1 line
2.55.0
author
#20 lore raw

PATCH v4 4/5 client: display BasicServiceSet RSN property

In reply to Aleksman4o

From: Aleks Man <[email protected]>

Parse the optional RSN dictionary and include the advertised key management and cipher capabilities in the station get-bsses output.

---
 client/bss.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)
client/bss.c +76 -0
index def4de7f..b5c80087 100644
@@ -32,6 +32,7 @@
 
 struct bss {
 	char *address;
+	char *rsn;
 };
 
 static const char *get_address(const void *data)
@@ -57,8 +58,82 @@ static void update_address(void *data, struct l_dbus_message_iter *variant)
 	bss->address = l_strdup(value);
 }
 
+static char *string_array_to_string(struct l_dbus_message_iter *variant)
+{
+	struct l_dbus_message_iter array;
+	const char *value;
+	char **strv;
+	char *result;
+
+	if (!l_dbus_message_iter_get_variant(variant, "as", &array))
+		return NULL;
+
+	strv = l_strv_new();
+
+	while (l_dbus_message_iter_next_entry(&array, &value))
+		strv = l_strv_append(strv, value);
+
+	result = l_strjoinv(strv, ' ');
+	l_strv_free(strv);
+
+	return result;
+}
+
+static void update_rsn(void *data, struct l_dbus_message_iter *variant)
+{
+	struct bss *bss = data;
+	struct l_dbus_message_iter dict;
+	struct l_dbus_message_iter value;
+	const char *key;
+	const char *group_value;
+	char *key_mgmt = NULL;
+	char *pairwise = NULL;
+	char *group = NULL;
+
+	l_free(bss->rsn);
+	bss->rsn = NULL;
+
+	if (!variant ||
+			!l_dbus_message_iter_get_variant(variant, "a{sv}", &dict))
+		goto done;
+
+	while (l_dbus_message_iter_next_entry(&dict, &key, &value)) {
+		if (!strcmp(key, "KeyMgmt")) {
+			l_free(key_mgmt);
+			key_mgmt = string_array_to_string(&value);
+		} else if (!strcmp(key, "Pairwise")) {
+			l_free(pairwise);
+			pairwise = string_array_to_string(&value);
+		} else if (!strcmp(key, "Group") &&
+				l_dbus_message_iter_get_variant(&value, "s",
+								&group_value)) {
+			l_free(group);
+			group = l_strdup(group_value);
+		}
+	}
+
+	if (!key_mgmt || !pairwise || !group)
+		goto done;
+
+	bss->rsn = l_strdup_printf("KeyMgmt: %s; Pairwise: %s; Group: %s",
+					key_mgmt, pairwise, group);
+
+done:
+	l_free(key_mgmt);
+	l_free(pairwise);
+	l_free(group);
+}
+
+static const char *get_rsn(const void *data)
+{
+	const struct bss *bss = data;
+
+	return bss->rsn;
+}
+
 static const struct proxy_interface_property bss_properties[] = {
 	{ "Address",       "s", update_address, get_address },
+	{ "RSN",           "a{sv}", update_rsn, get_rsn },
 	{ }
 };
 
@@ -72,6 +147,7 @@ static void bss_destroy(void *data)
 	struct bss *bss = data;
 
 	l_free(bss->address);
+	l_free(bss->rsn);
 	l_free(bss);
 }
 
Signature · 1 line
2.55.0
author
#21 lore raw

PATCH v4 5/5 client: widen BSS property value column

In reply to Aleksman4o

From: Aleks Man <[email protected]>

The get-bsses table used an 18-character value column because Address was the only property. Make room for longer values such as RSN capabilities while keeping the table within 80 columns.

---
 client/station.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
client/station.c +2 -2
index 5066a9f1..eccf8314 100644
@@ -754,7 +754,7 @@ static enum cmd_status cmd_get_bsses(const char *device_name,
 
 	sprintf(header, "%s BasicServiceSets", network_get_name(network_proxy));
 
-	proxy_properties_display_header(header, MARGIN, 10, 18);
+	proxy_properties_display_header(header, MARGIN, 10, 47);
 
 	for (e = l_queue_get_entries(bss_list); e; e = e->next) {
 		const char *path = e->data;
@@ -765,7 +765,7 @@ static enum cmd_status cmd_get_bsses(const char *device_name,
 			continue;
 
 		display_table_row(MARGIN, 1, strlen(path), path);
-		proxy_properties_display_inline(bss_i, MARGIN, 10, 18);
+		proxy_properties_display_inline(bss_i, MARGIN, 10, 47);
 		display_table_row(MARGIN, 1, 1, "");
 	}
 
Signature · 1 line
2.55.0
#22 lore raw

Re: [PATCH v4 0/5] dbus: expose per-BSS RSN capabilities

In reply to Aleksman4o

Hi Aleks,

Quoted 72 lines · On 7/28/26 8:46 AM, Aleksman4o wrote:

From: Aleks Man <[email protected]>

James, thanks for the review. This revision adds the requested iwctl support for displaying the new fields.

Network.Type intentionally represents WPA2-Personal, WPA3-Personal, and transition networks as "psk". This series exposes the RSN information that iwd already retains for each BSS so consumers can distinguish those networks without changing the existing Network API.

The optional BasicServiceSet.RSN dictionary uses the same KeyMgmt, Pairwise, and Group fields and values as wpa_supplicant's BSS.RSN property. NetworkManager can therefore reuse its existing parser rather than add another translation layer. Older consumers ignore the optional property, and consumers can retain their Network.Type fallback for older iwd versions.

The corresponding NetworkManager MR reads and combines the per-BSS capabilities for its synthetic ESS access point:
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2464

Validation:

- make check: 273 passed, 6 skipped, 0 failed.
- The installed patched iwd exposes the expected KeyMgmt, Pairwise, and Group values on a physical WPA2/WPA3 transition BSS.
- The new iwctl output displays the capabilities in the get-bsses table.
- An SAE profile auto-connects after restarting iwd and remains visible as connected in NetworkManager and the Plasma applet.
- A NetworkManager disconnect/reconnect completes without a password prompt and reconnects as WPA3-Personal with FT.
- The upstream CI suite passed all checks for v3.

Changes in v4:
- Add iwctl parsing and display of the BasicServiceSet RSN dictionary, as requested by James.
- Widen get-bsses property values so the capabilities remain readable while the table stays within 80 columns.
- No changes to patches 1-3.

Changes in v3:
- Shorten the auto-t filename so the generated patch metadata complies with GitLint's 80-column limit. There are no code changes.

Changes in v2:
- Resend with git-send-email after the Gmail web client corrupted the inline v1 patches. There are no code changes.

RFC:
https://lore.kernel.org/iwd/CAMq8zouhDMRW4Q7qSpMCFftp21wCzuC%[email protected]/

Related NetworkManager issues:
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1489
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1693

Aleks Man (5):
network: expose RSN capabilities on BSS
doc: document BasicServiceSet RSN property
auto-t: test BasicServiceSet RSN property
client: display BasicServiceSet RSN property
client: widen BSS property value column

  .../testBasicServiceSetRSN/bss_rsn_test.py    |  70 +++++++++++
  autotests/testBasicServiceSetRSN/hw.conf      |   9 ++
  autotests/testBasicServiceSetRSN/ssidSAE.conf |  11 ++
  .../ssidTransition.conf                       |  12 ++
  .../testBasicServiceSetRSN/ssidWPA2.conf      |   8 ++
  client/bss.c                                  |  76 ++++++++++++
  client/station.c                              |   4 +-
  doc/basic-service-set.txt                     |  23 ++++
  src/network.c                                 | 110 ++++++++++++++++++
  9 files changed, 321 insertions(+), 2 deletions(-)
Quoted 6 lines

create mode 100644 autotests/testBasicServiceSetRSN/bss_rsn_test.py create mode 100644 autotests/testBasicServiceSetRSN/hw.conf create mode 100644 autotests/testBasicServiceSetRSN/ssidSAE.conf create mode 100644 autotests/testBasicServiceSetRSN/ssidTransition.conf create mode 100644 autotests/testBasicServiceSetRSN/ssidWPA2.conf

Thanks for the quick turn around. These LGTM. Standby about merging, we'll get these in as soon as we can.

Thanks,

James