From 85031f1c1a8e704d69fa78af96d9141586377987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20S=C3=B6ntgen?= Date: Wed, 10 Dec 2014 10:08:51 +0100 Subject: [PATCH] wifi: report wlan networks The wifi_drv now provides two reports. The first one contains all accesspoints that were found while scanning the supported frequencies. The second one reports the state of the driver, i.e., if it is conntected to an accesspoint or not. In addition to that, the driver now gets its configuration via a ROM session. More detailed information are available in 'repos/dde_linux/README'. Issue #1415. --- repos/dde_linux/README | 42 ++++++ repos/dde_linux/lib/mk/wpa_supplicant.mk | 6 +- repos/dde_linux/patches/wpa_supplicant.patch | 113 ++++++++++++++++ repos/dde_linux/ports/dde_linux.hash | 2 +- repos/dde_linux/src/drivers/wifi/main.cc | 91 +++++++++++++ repos/dde_linux/src/drivers/wifi/wpa.h | 1 + repos/dde_linux/src/lib/wpa_supplicant/main.c | 51 +++++++- .../src/lib/wpa_supplicant/reporter.cc | 121 ++++++++++++++++++ 8 files changed, 421 insertions(+), 6 deletions(-) create mode 100644 repos/dde_linux/src/lib/wpa_supplicant/reporter.cc diff --git a/repos/dde_linux/README b/repos/dde_linux/README index a0c648ea3..7f3f66b2a 100644 --- a/repos/dde_linux/README +++ b/repos/dde_linux/README @@ -131,3 +131,45 @@ Configuration snippet: ! ! ! +! +! +! +! + +Each accesspoint node has attributes that contain the SSID and the BSSID +of the accesspoint as well as the link quality (signal strength). These +attributes are mandatory. If the network is protected, the node will also +have an attribute describing the type of protection in addition. + +The second report provides information about the state of the connection +to the currently connected accesspoint: + +! +! +! + +Valid state values are 'connected', 'disconnected', 'connecting' and +'disconnecting'. + +In return, the wifi_drv get its configuration via a ROM module. This ROM +module contains the selected accesspoint. To connect to a accesspoint +a report like the following is used: + +! + +To disconnect from an accesspoint, a empty report is sent: + +! + +By subscribing to both reports and providing the required ROM module, a +component can control the wireless driver. An example therefore is the Qt +based component in 'src/app/qt_wifi_connect'. + +Currently only WPA/WPA2 protection using a pre-shared key is supported. diff --git a/repos/dde_linux/lib/mk/wpa_supplicant.mk b/repos/dde_linux/lib/mk/wpa_supplicant.mk index 7baf4d361..aaa32fea1 100644 --- a/repos/dde_linux/lib/mk/wpa_supplicant.mk +++ b/repos/dde_linux/lib/mk/wpa_supplicant.mk @@ -10,7 +10,8 @@ CC_OPT += -Wno-unused-function CC_CXX_OPT += -fpermissive -SRC_C += main.c +SRC_C += main.c +SRC_CC += reporter.cc # wpa_supplicant SRC_C_wpa_supplicant = blacklist.c \ @@ -103,4 +104,5 @@ INC_DIR += $(WS_CONTRIB_DIR)/src/utils CC_OPT += -DCONFIG_ELOOP_POLL vpath %.c $(WS_CONTRIB_DIR) -vpath %.c $(WS_DIR) +vpath %.c $(WS_DIR) +vpath %.cc $(WS_DIR) diff --git a/repos/dde_linux/patches/wpa_supplicant.patch b/repos/dde_linux/patches/wpa_supplicant.patch index 5ac9822c3..443e222b4 100644 --- a/repos/dde_linux/patches/wpa_supplicant.patch +++ b/repos/dde_linux/patches/wpa_supplicant.patch @@ -81,3 +81,116 @@ #endif /* CONFIG_ELOOP_POLL */ #ifdef CONFIG_ELOOP_EPOLL +@@ -801,7 +801,7 @@ + #endif /* CONFIG_NATIVE_WINDOWS */ + + +-static void eloop_handle_signal(int sig) ++void eloop_handle_signal(int sig) + { + int i; + +--- a/wpa_supplicant/events.c ++++ b/wpa_supplicant/events.c +@@ -1245,6 +1245,9 @@ + } + + ++extern void wpa_report_scan_results(struct wpa_supplicant *); ++ ++ + /* Return != 0 if no scan results could be fetched or if scan results should not + * be shared with other virtual interfaces. */ + static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, +@@ -1281,6 +1284,8 @@ + goto scan_work_done; + } + ++ wpa_report_scan_results(wpa_s); ++ + #ifndef CONFIG_NO_RANDOM_POOL + num = scan_res->num; + if (num > 10) +--- a/src/drivers/driver.h ++++ b/src/drivers/driver.h +@@ -267,6 +267,20 @@ + + #define WPAS_MAX_SCAN_SSIDS 16 + ++struct wpa_driver_scan_ssid { ++ /** ++ * ssid - specific SSID to scan for (ProbeReq) ++ * %NULL or zero-length SSID is used to indicate active scan ++ * with wildcard SSID. ++ */ ++ const u8 *ssid; ++ /** ++ * ssid_len: Length of the SSID in octets ++ */ ++ size_t ssid_len; ++}; ++ ++ + /** + * struct wpa_driver_scan_params - Scan parameters + * Data for struct wpa_driver_ops::scan2(). +@@ -275,18 +289,7 @@ + /** + * ssids - SSIDs to scan for + */ +- struct wpa_driver_scan_ssid { +- /** +- * ssid - specific SSID to scan for (ProbeReq) +- * %NULL or zero-length SSID is used to indicate active scan +- * with wildcard SSID. +- */ +- const u8 *ssid; +- /** +- * ssid_len: Length of the SSID in octets +- */ +- size_t ssid_len; +- } ssids[WPAS_MAX_SCAN_SSIDS]; ++ struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; + + /** + * num_ssids - Number of entries in ssids array +--- a/wpa_supplicant/events.c ++++ b/wpa_supplicant/events.c +@@ -2067,6 +2067,9 @@ static int disconnect_reason_recoverable(u16 reason_code) + } + + ++void wpa_report_disconnect_event(struct wpa_supplicant *); ++ ++ + static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, + u16 reason_code, + int locally_generated) +@@ -2088,6 +2091,7 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, + + if (!is_zero_ether_addr(bssid) || + wpa_s->wpa_state >= WPA_AUTHENTICATING) { ++ wpa_report_disconnect_event(wpa_s); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR + " reason=%d%s", + MAC2STR(bssid), reason_code, +--- a/wpa_supplicant/wpa_supplicant.c ++++ b/wpa_supplicant/wpa_supplicant.c +@@ -656,6 +656,9 @@ void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s) + } + + ++void wpa_report_connect_event(struct wpa_ssid *); ++ ++ + /** + * wpa_supplicant_set_state - Set current connection state + * @wpa_s: Pointer to wpa_supplicant data +@@ -689,6 +692,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, + + if (state == WPA_COMPLETED && wpa_s->new_connection) { + struct wpa_ssid *ssid = wpa_s->current_ssid; ++ wpa_report_connect_event(ssid); + #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to " + MACSTR " completed [id=%d id_str=%s]", diff --git a/repos/dde_linux/ports/dde_linux.hash b/repos/dde_linux/ports/dde_linux.hash index 4d4bfbb04..2488f3617 100644 --- a/repos/dde_linux/ports/dde_linux.hash +++ b/repos/dde_linux/ports/dde_linux.hash @@ -1 +1 @@ -8251d08813903fffb40bab8041618909d06e690a +1752fc50fccc55b94e45ca8c730df74e7c349a7b diff --git a/repos/dde_linux/src/drivers/wifi/main.cc b/repos/dde_linux/src/drivers/wifi/main.cc index fa7828c57..c1d10ff2e 100644 --- a/repos/dde_linux/src/drivers/wifi/main.cc +++ b/repos/dde_linux/src/drivers/wifi/main.cc @@ -14,13 +14,19 @@ /* Genode includes */ #include #include +#include #include +#include /* local includes */ #include "wpa.h" +typedef long long ssize_t; extern void wifi_init(Server::Entrypoint &, Genode::Lock &); +extern "C" void wpa_conf_reload(void); +extern "C" ssize_t wpa_write_conf(char const *, Genode::size_t); + static Genode::Lock &wpa_startup_lock() { @@ -28,11 +34,92 @@ static Genode::Lock &wpa_startup_lock() return _l; } + +static int update_conf(char **p, Genode::size_t *len, char const *ssid, + bool encryption = false, char const *psk = 0) +{ + char const *ssid_fmt = "network={\n\tssid=\"%s\"\n\tkey_mgmt=%s\n"; + char const *psk_fmt = "psk=\"%s\"\n"; + char const *end_fmt = "}\n"; + + static char buf[4096]; + Genode::size_t n = Genode::snprintf(buf, sizeof(buf), ssid_fmt, ssid, + encryption ? "WPA-PSK" : "NONE"); + if (encryption) { + n += Genode::snprintf(buf + n, sizeof(buf) - n, psk_fmt, psk); + } + + n += Genode::snprintf(buf + n, sizeof(buf) - n, end_fmt); + + *p = buf; + *len = n; + + return 0; +} + + +struct Wlan_configration +{ + Genode::Attached_rom_dataspace config_rom { "wlan_configuration" }; + Genode::Signal_rpc_member dispatcher; + + void _update_conf() + { + config_rom.update(); + Genode::size_t size = config_rom.size(); + + char *file_buffer; + Genode::size_t buffer_length; + + Genode::Xml_node node(config_rom.local_addr(), size); + + /** + * Since is empty we generate a dummy + * configuration to fool wpa_supplicant to keep it scanning for + * the non exisiting network. + */ + if (!node.has_attribute("ssid")) { + update_conf(&file_buffer, &buffer_length, "dummyssid"); + } else { + char ssid[32+1] = { 0 }; + node.attribute("ssid").value(ssid, sizeof(ssid)); + + bool use_protection = node.has_attribute("protection"); + + char psk[63+1] = { 0 }; + if (use_protection && node.has_attribute("psk")) + node.attribute("psk").value(psk, sizeof(psk)); + + if (update_conf(&file_buffer, &buffer_length, ssid, + use_protection, psk) != 0) + return; + } + + if (wpa_write_conf(file_buffer, buffer_length) == 0) { + PINF("reload wpa_supplicant configuration"); + wpa_conf_reload(); + } + } + + void _handle_update(unsigned) { _update_conf(); } + + Wlan_configration(Server::Entrypoint &ep) + : + dispatcher(ep, *this, &Wlan_configration::_handle_update) + { + config_rom.sigh(dispatcher); + _update_conf(); + } +}; + + struct Main { Server::Entrypoint &_ep; Wpa_thread *_wpa; + Wlan_configration *_wc; + Main(Server::Entrypoint &ep) : _ep(ep) @@ -41,6 +128,10 @@ struct Main _wpa->start(); + try { + _wc = new (Genode::env()->heap()) Wlan_configration(_ep); + } catch (...) { PWRN("could not create wlan_configration handler"); } + wifi_init(ep, wpa_startup_lock()); } }; diff --git a/repos/dde_linux/src/drivers/wifi/wpa.h b/repos/dde_linux/src/drivers/wifi/wpa.h index 063737fc7..c8ac211ea 100644 --- a/repos/dde_linux/src/drivers/wifi/wpa.h +++ b/repos/dde_linux/src/drivers/wifi/wpa.h @@ -19,6 +19,7 @@ /* entry function */ extern "C" int wpa_main(void); +extern "C" void wpa_conf_reload(void); class Wpa_thread : public Genode::Thread<8 * 1024 * sizeof(long)> { diff --git a/repos/dde_linux/src/lib/wpa_supplicant/main.c b/repos/dde_linux/src/lib/wpa_supplicant/main.c index 60c50e6ee..c776a5935 100644 --- a/repos/dde_linux/src/lib/wpa_supplicant/main.c +++ b/repos/dde_linux/src/lib/wpa_supplicant/main.c @@ -1,4 +1,19 @@ /* + * \brief WPA Supplicant frontend + * \author Josef Soentgen + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* + * based on: + * * WPA Supplicant / Example program entrypoint * Copyright (c) 2003-2005, Jouni Malinen * @@ -6,12 +21,20 @@ * See README for more details. */ -#include "includes.h" +/* libc includes */ +#include +#include +#include +#include +/* local includes */ +#include "includes.h" #include "common.h" #include "wpa_supplicant_i.h" -int wpa_main() +static char const *conf_file = "/config/wpa_supplicant.conf"; + +int wpa_main(void) { struct wpa_interface iface; int exitcode = 0; @@ -21,6 +44,7 @@ int wpa_main() memset(¶ms, 0, sizeof(params)); params.wpa_debug_level = MSG_INFO; + // params.wpa_debug_level = MSG_DEBUG; global = wpa_supplicant_init(¶ms); if (global == NULL) @@ -29,7 +53,7 @@ int wpa_main() memset(&iface, 0, sizeof(iface)); iface.ifname = "wlan0"; - iface.confname = "/wpa_supplicant.conf"; + iface.confname = conf_file; if (wpa_supplicant_add_iface(global, &iface) == NULL) exitcode = -1; @@ -41,3 +65,24 @@ int wpa_main() return exitcode; } + + +void eloop_handle_signal(int); +void wpa_conf_reload(void) +{ + /* (ab)use POSIX signal to trigger reloading the conf file */ + eloop_handle_signal(SIGHUP); +} + + +int wpa_write_conf(char const *buffer, size_t len) +{ + int fd = open(conf_file, O_TRUNC|O_WRONLY); + if (fd == -1) + return -1; + + ssize_t n = write(fd, buffer, len); + close(fd); + + return n > 0 ? 0 : -1; +} diff --git a/repos/dde_linux/src/lib/wpa_supplicant/reporter.cc b/repos/dde_linux/src/lib/wpa_supplicant/reporter.cc new file mode 100644 index 000000000..44af972c2 --- /dev/null +++ b/repos/dde_linux/src/lib/wpa_supplicant/reporter.cc @@ -0,0 +1,121 @@ +/* + * \brief WPA Supplicant frontend + * \author Josef Soentgen + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* WPA Supplicant includes */ +extern "C" { +#include "includes.h" +#include "common.h" +#include "drivers/driver.h" +#include "wpa_supplicant_i.h" +#include "bss.h" +#include "scan.h" +#include "common/ieee802_11_defs.h" +} + + +static Genode::Reporter accesspoints_reporter = { "wlan_accesspoints" }; +static Genode::Reporter state_reporter = { "wlan_state" }; + + +enum { SSID_MAX_LEN = 32 + 1, MAC_STR_LEN = 6*2 + 5 + 1}; + + +static inline void mac2str(char *buf, u8 const *mac) +{ + Genode::snprintf(buf, MAC_STR_LEN, "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} + + +extern "C" void wpa_report_connect_event(struct wpa_ssid *wpa_ssid) +{ + state_reporter.enabled(true); + + try { + Genode::Reporter::Xml_generator xml(state_reporter, [&]() { + /* FIXME ssid may contain any characters, even NUL */ + Genode::String ssid((char const*)wpa_ssid->ssid, wpa_ssid->ssid_len); + + char bssid_buf[MAC_STR_LEN]; + mac2str(bssid_buf, wpa_ssid->bssid); + + xml.node("accesspoint", [&]() { + xml.attribute("ssid", ssid.string()); + xml.attribute("bssid", bssid_buf); + xml.attribute("state", "connected"); + }); + }); + } catch (...) { PWRN("could not report connected state"); } +} + + +extern "C" void wpa_report_disconnect_event(struct wpa_supplicant *wpa_s) +{ + state_reporter.enabled(true); + + try { + Genode::Reporter::Xml_generator xml(state_reporter, [&]() { + struct wpa_ssid *wpa_ssid = wpa_s->current_ssid; + + /* FIXME ssid may contain any characters, even NUL */ + Genode::String ssid((char const*)wpa_ssid->ssid, wpa_ssid->ssid_len); + + char bssid_buf[MAC_STR_LEN]; + mac2str(bssid_buf, wpa_ssid->bssid); + + xml.node("accesspoint", [&]() { + xml.attribute("ssid", ssid.string()); + xml.attribute("bssid", bssid_buf); + xml.attribute("state", "disconnected"); + }); + + }); + } catch (...) { PWRN("could not report disconnected state"); } +} + + +extern "C" void wpa_report_scan_results(struct wpa_supplicant *wpa_s) +{ + accesspoints_reporter.enabled(true); + + try { + Genode::Reporter::Xml_generator xml(accesspoints_reporter, [&]() { + for (unsigned i = 0; i < wpa_s->last_scan_res_used; i++) { + struct wpa_bss *bss = wpa_s->last_scan_res[i]; + + bool wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) != NULL; + bool wpa2 = wpa_bss_get_ie(bss, WLAN_EID_RSN) != NULL; + + char bssid_buf[MAC_STR_LEN]; + mac2str(bssid_buf, bss->bssid); + + Genode::String ssid((char const*)bss->ssid, bss->ssid_len); + + xml.node("accesspoint", [&]() { + xml.attribute("ssid", ssid.string()); + xml.attribute("bssid", bssid_buf); + + /* XXX we forcefully only support WPA/WPA2 psk for now */ + if (wpa || wpa2) + xml.attribute("protection", "WPA-PSK"); + }); + } + }); + } catch (...) { PWRN("could not report scan results"); } +}