From 6859f1cb2407f92606b9aa95d593c85b3dc8d625 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Fri, 26 Nov 2010 21:46:30 +0200 Subject: [PATCH] Enable sharing of scan result events among virtual interfaces When controlling multiple virtual interfaces on the same physical radio, share the scan results events with sibling interfaces. This decreases the time it takes to connect many virtual interfaces. This is currently only supported on Linux with cfg80211-based drivers when using nl80211 or wext driver interface. Signed-off-by: Ben Greear --- src/drivers/driver.h | 13 ++++++++++ src/drivers/driver_ndis.c | 3 ++- src/drivers/driver_nl80211.c | 48 ++++++++++++++++++++++++++++++++++++ src/drivers/driver_wext.c | 44 +++++++++++++++++++++++++++++++++ src/drivers/driver_wext.h | 1 + wpa_supplicant/events.c | 42 +++++++++++++++++++++++++++++-- 6 files changed, 148 insertions(+), 3 deletions(-) diff --git a/src/drivers/driver.h b/src/drivers/driver.h index eaaef00a3..956403a15 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1924,6 +1924,19 @@ struct wpa_driver_ops { * set_intra_bss - Enables/Disables intra BSS bridging */ int (*set_intra_bss)(void *priv, int enabled); + + /** + * get_radio_name - Get physical radio name for the device + * @priv: Private driver interface data + * Returns: Radio name or %NULL if not known + * + * The returned data must not be modified by the caller. It is assumed + * that any interface that has the same radio name as another is + * sharing the same physical radio. This information can be used to + * share scan results etc. information between the virtual interfaces + * to speed up various operations. + */ + const char * (*get_radio_name)(void *priv); }; diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index 5bfd61369..34bfa2bc0 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -3309,5 +3309,6 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = { NULL /* set_noa */, NULL /* set_p2p_powersave */, NULL /* ampdu */, - NULL /* set_intra_bss */ + NULL /* set_intra_bss */, + NULL /* get_radio_name */ }; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index a53cbe04a..062145869 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -18,6 +18,9 @@ #include "includes.h" #include +#include +#include +#include #include #include #include @@ -116,6 +119,7 @@ struct wpa_driver_nl80211_data { struct nl80211_global *global; struct dl_list list; u8 addr[ETH_ALEN]; + char phyname[32]; void *ctx; struct netlink_data *netlink; int ioctl_sock; /* socket for ioctl() use */ @@ -1654,6 +1658,39 @@ static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) } +static void nl80211_get_phy_name(struct wpa_driver_nl80211_data *drv) +{ + /* Find phy (radio) to which this interface belongs */ + char buf[90], *pos; + int f, rv; + + drv->phyname[0] = '\0'; + snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name", + drv->first_bss.ifname); + f = open(buf, O_RDONLY); + if (f < 0) { + wpa_printf(MSG_DEBUG, "Could not open file %s: %s", + buf, strerror(errno)); + return; + } + + rv = read(f, drv->phyname, sizeof(drv->phyname) - 1); + close(f); + if (rv < 0) { + wpa_printf(MSG_DEBUG, "Could not read file %s: %s", + buf, strerror(errno)); + return; + } + + drv->phyname[rv] = '\0'; + pos = os_strchr(drv->phyname, '\n'); + if (pos) + *pos = '\0'; + wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s", + drv->first_bss.ifname, drv->phyname); +} + + /** * wpa_driver_nl80211_init - Initialize nl80211 driver interface * @ctx: context to be used when calling wpa_supplicant functions, @@ -1689,6 +1726,8 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, return NULL; } + nl80211_get_phy_name(drv); + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { perror("socket(PF_INET,SOCK_DGRAM)"); @@ -6095,6 +6134,14 @@ static void nl80211_global_deinit(void *priv) } +static const char * nl80211_get_radio_name(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + return drv->phyname; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -6158,4 +6205,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .send_frame = nl80211_send_frame, .set_intra_bss = nl80211_set_intra_bss, .set_param = nl80211_set_param, + .get_radio_name = nl80211_get_radio_name, }; diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index f0de6aae3..ecb03aeb5 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -20,7 +20,9 @@ #include "includes.h" #include +#include #include +#include #include #include "wireless_copy.h" @@ -724,6 +726,39 @@ static void wpa_driver_wext_rfkill_unblocked(void *ctx) } +static void wext_get_phy_name(struct wpa_driver_wext_data *drv) +{ + /* Find phy (radio) to which this interface belongs */ + char buf[90], *pos; + int f, rv; + + drv->phyname[0] = '\0'; + snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name", + drv->ifname); + f = open(buf, O_RDONLY); + if (f < 0) { + wpa_printf(MSG_DEBUG, "Could not open file %s: %s", + buf, strerror(errno)); + return; + } + + rv = read(f, drv->phyname, sizeof(drv->phyname) - 1); + close(f); + if (rv < 0) { + wpa_printf(MSG_DEBUG, "Could not read file %s: %s", + buf, strerror(errno)); + return; + } + + drv->phyname[rv] = '\0'; + pos = os_strchr(drv->phyname, '\n'); + if (pos) + *pos = '\0'; + wpa_printf(MSG_DEBUG, "wext: interface %s phy: %s", + drv->ifname, drv->phyname); +} + + /** * wpa_driver_wext_init - Initialize WE driver interface * @ctx: context to be used when calling wpa_supplicant functions, @@ -749,6 +784,7 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) if (stat(path, &buf) == 0) { wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected"); drv->cfg80211 = 1; + wext_get_phy_name(drv); } drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); @@ -2255,6 +2291,13 @@ int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv) } +static const char * wext_get_radio_name(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + return drv->phyname; +} + + const struct wpa_driver_ops wpa_driver_wext_ops = { .name = "wext", .desc = "Linux wireless extensions (generic)", @@ -2274,4 +2317,5 @@ const struct wpa_driver_ops wpa_driver_wext_ops = { .flush_pmkid = wpa_driver_wext_flush_pmkid, .get_capa = wpa_driver_wext_get_capa, .set_operstate = wpa_driver_wext_set_operstate, + .get_radio_name = wext_get_radio_name, }; diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h index 6382bbbcd..89c13eb75 100644 --- a/src/drivers/driver_wext.h +++ b/src/drivers/driver_wext.h @@ -23,6 +23,7 @@ struct wpa_driver_wext_data { int ioctl_sock; int mlme_sock; char ifname[IFNAMSIZ + 1]; + char phyname[32]; int ifindex; int ifindex2; int if_removed; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 86f995823..51cd116ea 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -828,8 +828,8 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, } -static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) +static void _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) { struct wpa_bss *selected; struct wpa_ssid *ssid = NULL; @@ -944,6 +944,44 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, } } } + + +static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + const char *rn, *rn2; + struct wpa_supplicant *ifs; + + _wpa_supplicant_event_scan_results(wpa_s, data); + + /* + * Check other interfaces to see if they have the same radio-name. If + * so, they get updated with this same scan info. + */ + if (!wpa_s->driver->get_radio_name) + return; + + rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); + if (rn == NULL || rn[0] == '\0') + return; + + wpa_printf(MSG_DEBUG, "%s: Checking for other virtual interfaces " + "sharing same radio (%s) in event_scan_results", + wpa_s->ifname, rn); + + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + if (ifs == wpa_s || !ifs->driver->get_radio_name) + continue; + + rn2 = ifs->driver->get_radio_name(ifs->drv_priv); + if (rn2 && os_strcmp(rn, rn2) == 0) { + wpa_printf(MSG_DEBUG, "%s: Updating scan results from " + "sibling", ifs->ifname); + _wpa_supplicant_event_scan_results(ifs, data); + } + } +} + #endif /* CONFIG_NO_SCAN_PROCESSING */