diff mbox series

DBus: add ANQP fields to BSS properties

Message ID 20240226163247.806012-1-damiendejean@chromium.org
State Accepted
Headers show
Series DBus: add ANQP fields to BSS properties | expand

Commit Message

Damien Dejean Feb. 26, 2024, 4:32 p.m. UTC
Add ANQP fields to the BSS properties to allow DBus clients to be
notified and obtain the values when it changes.

Signed-off-by: Damien Dejean <damiendejean@chromium.org>

Change-Id: I40c192ed71c21f809748a03405e680245c37c4dc
---
 doc/dbus.doxygen                        |  24 ++++
 tests/hwsim/test_dbus.py                |  67 +++++++++
 wpa_supplicant/dbus/dbus_new.c          |   8 ++
 wpa_supplicant/dbus/dbus_new.h          |   1 +
 wpa_supplicant/dbus/dbus_new_handlers.c | 184 ++++++++++++++++++++++++
 wpa_supplicant/dbus/dbus_new_handlers.h |   1 +
 wpa_supplicant/interworking.c           |   1 +
 wpa_supplicant/notify.c                 |   9 ++
 wpa_supplicant/notify.h                 |   2 +
 9 files changed, 297 insertions(+)

Comments

Jouni Malinen March 9, 2024, 5:04 p.m. UTC | #1
On Mon, Feb 26, 2024 at 04:32:41PM +0000, Damien Dejean wrote:
> Add ANQP fields to the BSS properties to allow DBus clients to be
> notified and obtain the values when it changes.

Thanks, applied with some cleanup and a fix.

> diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
> @@ -3198,6 +3198,7 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,

>  out_parse_done:
> +	wpas_notify_bss_anqp_changed(wpa_s, bss->id);

That can trigger a segfault since bss might be NULL here. One of the
automated test cases (gas_anqp_get_no_scan) found this.
diff mbox series

Patch

diff --git a/doc/dbus.doxygen b/doc/dbus.doxygen
index 61d67dca1..a4a277086 100644
--- a/doc/dbus.doxygen
+++ b/doc/dbus.doxygen
@@ -2214,6 +2214,30 @@  scan results.
 	<h3>Age - u - (read)</h3>
 	<p>Number of seconds since the BSS was last seen.</p>
       </li>
+      <li>
+	<h3>ANQP - a{sv} - (read)</h3>
+	<p>ANQP information of the BSS. Empty dictionary indicates no ANQP field. Named dictionary entries are:</p>
+	<table>
+	  <tr><td>CapabilityList</td><td>ay</td></tr>
+	  <tr><td>VenueName</td><td>ay</td></tr>
+	  <tr><td>NetworkAuthType</td><td>ay</td></tr>
+	  <tr><td>RoamingConsortium</td><td>ay</td></tr>
+	  <tr><td>IPAddrTypeAvailability</td><td>ay</td></tr>
+	  <tr><td>NAIRealm</td><td>ay</td></tr>
+	  <tr><td>3GPP</td><td>ay</td></tr>
+	  <tr><td>DomainName</td><td>ay</td></tr>
+	  <tr><td>FilsRealmInfo</td><td>ay</td></tr>
+	  <tr><td>HS20CapabilityList</td><td>ay</td></tr>
+	  <tr><td>HS20OperatorFriendlyName</td><td>ay</td></tr>
+	  <tr><td>HS20WanMetrics</td><td>ay</td></tr>
+	  <tr><td>HS20ConnectionCapability</td><td>ay</td></tr>
+	  <tr><td>HS20OperatingClass</td><td>ay</td></tr>
+	  <tr><td>HS20OSUProvidersList</td><td>ay</td></tr>
+	  <tr><td>HS20OperatorIconMetadata</td><td>ay</td></tr>
+	  <tr><td>HS20OSUProvidersNAIList</td><td>ay</td></tr>
+	</table>
+	<p>Unnamed ANQP elements have a generic entry name 'anqp[id]' where 'id' is the index of the ANQP element as described in IEEE802.11-2020 ยง9.4.5.1.</p>
+      </li>
     </ul>
 
 \subsection dbus_bss_signals Signals
diff --git a/tests/hwsim/test_dbus.py b/tests/hwsim/test_dbus.py
index ce9a2336f..2deceedbe 100644
--- a/tests/hwsim/test_dbus.py
+++ b/tests/hwsim/test_dbus.py
@@ -6400,3 +6400,70 @@  def test_dbus_anqp_query_done(dev, apdev):
     with TestDbusANQPGet(bus) as t:
         if not t.success():
             raise Exception("Expected signals not seen")
+
+def test_dbus_bss_anqp_properties(dev, apdev):
+    "D-Bus ANQP BSS properties changed"
+
+    (bus, wpa_obj, path, if_obj) = prepare_dbus(dev[0])
+    iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+    venue_group = 1
+    venue_type = 13
+    lang1 = "eng"
+    name1 = "Example venue"
+    lang2 = "fin"
+    name2 = "Esimerkkipaikka"
+
+    bssid = apdev[0]['bssid']
+    params = {"ssid": "test-anqp", "hessid": bssid, "wpa": "2",
+              "rsn_pairwise": "CCMP", "wpa_key_mgmt": "WPA-EAP",
+              "ieee80211w": "1", "ieee8021x": "1",
+              "auth_server_addr": "127.0.0.1", "auth_server_port": "1812",
+              "auth_server_shared_secret": "radius",
+              "interworking": "1",
+              "venue_group": str(venue_group),
+              "venue_type": str(venue_type),
+              "venue_name": [lang1 + ":" + name1, lang2 + ":" + name2],
+              "roaming_consortium": ["112233", "1020304050", "010203040506", "fedcba"],
+              "domain_name": "example.com,another.example.com",
+              "nai_realm": ["0,example.com,13[5:6],21[2:4][5:7]", "0,another.example.com"]}
+
+    hapd = hostapd.add_ap(apdev[0], params)
+
+    class TestDbusANQPBSSPropertiesChanged(TestDbus):
+        def __init__(self, bus):
+            TestDbus.__init__(self, bus)
+            self.capability_list = False
+            self.venue_name = False
+            self.roaming_consortium = False
+            self.nai_realm = False
+
+        def __enter__(self):
+            gobject.timeout_add(1, self.run_query)
+            gobject.timeout_add(15000, self.timeout)
+            self.add_signal(self.propertiesChanged, WPAS_DBUS_BSS,
+                            "PropertiesChanged")
+            self.loop.run()
+            return self
+
+        def propertiesChanged(self, properties):
+            logger.debug("propertiesChanged: %s" % str(properties))
+            if 'ANQP' in properties:
+                anqp_properties = properties['ANQP']
+                self.capability_list = 'CapabilityList' in anqp_properties
+                self.venue_name = 'VenueName' in anqp_properties
+                self.roaming_consortium = 'RoamingConsortium' in anqp_properties
+                self.nai_realm = 'NAIRealm' in anqp_properties
+
+        def run_query(self, *args):
+            dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+            iface.ANQPGet({"addr": bssid,
+                           "ids": dbus.Array([257,258,261,263], dbus.Signature("q"))})
+            return False
+
+        def success(self):
+            return self.capability_list and self.venue_name and self.roaming_consortium and self.nai_realm
+
+    with TestDbusANQPBSSPropertiesChanged(bus) as t:
+        if not t.success():
+            raise Exception("Expected signals not seen")
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 4eb339966..fcf614eaa 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -2473,6 +2473,9 @@  void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
 	case WPAS_DBUS_BSS_PROP_AGE:
 		prop = "Age";
 		break;
+	case WPAS_DBUS_BSS_PROP_ANQP:
+		prop = "ANQP";
+		break;
 	default:
 		wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
 			   __func__, property);
@@ -3011,6 +3014,11 @@  static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
 	  NULL,
 	  NULL
 	},
+	{"ANQP", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+	  wpas_dbus_getter_bss_anqp,
+	  NULL,
+	  NULL,
+	},
 	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 545f326d3..f0e99670d 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -53,6 +53,7 @@  enum wpas_dbus_bss_prop {
 	WPAS_DBUS_BSS_PROP_WPS,
 	WPAS_DBUS_BSS_PROP_IES,
 	WPAS_DBUS_BSS_PROP_AGE,
+	WPAS_DBUS_BSS_PROP_ANQP,
 };
 
 enum wpas_dbus_sta_prop {
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index e19a7bc8b..a3d797190 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -5751,6 +5751,190 @@  dbus_bool_t wpas_dbus_getter_bss_age(
 }
 
 
+/**
+ * wpas_dbus_getter_bss_anqp - Return all the ANQP fields of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "ANQP" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_anqp(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	DBusMessageIter iter_dict, variant_iter;
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *bss;
+	struct wpa_bss_anqp *anqp;
+	struct wpa_bss_anqp_elem *elem;
+
+	bss = get_bss_helper(args, error, __func__);
+	if (!bss)
+		return FALSE;
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+			"a{sv}", &variant_iter) ||
+	    !wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+		goto nomem;
+
+	anqp = bss->anqp;
+	if (anqp) {
+#ifdef CONFIG_INTERWORKING
+		if (anqp->capability_list) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "CapabilityList",
+					wpabuf_head(anqp->capability_list),
+					wpabuf_len(anqp->capability_list)))
+				goto nomem;
+		}
+		if (anqp->venue_name) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "VenueName",
+					wpabuf_head(anqp->venue_name),
+					wpabuf_len(anqp->venue_name)))
+				goto nomem;
+		}
+		if (anqp->network_auth_type) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "NetworkAuthType",
+					wpabuf_head(anqp->network_auth_type),
+					wpabuf_len(anqp->network_auth_type)))
+				goto nomem;
+		}
+		if (anqp->roaming_consortium) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "RoamingConsortium",
+					wpabuf_head(anqp->roaming_consortium),
+					wpabuf_len(anqp->roaming_consortium)))
+				goto nomem;
+		}
+		if (anqp->ip_addr_type_availability) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "IPAddrTypeAvailability",
+					wpabuf_head(anqp->ip_addr_type_availability),
+					wpabuf_len(anqp->ip_addr_type_availability)))
+				goto nomem;
+		}
+		if (anqp->nai_realm) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "NAIRealm",
+					wpabuf_head(anqp->nai_realm),
+					wpabuf_len(anqp->nai_realm)))
+				goto nomem;
+		}
+		if (anqp->anqp_3gpp) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "3GPP",
+					wpabuf_head(anqp->anqp_3gpp),
+					wpabuf_len(anqp->anqp_3gpp)))
+				goto nomem;
+		}
+		if (anqp->domain_name) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "DomainName",
+					wpabuf_head(anqp->domain_name),
+					wpabuf_len(anqp->domain_name)))
+				goto nomem;
+		}
+		if (anqp->fils_realm_info) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "FilsRealmInfo",
+					wpabuf_head(anqp->fils_realm_info),
+					wpabuf_len(anqp->fils_realm_info)))
+				goto nomem;
+		}
+#ifdef CONFIG_HS20
+		if (anqp->hs20_capability_list) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "HS20CapabilityList",
+					wpabuf_head(anqp->hs20_capability_list),
+					wpabuf_len(anqp->hs20_capability_list)))
+				goto nomem;
+		}
+		if (anqp->hs20_operator_friendly_name) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "HS20OperatorFriendlyName",
+					wpabuf_head(anqp->hs20_operator_friendly_name),
+					wpabuf_len(anqp->hs20_operator_friendly_name)))
+				goto nomem;
+		}
+		if (anqp->hs20_wan_metrics) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "HS20WanMetrics",
+					wpabuf_head(anqp->hs20_wan_metrics),
+					wpabuf_len(anqp->hs20_wan_metrics)))
+				goto nomem;
+		}
+		if (anqp->hs20_connection_capability) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "HS20ConnectionCapability",
+					wpabuf_head(anqp->hs20_connection_capability),
+					wpabuf_len(anqp->hs20_connection_capability)))
+				goto nomem;
+		}
+		if (anqp->hs20_operating_class) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "HS20OperatingClass",
+					wpabuf_head(anqp->hs20_operating_class),
+					wpabuf_len(anqp->hs20_operating_class)))
+				goto nomem;
+		}
+		if (anqp->hs20_osu_providers_list) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "HS20OSUProvidersList",
+					wpabuf_head(anqp->hs20_osu_providers_list),
+					wpabuf_len(anqp->hs20_osu_providers_list)))
+				goto nomem;
+		}
+		if (anqp->hs20_operator_icon_metadata) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "HS20OperatorIconMetadata",
+					wpabuf_head(anqp->hs20_operator_icon_metadata),
+					wpabuf_len(anqp->hs20_operator_icon_metadata)))
+				goto nomem;
+		}
+		if (anqp->hs20_osu_providers_nai_list) {
+			if (!wpa_dbus_dict_append_byte_array(
+					&iter_dict, "HS20OSUProvidersNAIList",
+					wpabuf_head(anqp->hs20_osu_providers_nai_list),
+					wpabuf_len(anqp->hs20_osu_providers_nai_list)))
+				goto nomem;
+		}
+#endif /* CONFIG_HS20 */
+		dl_list_for_each(elem, &anqp->anqp_elems,
+				 struct wpa_bss_anqp_elem, list) {
+			char title[32];
+
+			os_snprintf(title, sizeof(title), "anqp[%u]",
+				    elem->infoid);
+			if (!wpa_dbus_dict_append_byte_array(&iter_dict,
+							     title,
+							     wpabuf_head(elem->payload),
+							     wpabuf_len(elem->payload)))
+				goto nomem;
+
+			os_snprintf(title, sizeof(title), "protected-anqp-info[%u]", elem->infoid);
+			if (!wpa_dbus_dict_append_bool(&iter_dict, title,
+						       elem->protected_response))
+				goto nomem;
+		}
+#endif /* CONFIG_INTERWORKING */
+	}
+
+	if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
+		goto nomem;
+
+	return TRUE;
+
+nomem:
+	dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
+	return FALSE;
+}
+
+
 /**
  * wpas_dbus_getter_enabled - Check whether network is enabled or disabled
  * @iter: Pointer to incoming dbus message iter
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 5a69ec6de..3351dbdc3 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -220,6 +220,7 @@  DECLARE_ACCESSOR(wpas_dbus_getter_bss_rsn);
 DECLARE_ACCESSOR(wpas_dbus_getter_bss_wps);
 DECLARE_ACCESSOR(wpas_dbus_getter_bss_ies);
 DECLARE_ACCESSOR(wpas_dbus_getter_bss_age);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_anqp);
 DECLARE_ACCESSOR(wpas_dbus_getter_enabled);
 DECLARE_ACCESSOR(wpas_dbus_setter_enabled);
 DECLARE_ACCESSOR(wpas_dbus_getter_network_properties);
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 06b4d1116..735a20bb7 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -3198,6 +3198,7 @@  void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
 	}
 
 out_parse_done:
+	wpas_notify_bss_anqp_changed(wpa_s, bss->id);
 #ifdef CONFIG_HS20
 	hs20_notify_parse_done(wpa_s);
 #endif /* CONFIG_HS20 */
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 1c7406021..0845a8f63 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -559,6 +559,15 @@  void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id)
 }
 
 
+void wpas_notify_bss_anqp_changed(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_ANQP, id);
+}
+
+
 void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name)
 {
 	if (wpa_s->p2p_mgmt)
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 659380b0f..37bb3b710 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -83,6 +83,8 @@  void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s,
 void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
 				   unsigned int id);
 void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id);
+void wpas_notify_bss_anqp_changed(struct wpa_supplicant *wpa_s,
+				  unsigned int id);
 void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name);
 void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name);