+ if (ies_len < 2)
+ return;
+ if (ies_len < (uint32_t)(2 + ies[1]))
+ return;
+
+ *ssid_len = ies[1];
+ *ssid = ies + 2;
+}
+
+static int gwi_sta_cb(struct nl_msg *msg, void *data) {
+ wireless_info_t *info = data;
+
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+ struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+ static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+ [NL80211_STA_INFO_RX_BITRATE] = {.type = NLA_NESTED},
+ };
+
+ static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+ [NL80211_RATE_INFO_BITRATE] = {.type = NLA_U16},
+ };
+
+ if (nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
+ return NL_SKIP;
+
+ if (tb[NL80211_ATTR_STA_INFO] == NULL)
+ return NL_SKIP;
+
+ if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, tb[NL80211_ATTR_STA_INFO], stats_policy))
+ return NL_SKIP;
+
+ if (sinfo[NL80211_STA_INFO_RX_BITRATE] == NULL)
+ return NL_SKIP;
+
+ if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, sinfo[NL80211_STA_INFO_RX_BITRATE], rate_policy))
+ return NL_SKIP;
+
+ if (rinfo[NL80211_RATE_INFO_BITRATE] == NULL)
+ return NL_SKIP;
+
+ // NL80211_RATE_INFO_BITRATE is specified in units of 100 kbit/s, but iw
+ // used to specify bit/s, so we convert to use the same code path.
+ info->bitrate = (int)nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]) * 100 * 1000;
+
+ return NL_SKIP;
+}
+
+static int gwi_scan_cb(struct nl_msg *msg, void *data) {
+ wireless_info_t *info = data;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct nlattr *bss[NL80211_BSS_MAX + 1];
+ struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+ [NL80211_BSS_FREQUENCY] = {.type = NLA_U32},
+ [NL80211_BSS_BSSID] = {.type = NLA_UNSPEC},
+ [NL80211_BSS_INFORMATION_ELEMENTS] = {.type = NLA_UNSPEC},
+ [NL80211_BSS_SIGNAL_MBM] = {.type = NLA_U32},
+ [NL80211_BSS_SIGNAL_UNSPEC] = {.type = NLA_U8},
+ [NL80211_BSS_STATUS] = {.type = NLA_U32},
+ };
+
+ if (nla_parse(tb, NL80211_ATTR_MAX, 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(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy))
+ return NL_SKIP;
+
+ if (bss[NL80211_BSS_STATUS] == NULL)
+ return NL_SKIP;
+
+ const uint32_t 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->flags |= WIRELESS_INFO_FLAG_HAS_FREQUENCY;
+ info->frequency = (double)nla_get_u32(bss[NL80211_BSS_FREQUENCY]) * 1e6;
+ }
+
+ if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
+ info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
+ info->signal_level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
+ info->signal_level_max = 100;
+
+ info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY;
+ info->quality = info->signal_level;
+ info->quality_max = 100;
+ info->quality_average = 50;
+ }
+
+ if (bss[NL80211_BSS_SIGNAL_MBM]) {
+ info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
+ info->signal_level = (int)nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]) / 100;
+
+ info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY;
+ info->quality = nl80211_xbm_to_percent(nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]), 100);
+ info->quality_max = 100;
+ info->quality_average = 50;
+ }
+
+ if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+ uint8_t *ssid;
+ uint32_t 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) {
+ info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID;
+ snprintf(info->essid, sizeof(info->essid), "%.*s", ssid_len, ssid);
+ }
+ }
+
+ return NL_SKIP;