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@xxxxxxxxxxxx> --- 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 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap