summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Zaborowski <andrew.zaborowski@intel.com>2020-10-14 02:38:08 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2020-10-22 16:58:27 +0200
commit277e8eaa1fbcea0c8fb35c719d9570730f178c4e (patch)
tree8e8361a5876477e027c7fee9020521795a3b4485
parentc6456dd104b204a44e2964a7cf443efb87d4661d (diff)
platform/wifi: Use GET_STATION instead of GET_SCAN dumps
As an optimization, use the NL80211_CMD_GET_STATION dump data instead of the NL80211_CMD_GET_SCAN dump + GET_STATION command (non-dump) to implement the following methods: wifi_nl80211_get_bssid wifi_nl80211_get_rate wifi_nl80211_get_qual GET_STATION records vary in size from a few hundred bytes to a few kB. GET_SCAN records are usually on the few hundred bytes side, but there can be many of them. In managed mode there will only be one GET_STATION record. In AdHoc mode there may be more. These methods are not used in AP or Mesh mode. So without that patch we'd have a GET_SCAN dump that could be quite big and then a GET_STATION with one record. Now it should be a GET_STATION dump with one record or a few records, in any case fewer synchronous commands is better. Additionally this should now not depend on the currently-connected BSS being in the kernel's scan result cache. The downside is that the signal strength is "optional" in the GET_STATION records, depends on the driver's capabilities. Most mainline drivers do seem to include it (the mac80211 based ones and a few full-mac ones) but I can't know if all of them do.
-rw-r--r--src/platform/wifi/nm-wifi-utils-nl80211.c237
1 files changed, 64 insertions, 173 deletions
diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.c b/src/platform/wifi/nm-wifi-utils-nl80211.c
index cad291b47..947908444 100644
--- a/src/platform/wifi/nm-wifi-utils-nl80211.c
+++ b/src/platform/wifi/nm-wifi-utils-nl80211.c
@@ -363,133 +363,6 @@ nla_put_failure:
g_return_val_if_reached(FALSE);
}
-/* @divisor: pass what value @xbm should be divided by to get dBm */
-static guint32
-nl80211_xbm_to_percent(gint32 xbm, guint32 divisor)
-{
-#define NOISE_FLOOR_DBM -90
-#define SIGNAL_MAX_DBM -20
-
- xbm /= divisor;
- xbm = CLAMP(xbm, NOISE_FLOOR_DBM, SIGNAL_MAX_DBM);
-
- return 100
- - 70
- * (((float) SIGNAL_MAX_DBM - (float) xbm)
- / ((float) SIGNAL_MAX_DBM - (float) NOISE_FLOOR_DBM));
-}
-
-struct nl80211_bss_info {
- guint32 freq;
- guint8 bssid[ETH_ALEN];
- guint8 ssid[32];
- guint32 ssid_len;
- guint32 beacon_signal;
- gboolean valid;
-};
-
-#define WLAN_EID_SSID 0
-
-static void
-find_ssid(guint8 *ies, guint32 ies_len, guint8 **ssid, guint32 *ssid_len)
-{
- *ssid = NULL;
- *ssid_len = 0;
-
- while (ies_len > 2 && ies[0] != WLAN_EID_SSID) {
- ies_len -= ies[1] + 2;
- ies += ies[1] + 2;
- }
- if (ies_len < 2)
- return;
- if (ies_len < 2 + ies[1])
- return;
-
- *ssid_len = ies[1];
- *ssid = ies + 2;
-}
-
-static int
-nl80211_bss_dump_handler(struct nl_msg *msg, void *arg)
-{
- static const struct nla_policy bss_policy[] = {
- [NL80211_BSS_TSF] = {.type = NLA_U64},
- [NL80211_BSS_FREQUENCY] = {.type = NLA_U32},
- [NL80211_BSS_BSSID] = {.minlen = ETH_ALEN},
- [NL80211_BSS_BEACON_INTERVAL] = {.type = NLA_U16},
- [NL80211_BSS_CAPABILITY] = {.type = NLA_U16},
- [NL80211_BSS_INFORMATION_ELEMENTS] = {},
- [NL80211_BSS_SIGNAL_MBM] = {.type = NLA_U32},
- [NL80211_BSS_SIGNAL_UNSPEC] = {.type = NLA_U8},
- [NL80211_BSS_STATUS] = {.type = NLA_U32},
- };
- struct nl80211_bss_info *info = arg;
- struct genlmsghdr * gnlh = nlmsg_data(nlmsg_hdr(msg));
- struct nlattr * tb[NL80211_ATTR_MAX + 1];
- struct nlattr * bss[G_N_ELEMENTS(bss_policy)];
- guint32 status;
-
- if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
- return NL_SKIP;
-
- if (tb[NL80211_ATTR_BSS] == NULL)
- return NL_SKIP;
-
- if (nla_parse_nested_arr(bss, tb[NL80211_ATTR_BSS], bss_policy))
- return NL_SKIP;
-
- if (bss[NL80211_BSS_STATUS] == NULL)
- return NL_SKIP;
-
- status = nla_get_u32(bss[NL80211_BSS_STATUS]);
-
- if (status != NL80211_BSS_STATUS_ASSOCIATED && status != NL80211_BSS_STATUS_IBSS_JOINED)
- return NL_SKIP;
-
- if (bss[NL80211_BSS_BSSID] == NULL)
- return NL_SKIP;
- memcpy(info->bssid, nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
-
- if (bss[NL80211_BSS_FREQUENCY])
- info->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
-
- if (bss[NL80211_BSS_SIGNAL_UNSPEC])
- info->beacon_signal = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
-
- if (bss[NL80211_BSS_SIGNAL_MBM])
- info->beacon_signal = nl80211_xbm_to_percent(nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]), 100);
-
- if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
- guint8 *ssid;
- guint32 ssid_len;
-
- find_ssid(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
- nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
- &ssid,
- &ssid_len);
- if (ssid && ssid_len && ssid_len <= sizeof(info->ssid)) {
- memcpy(info->ssid, ssid, ssid_len);
- info->ssid_len = ssid_len;
- }
- }
-
- info->valid = TRUE;
-
- return NL_SKIP;
-}
-
-static void
-nl80211_get_bss_info(NMWifiUtilsNl80211 *self, struct nl80211_bss_info *bss_info)
-{
- nm_auto_nlmsg struct nl_msg *msg = NULL;
-
- memset(bss_info, 0, sizeof(*bss_info));
-
- msg = nl80211_alloc_msg(self, NL80211_CMD_GET_SCAN, NLM_F_DUMP);
-
- nl80211_send_and_recv(self, msg, nl80211_bss_dump_handler, bss_info);
-}
-
static guint32
wifi_nl80211_get_freq(NMWifiUtils *data)
{
@@ -521,21 +394,25 @@ wifi_nl80211_find_freq(NMWifiUtils *data, const guint32 *freqs)
return 0;
}
-static gboolean
-wifi_nl80211_get_bssid(NMWifiUtils *data, guint8 *out_bssid)
+/* @divisor: pass what value @xbm should be divided by to get dBm */
+static guint32
+nl80211_xbm_to_percent(gint32 xbm, guint32 divisor)
{
- NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data;
- struct nl80211_bss_info bss_info;
-
- nl80211_get_bss_info(self, &bss_info);
+#define NOISE_FLOOR_DBM -90
+#define SIGNAL_MAX_DBM -20
- if (bss_info.valid)
- memcpy(out_bssid, bss_info.bssid, ETH_ALEN);
+ xbm /= divisor;
+ xbm = CLAMP(xbm, NOISE_FLOOR_DBM, SIGNAL_MAX_DBM);
- return bss_info.valid;
+ return 100
+ - 70
+ * (((float) SIGNAL_MAX_DBM - (float) xbm)
+ / ((float) SIGNAL_MAX_DBM - (float) NOISE_FLOOR_DBM));
}
struct nl80211_station_info {
+ gboolean valid;
+ guint8 bssid[ETH_ALEN];
guint32 txrate;
gboolean txrate_valid;
guint8 signal;
@@ -543,19 +420,21 @@ struct nl80211_station_info {
};
static int
-nl80211_station_handler(struct nl_msg *msg, void *arg)
+nl80211_station_dump_handler(struct nl_msg *msg, void *arg)
{
static const struct nla_policy stats_policy[] = {
- [NL80211_STA_INFO_INACTIVE_TIME] = {.type = NLA_U32},
- [NL80211_STA_INFO_RX_BYTES] = {.type = NLA_U32},
- [NL80211_STA_INFO_TX_BYTES] = {.type = NLA_U32},
- [NL80211_STA_INFO_RX_PACKETS] = {.type = NLA_U32},
- [NL80211_STA_INFO_TX_PACKETS] = {.type = NLA_U32},
- [NL80211_STA_INFO_SIGNAL] = {.type = NLA_U8},
- [NL80211_STA_INFO_TX_BITRATE] = {.type = NLA_NESTED},
- [NL80211_STA_INFO_LLID] = {.type = NLA_U16},
- [NL80211_STA_INFO_PLID] = {.type = NLA_U16},
- [NL80211_STA_INFO_PLINK_STATE] = {.type = NLA_U8},
+ [NL80211_STA_INFO_INACTIVE_TIME] = {.type = NLA_U32},
+ [NL80211_STA_INFO_RX_BYTES] = {.type = NLA_U32},
+ [NL80211_STA_INFO_TX_BYTES] = {.type = NLA_U32},
+ [NL80211_STA_INFO_RX_PACKETS] = {.type = NLA_U32},
+ [NL80211_STA_INFO_TX_PACKETS] = {.type = NLA_U32},
+ [NL80211_STA_INFO_SIGNAL] = {.type = NLA_U8},
+ [NL80211_STA_INFO_TX_BITRATE] = {.type = NLA_NESTED},
+ [NL80211_STA_INFO_LLID] = {.type = NLA_U16},
+ [NL80211_STA_INFO_PLID] = {.type = NLA_U16},
+ [NL80211_STA_INFO_PLINK_STATE] = {.type = NLA_U8},
+ [NL80211_STA_INFO_STA_FLAGS] = {.minlen = sizeof(struct nl80211_sta_flag_update)},
+ [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = {.type = NLA_U8},
};
static const struct nla_policy rate_policy[] = {
[NL80211_RATE_INFO_BITRATE] = {.type = NLA_U16},
@@ -572,59 +451,71 @@ nl80211_station_handler(struct nl_msg *msg, void *arg)
if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
return NL_SKIP;
+ if (tb[NL80211_ATTR_MAC] == NULL)
+ return NL_SKIP;
+
if (tb[NL80211_ATTR_STA_INFO] == NULL)
return NL_SKIP;
if (nla_parse_nested_arr(sinfo, tb[NL80211_ATTR_STA_INFO], stats_policy))
return NL_SKIP;
- if (sinfo[NL80211_STA_INFO_TX_BITRATE] == NULL)
- return NL_SKIP;
+ if (sinfo[NL80211_STA_INFO_STA_FLAGS] != NULL) {
+ const struct nl80211_sta_flag_update *flags = nla_data(sinfo[NL80211_STA_INFO_STA_FLAGS]);
- if (nla_parse_nested_arr(rinfo, sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy))
- return NL_SKIP;
+ if (flags->mask & ~flags->set & (1 << NL80211_STA_FLAG_ASSOCIATED))
+ return NL_SKIP;
+ }
- if (rinfo[NL80211_RATE_INFO_BITRATE] == NULL)
- return NL_SKIP;
+ memcpy(info->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+ info->valid = TRUE;
- /* convert from nl80211's units of 100kbps to NM's kbps */
- info->txrate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]) * 100;
- info->txrate_valid = TRUE;
+ if (sinfo[NL80211_STA_INFO_TX_BITRATE] != NULL
+ && !nla_parse_nested_arr(rinfo, sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy)
+ && rinfo[NL80211_RATE_INFO_BITRATE] != NULL) {
+ /* convert from nl80211's units of 100kbps to NM's kbps */
+ info->txrate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]) * 100;
+ info->txrate_valid = TRUE;
+ }
if (sinfo[NL80211_STA_INFO_SIGNAL] != NULL) {
info->signal =
nl80211_xbm_to_percent((gint8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]), 1);
info->signal_valid = TRUE;
+ } else if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG] != NULL) {
+ /* Fall back to beacon signal strength */
+ info->signal =
+ nl80211_xbm_to_percent((gint8) nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]), 1);
+ info->signal_valid = TRUE;
}
return NL_SKIP;
}
static void
-nl80211_get_ap_info(NMWifiUtilsNl80211 *self, struct nl80211_station_info *sta_info)
+nl80211_get_sta_info(NMWifiUtilsNl80211 *self, struct nl80211_station_info *sta_info)
{
nm_auto_nlmsg struct nl_msg *msg = NULL;
- struct nl80211_bss_info bss_info;
memset(sta_info, 0, sizeof(*sta_info));
- nl80211_get_bss_info(self, &bss_info);
- if (!bss_info.valid)
- return;
+ msg = nl80211_alloc_msg(self, NL80211_CMD_GET_STATION, NLM_F_DUMP);
- msg = nl80211_alloc_msg(self, NL80211_CMD_GET_STATION, 0);
- NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bss_info.bssid);
+ nl80211_send_and_recv(self, msg, nl80211_station_dump_handler, sta_info);
+}
- nl80211_send_and_recv(self, msg, nl80211_station_handler, sta_info);
- if (!sta_info->signal_valid) {
- /* Fall back to bss_info signal quality (both are in percent) */
- sta_info->signal = bss_info.beacon_signal;
- }
+static gboolean
+wifi_nl80211_get_bssid(NMWifiUtils *data, guint8 *out_bssid)
+{
+ NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data;
+ struct nl80211_station_info sta_info;
- return;
+ nl80211_get_sta_info(self, &sta_info);
-nla_put_failure:
- g_return_if_reached();
+ if (sta_info.valid)
+ memcpy(out_bssid, sta_info.bssid, ETH_ALEN);
+
+ return sta_info.valid;
}
static guint32
@@ -633,7 +524,7 @@ wifi_nl80211_get_rate(NMWifiUtils *data)
NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data;
struct nl80211_station_info sta_info;
- nl80211_get_ap_info(self, &sta_info);
+ nl80211_get_sta_info(self, &sta_info);
return sta_info.txrate;
}
@@ -644,7 +535,7 @@ wifi_nl80211_get_qual(NMWifiUtils *data)
NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data;
struct nl80211_station_info sta_info;
- nl80211_get_ap_info(self, &sta_info);
+ nl80211_get_sta_info(self, &sta_info);
return sta_info.signal;
}