From c8e4283f90e0f46e8ec5aeefe26d23a18f7436df Mon Sep 17 00:00:00 2001 From: Damien Dejean Date: Wed, 8 Dec 2021 07:45:32 +0000 Subject: [PATCH] D-Bus: Interworking network selection Add the "InterworkingSelect" method to the DBus API to trigger an Interworking scan with ANQP fetches. When a BSS that matches a configured credential is found, the result is emitted using the signal "InterworkingAPAdded". Completion of the full InterworkingSelect operation is indicated with the "InterworkingSelectDone" signal. Signed-off-by: Damien Dejean --- doc/dbus.doxygen | 12 +++ tests/hwsim/test_dbus.py | 66 ++++++++++++++ wpa_supplicant/dbus/dbus_new.c | 110 ++++++++++++++++++++++++ wpa_supplicant/dbus/dbus_new.h | 24 ++++++ wpa_supplicant/dbus/dbus_new_handlers.c | 23 +++++ wpa_supplicant/dbus/dbus_new_handlers.h | 4 + wpa_supplicant/interworking.c | 12 ++- wpa_supplicant/notify.c | 30 +++++++ wpa_supplicant/notify.h | 7 ++ 9 files changed, 281 insertions(+), 7 deletions(-) diff --git a/doc/dbus.doxygen b/doc/dbus.doxygen index 68fc84572..f6ab82000 100644 --- a/doc/dbus.doxygen +++ b/doc/dbus.doxygen @@ -574,6 +574,10 @@ fi.w1.wpa_supplicant1.CreateInterface.

RemoveAllCreds ( ) --> nothing

Remove all configured Interworking/Hotspot 2.0 credentials.

+
  • +

    InterworkingSelect ( ) --> nothing

    +

    Perform Interworking (Hotspot 2.0) network selection.

    +
  • EAPLogoff ( ) --> nothing

    IEEE 802.1X EAPOL state machine logoff.

    @@ -1283,6 +1287,14 @@ fi.w1.wpa_supplicant1.CreateInterface.
    A dictionary with pairs of field names and their values. Possible dictionary keys are: "addr", "dst", "bssid", "ies", "signal".
  • + +
  • +

    InterworkingAPAdded ( o : bss, o : cred, a{sv} : args )

    +
  • + +
  • +

    InterworkingSelectDone ( )

    +
  • diff --git a/tests/hwsim/test_dbus.py b/tests/hwsim/test_dbus.py index 4edd0fd73..28fb05014 100644 --- a/tests/hwsim/test_dbus.py +++ b/tests/hwsim/test_dbus.py @@ -6136,3 +6136,69 @@ def test_dbus_creds(dev, apdev): raise Exception("Credential remove failed") if not "FAIL" in dev[0].get_cred(1, 'domain'): raise Exception("Credential remove failed") + +def test_dbus_interworking(dev, apdev): + "D-Bus interworking selection" + (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0]) + iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE) + + params = {"ssid": "test-interworking", "wpa": "2", + "wpa_key_mgmt": "WPA-EAP", "rsn_pairwise": "CCMP", + "ieee8021x": "1", "eapol_version": "2", + "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf", + "ca_cert": "auth_serv/ca.pem", + "server_cert": "auth_serv/server.pem", + "private_key": "auth_serv/server.key", + "interworking": "1", + "domain_name": "server.w1.fi", + "nai_realm": "0,server.w1.fi,21[2:4][5:7]", + "roaming_consortium": "2233445566", + "hs20": "1", "anqp_domain_id": "1234"} + + hapd = hostapd.add_ap(apdev[0], params) + + class TestDbusInterworking(TestDbus): + def __init__(self, bus): + TestDbus.__init__(self, bus) + self.interworking_ap_seen = False + self.interworking_select_done = False + + def __enter__(self): + gobject.timeout_add(1, self.run_select) + gobject.timeout_add(15000, self.timeout) + self.add_signal(self.interworkingAPAdded, WPAS_DBUS_IFACE, + "InterworkingAPAdded") + self.add_signal(self.interworkingSelectDone, WPAS_DBUS_IFACE, + "InterworkingSelectDone") + self.loop.run() + return self + + def interworkingAPAdded(self, bss, cred, properties): + logger.debug("interworkingAPAdded: bss=%s cred=%s %s" % (bss, cred, str(properties))) + if self.cred == cred: + self.interworking_ap_seen = True + + def interworkingSelectDone(self): + logger.debug("interworkingSelectDone") + self.interworking_select_done = True + self.loop.quit() + + def run_select(self, *args): + args = {"domain": "server.w1.fi", + "realm": "server.w1.fi", + "eap": "TTLS", + "phase2": "auth=MSCHAPV2", + "username": "user", + "password": "password", + "domain_suffix_match": "server.w1.fi", + "ca_cert": "auth_serv/ca.pem"} + self.cred = iface.AddCred(dbus.Dictionary(args, signature='sv')) + iface.InterworkingSelect() + return False + + def success(self): + return self.interworking_ap_seen and self.interworking_select_done + + with TestDbusInterworking(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 bfa49d654..9279ae4d5 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -937,6 +937,95 @@ void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s, #endif /* CONFIG_MESH */ +#ifdef CONFIG_INTERWORKING + +void wpas_dbus_signal_interworking_ap_added(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + struct wpa_cred *cred, + const char *type, + int excluded, + int bh, + int bss_load, + int conn_capab) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + char bss_path[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path; + char cred_path[WPAS_DBUS_OBJECT_PATH_MAX], *cred_obj_path; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "InterworkingAPAdded"); + if (!msg) + return; + + os_snprintf(bss_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", + wpa_s->dbus_new_path, bss->id); + bss_obj_path = bss_path; + + os_snprintf(cred_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_CREDENTIALS_PART "/%u", + wpa_s->dbus_new_path, cred->id); + cred_obj_path = cred_path; + + dbus_message_iter_init_append(msg, &iter); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &bss_obj_path) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &cred_obj_path) || + !wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_string(&dict_iter, "type", type) || + !wpa_dbus_dict_append_int32(&dict_iter, "excluded", excluded) || + !wpa_dbus_dict_append_int32(&dict_iter, "priority", + cred->priority) || + !wpa_dbus_dict_append_int32(&dict_iter, "sp_priority", + cred->sp_priority) || + !wpa_dbus_dict_append_int32(&dict_iter, "below_min_backhaul", bh) || + !wpa_dbus_dict_append_int32(&dict_iter, "over_max_bss_load", + bss_load) || + !wpa_dbus_dict_append_int32(&dict_iter, "conn_capab_missing", + conn_capab) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); +} + + +void wpas_dbus_signal_interworking_select_done(struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "InterworkingSelectDone"); + if (!msg) + return; + + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); +} + +#endif /* CONFIG_INTERWORKING */ + + void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s, int depth, const char *subject, const char *altsubject[], @@ -3592,6 +3681,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "InterworkingSelect", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_interworking_select, + { + END_ARGS + } + }, #endif /* CONFIG_INTERWORKING */ { NULL, NULL, NULL, { END_ARGS } } }; @@ -4160,6 +4255,21 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { } }, #endif /* CONFIG_MESH */ +#ifdef CONFIG_INTERWORKING + { "InterworkingAPAdded", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "bss", "o", ARG_OUT }, + { "cred", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "InterworkingSelectDone", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + END_ARGS + } + }, +#endif /* CONFIG_INTERWORKING */ { NULL, NULL, { END_ARGS } } }; diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h index 6cdd9dc16..26bdcb548 100644 --- a/wpa_supplicant/dbus/dbus_new.h +++ b/wpa_supplicant/dbus/dbus_new.h @@ -16,6 +16,8 @@ struct wpa_global; struct wpa_supplicant; struct wpa_ssid; +struct wpa_cred; +struct wpa_bss; struct wps_event_m2d; struct wps_event_fail; struct wps_credential; @@ -267,6 +269,13 @@ void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s, const u8 *peer_addr); void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int reason); +void wpas_dbus_signal_interworking_ap_added(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + struct wpa_cred *cred, + const char *type, int excluded, + int bh, int bss_load, + int conn_capab); +void wpas_dbus_signal_interworking_select_done(struct wpa_supplicant *wpa_s); #else /* CONFIG_CTRL_IFACE_DBUS_NEW */ @@ -619,6 +628,21 @@ void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s, { } +static inline +void wpas_dbus_signal_interworking_ap_added(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + struct wpa_cred *cred, + const char *type, int excluded, + int bh, int bss_load, + int conn_capab) +{ +} + +static inline +void wpas_dbus_signal_interworking_select_done(struct wpa_supplicant *wpa_s) +{ +} + #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #endif /* CTRL_IFACE_DBUS_H_NEW */ diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index ce6923e95..545e9f642 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -26,6 +26,7 @@ #include "../scan.h" #include "../autoscan.h" #include "../ap.h" +#include "../interworking.h" #include "dbus_new_helpers.h" #include "dbus_new.h" #include "dbus_new_handlers.h" @@ -1779,6 +1780,28 @@ DBusMessage * wpas_dbus_handler_remove_all_creds(DBusMessage *message, } +DBusMessage * +wpas_dbus_handler_interworking_select(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + int result; + DBusMessage *reply = NULL; + + /* Automatic selection is disabled and no constraint on channels */ + result = interworking_select(wpa_s, 0, NULL); + if (result < 0) { + wpa_printf(MSG_ERROR, + "%s[dbus]: failed to start Interworking selection", + __func__); + reply = wpas_dbus_error_scan_error( + message, + "error starting Interworking selection."); + } + + return reply; +} + + /** * wpas_dbus_handler_signal_poll - Request immediate signal properties * @message: Pointer to incoming dbus message diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h index 1496f5535..a421083f7 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/wpa_supplicant/dbus/dbus_new_handlers.h @@ -153,6 +153,10 @@ DBusMessage * wpas_dbus_handler_remove_cred(DBusMessage *message, DBusMessage * wpas_dbus_handler_remove_all_creds(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * +wpas_dbus_handler_interworking_select(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); diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 5ae71ca1c..71a5c1651 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -2502,13 +2502,9 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) bh = cred_below_min_backhaul(wpa_s, cred, bss); bss_load = cred_over_max_bss_load(wpa_s, cred, bss); conn_capab = cred_conn_capab_missing(wpa_s, cred, bss); - wpa_msg(wpa_s, MSG_INFO, "%s" MACSTR " type=%s%s%s%s id=%d priority=%d sp_priority=%d", - excluded ? INTERWORKING_EXCLUDED : INTERWORKING_AP, - MAC2STR(bss->bssid), type, - bh ? " below_min_backhaul=1" : "", - bss_load ? " over_max_bss_load=1" : "", - conn_capab ? " conn_capab_missing=1" : "", - cred->id, cred->priority, cred->sp_priority); + wpas_notify_interworking_ap_added(wpa_s, bss, cred, excluded, + type, bh, bss_load, + conn_capab); if (excluded) continue; if (wpa_s->auto_select || @@ -2599,6 +2595,8 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); } + wpas_notify_interworking_select_done(wpa_s); + if (selected) { wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR, MAC2STR(selected->bssid)); diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index fe5e072c2..821c916c1 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -19,6 +19,7 @@ #include "rsn_supp/wpa.h" #include "fst/fst.h" #include "crypto/tls.h" +#include "bss.h" #include "driver_i.h" #include "scan.h" #include "p2p_supplicant.h" @@ -943,3 +944,32 @@ void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_MESH */ + + +#ifdef CONFIG_INTERWORKING + +void wpas_notify_interworking_ap_added(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + struct wpa_cred *cred, int excluded, + const char *type, int bh, int bss_load, + int conn_capab) +{ + wpa_msg(wpa_s, MSG_INFO, "%s" MACSTR " type=%s%s%s%s id=%d priority=%d sp_priority=%d", + excluded ? INTERWORKING_EXCLUDED : INTERWORKING_AP, + MAC2STR(bss->bssid), type, + bh ? " below_min_backhaul=1" : "", + bss_load ? " over_max_bss_load=1" : "", + conn_capab ? " conn_capab_missing=1" : "", + cred->id, cred->priority, cred->sp_priority); + + wpas_dbus_signal_interworking_ap_added(wpa_s, bss, cred, type, excluded, + bh, bss_load, conn_capab); +} + + +void wpas_notify_interworking_select_done(struct wpa_supplicant *wpa_s) +{ + wpas_dbus_signal_interworking_select_done(wpa_s); +} + +#endif /* CONFIG_INTERWORKING */ diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h index e843aa124..c46e7986e 100644 --- a/wpa_supplicant/notify.h +++ b/wpa_supplicant/notify.h @@ -15,6 +15,7 @@ struct wps_credential; struct wps_event_m2d; struct wps_event_fail; struct tls_cert_data; +struct wpa_cred; int wpas_notify_supplicant_initialized(struct wpa_global *global); void wpas_notify_supplicant_deinitialized(struct wpa_global *global); @@ -156,5 +157,11 @@ void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s, const u8 *peer_addr); void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s, const u8 *peer_addr, u16 reason_code); +void wpas_notify_interworking_ap_added(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + struct wpa_cred *cred, int excluded, + const char *type, int bh, int bss_load, + int conn_capab); +void wpas_notify_interworking_select_done(struct wpa_supplicant *wpa_s); #endif /* NOTIFY_H */