[PATCH 1/2] DBus: add a method to get ANQP fields.
Damien Dejean
damiendejean at chromium.org
Tue Feb 20 04:11:40 PST 2024
Adds a D-Bus method to perform ANQP get requests. The new method is
equivalent to the "anqp_get" command available in wpa_cli.
Signed-off-by: Damien Dejean <damiendejean at chromium.org>
---
doc/dbus.doxygen | 18 +++++
tests/hwsim/test_dbus.py | 69 ++++++++++++++++
wpa_supplicant/dbus/dbus_dict_helpers.c | 62 +++++++++++++++
wpa_supplicant/dbus/dbus_dict_helpers.h | 1 +
wpa_supplicant/dbus/dbus_new.c | 7 ++
wpa_supplicant/dbus/dbus_new_handlers.c | 100 ++++++++++++++++++++++++
wpa_supplicant/dbus/dbus_new_handlers.h | 4 +
7 files changed, 261 insertions(+)
diff --git a/doc/dbus.doxygen b/doc/dbus.doxygen
index 4c5f5f9e9..93d387def 100644
--- a/doc/dbus.doxygen
+++ b/doc/dbus.doxygen
@@ -583,6 +583,24 @@ fi.w1.wpa_supplicant1.CreateInterface.
<h3>InterworkingSelect ( ) --> nothing</h3>
<p>Perform Interworking (Hotspot 2.0) network selection.</p>
</li>
+ <li>
+ <h3>ANQPGet ( a{sv} : args) --> nothing</h3>
+ <p>Send an ANQP request.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
+ <tr><td>addr</td><td>s</td><td>Address of the BSS</td><td>Yes</td>
+ <tr><td>freq</td><td>u</td><td>Frequency of the BSS</td><td>No</td>
+ <tr><td>ids</td><td>aq</td><td>List of ANQP information IDs to query</td><td>No</td>
+ <tr><td>hs20_ids</td><td>ay</td><td>List of Hotspot 2.0 ANQP information IDs to query</td><td>No</td>
+ <tr><td>mbo_ids</td><td>ay</td><td>List of MBO ANQP information IDs to query</td><td>No</td>
+ </table>
+ </dd>
+ </dl>
+ </li>
<li>
<h3>EAPLogoff ( ) --> nothing</h3>
<p>IEEE 802.1X EAPOL state machine logoff.</p>
diff --git a/tests/hwsim/test_dbus.py b/tests/hwsim/test_dbus.py
index 3ff911364..8bae4b546 100644
--- a/tests/hwsim/test_dbus.py
+++ b/tests/hwsim/test_dbus.py
@@ -6267,3 +6267,72 @@ def test_dbus_hs20_terms_and_conditions(dev, apdev):
with TestDbusInterworking(bus) as t:
if not t.success():
raise Exception("Expected signals not seen")
+
+def test_dbus_anqp_get(dev, apdev):
+ "D-Bus ANQP get test"
+
+ (bus, wpa_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ venue_group = 1
+ venue_type = 13
+ venue_info = struct.pack('BB', venue_group, venue_type)
+ lang1 = "eng"
+ name1 = "Example venue"
+ lang2 = "fin"
+ name2 = "Esimerkkipaikka"
+ venue1 = struct.pack('B', len(lang1 + name1)) + lang1.encode() + name1.encode()
+ venue2 = struct.pack('B', len(lang2 + name2)) + lang2.encode() + name2.encode()
+
+ url1 = b"http://example.com/venue"
+ url2 = b"https://example.org/venue-info/"
+ duple1 = struct.pack('BB', 1 + len(url1), 1) + url1
+ duple2 = struct.pack('BB', 1 + len(url2), 2) + url2
+ venue_url = binascii.hexlify(duple1 + duple2).decode()
+
+ 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],
+ "anqp_elem": ["277:" + venue_url],
+ "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"],
+ 'mbo': '1',
+ 'mbo_cell_data_conn_pref': '1',
+ 'hs20': '1',
+ 'hs20_oper_friendly_name': ["eng:Example operator", "fin:Esimerkkioperaattori"]}
+
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ iface.ANQPGet({"addr": bssid,
+ "ids": dbus.Array([257], dbus.Signature("q")),
+ "mbo_ids": dbus.Array([2], dbus.Signature("y")),
+ "hs20_ids": dbus.Array([3, 4], dbus.Signature("y"))})
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
+ if ev is None or "ANQP Capability list" not in ev:
+ raise Exception("Did not receive Capability list")
+
+ ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
+ if ev is None or "Operator Friendly Name" not in ev:
+ raise Exception("Did not receive Operator Friendly Name")
+
+ ev = dev[0].wait_event(["RX-MBO-ANQP"], timeout=1)
+ if ev is None or "cell_conn_pref" not in ev:
+ raise Exception("Did not receive MBO Cellular Data Connection Preference")
+
+ bss = dev[0].get_bss(bssid)
+ if 'anqp_capability_list' not in bss:
+ raise Exception("Capability List ANQP-element not seen")
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index fdf7b1258..090f2b1fb 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -705,6 +705,61 @@ done:
return success;
}
+#define UINT16_ARRAY_CHUNK_SIZE 18
+#define UINT16_ARRAY_ITEM_SIZE (sizeof(dbus_uint16_t))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_uint16_array(
+ DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
+{
+ dbus_uint32_t count = 0;
+ dbus_bool_t success = FALSE;
+ dbus_uint16_t *buffer, *nbuffer;
+
+ entry->uint16array_value = NULL;
+ entry->array_type = DBUS_TYPE_UINT16;
+
+ buffer = os_calloc(UINT16_ARRAY_CHUNK_SIZE, UINT16_ARRAY_ITEM_SIZE);
+ if (!buffer)
+ return FALSE;
+
+ entry->array_len = 0;
+ while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_UINT16) {
+ dbus_uint16_t value;
+
+ if ((count % UINT16_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
+ nbuffer = os_realloc_array(
+ buffer, count + UINT16_ARRAY_CHUNK_SIZE,
+ UINT16_ARRAY_ITEM_SIZE);
+ if (nbuffer == NULL) {
+ os_free(buffer);
+ wpa_printf(MSG_ERROR,
+ "dbus: %s out of memory trying to retrieve the uint16 array",
+ __func__);
+ goto done;
+ }
+ buffer = nbuffer;
+ }
+
+ dbus_message_iter_get_basic(iter, &value);
+ buffer[count] = value;
+ entry->array_len = ++count;
+ dbus_message_iter_next(iter);
+ }
+ entry->uint16array_value = buffer;
+ wpa_hexdump_key(MSG_MSGDUMP, "dbus: uint16 array contents",
+ entry->bytearray_value, entry->array_len);
+
+ /* Zero-length arrays are valid. */
+ if (entry->array_len == 0) {
+ os_free(entry->uint16array_value);
+ entry->uint16array_value = NULL;
+ }
+
+ success = TRUE;
+
+done:
+ return success;
+}
#define STR_ARRAY_CHUNK_SIZE 8
#define STR_ARRAY_ITEM_SIZE (sizeof(char *))
@@ -873,6 +928,10 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array(
success = _wpa_dbus_dict_entry_get_byte_array(&iter_array,
entry);
break;
+ case DBUS_TYPE_UINT16:
+ success = _wpa_dbus_dict_entry_get_uint16_array(&iter_array,
+ entry);
+ break;
case DBUS_TYPE_STRING:
success = _wpa_dbus_dict_entry_get_string_array(&iter_array,
array_type,
@@ -1081,6 +1140,9 @@ void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry)
case DBUS_TYPE_BYTE:
os_free(entry->bytearray_value);
break;
+ case DBUS_TYPE_UINT16:
+ os_free(entry->uint16array_value);
+ break;
case DBUS_TYPE_STRING:
if (!entry->strarray_value)
break;
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index cc9e26fa9..1d33689a8 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -139,6 +139,7 @@ struct wpa_dbus_dict_entry {
dbus_uint64_t uint64_value;
double double_value;
char *bytearray_value;
+ dbus_uint16_t *uint16array_value;
char **strarray_value;
struct wpabuf **binarray_value;
};
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 00b38edf5..25b5919c0 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -3716,6 +3716,13 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
END_ARGS
}
},
+ {"ANQPGet", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_anqp_get,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ },
+ },
#endif /* CONFIG_INTERWORKING */
{ NULL, NULL, NULL, { END_ARGS } }
};
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 6ad49a136..e19a7bc8b 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -1977,6 +1977,106 @@ wpas_dbus_handler_interworking_select(DBusMessage *message,
return reply;
}
+
+DBusMessage *
+wpas_dbus_handler_anqp_get(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter, iter_dict;
+ struct wpa_dbus_dict_entry entry;
+ int ret;
+ u8 dst_addr[ETH_ALEN];
+ int is_addr_present = 0;
+ unsigned int freq = 0;
+// Max info ID count from CLI implementation.
+#define MAX_ANQP_INFO_ID 100
+ u16 id[MAX_ANQP_INFO_ID];
+ size_t num_id = 0;
+ u32 subtypes = 0;
+ u32 mbo_subtypes = 0;
+
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ if (os_strcmp(entry.key, "addr") == 0 &&
+ entry.type == DBUS_TYPE_STRING) {
+ if (hwaddr_aton(entry.str_value, dst_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "%s[dbus]: Invalid address '%s'",
+ __func__, entry.str_value);
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(message,
+ "invalid address");
+ }
+
+ is_addr_present = 1;
+ } else if (os_strcmp(entry.key, "freq") == 0 &&
+ entry.type == DBUS_TYPE_UINT32) {
+ freq = entry.uint32_value;
+ } else if (os_strcmp(entry.key, "ids") == 0 &&
+ entry.type == DBUS_TYPE_ARRAY &&
+ entry.array_type == DBUS_TYPE_UINT16) {
+ for (size_t i = 0; i < entry.array_len && num_id < MAX_ANQP_INFO_ID; i++) {
+ id[num_id] = entry.uint16array_value[i];
+ num_id++;
+ }
+ } else if (os_strcmp(entry.key, "hs20_ids") == 0 &&
+ entry.type == DBUS_TYPE_ARRAY &&
+ entry.array_type == DBUS_TYPE_BYTE) {
+ for (size_t i = 0; i < entry.array_len; i++) {
+ int num = entry.bytearray_value[i];
+ if (num <= 0 || num > 31) {
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(message, "invalid HS20 ANQP id");
+ }
+ subtypes |= BIT(num);
+ }
+
+ } else if (os_strcmp(entry.key, "mbo_ids") == 0 &&
+ entry.type == DBUS_TYPE_ARRAY &&
+ entry.array_type == DBUS_TYPE_BYTE) {
+ for (size_t i = 0; i < entry.array_len; i++) {
+ int num = entry.bytearray_value[i];
+ if (num <= 0 || num > MAX_MBO_ANQP_SUBTYPE) {
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(message, "invalid MBO ANQP id");
+ }
+ mbo_subtypes |= BIT(num);
+ }
+
+ } else {
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(message, "unsupported parameter");
+ }
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (is_addr_present == 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s[dbus]: address not provided", __func__);
+ return wpas_dbus_error_invalid_args(
+ message, "address not provided");
+ }
+
+ ret = anqp_send_req(wpa_s, dst_addr, freq, id, num_id, subtypes, mbo_subtypes);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ "%s[dbus]: failed to send ANQP request",
+ __func__);
+ return wpas_dbus_error_unknown_error(
+ message, "error sending ANQP request.");
+ }
+
+ return NULL;
+}
#endif /* CONFIG_INTERWORKING */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 97fa337bd..5a69ec6de 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -157,6 +157,10 @@ DBusMessage *
wpas_dbus_handler_interworking_select(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage *
+wpas_dbus_handler_anqp_get(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
DECLARE_ACCESSOR(wpas_dbus_getter_capabilities);
DECLARE_ACCESSOR(wpas_dbus_getter_state);
DECLARE_ACCESSOR(wpas_dbus_getter_scanning);
--
2.44.0.rc0.258.g7320e95886-goog
More information about the Hostap
mailing list