123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- /*
- * hostapd / Client taxonomy
- * Copyright (c) 2015 Google, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- *
- * Parse a series of IEs, as in Probe Request or (Re)Association Request frames,
- * and render them to a descriptive string. The tag number of standard options
- * is written to the string, while the vendor ID and subtag are written for
- * vendor options.
- *
- * Example strings:
- * 0,1,50,45,221(00904c,51)
- * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2)
- */
- #include "utils/includes.h"
- #include "utils/common.h"
- #include "common/wpa_ctrl.h"
- #include "hostapd.h"
- #include "sta_info.h"
- /* Copy a string with no funny schtuff allowed; only alphanumerics. */
- static void no_mischief_strncpy(char *dst, const char *src, size_t n)
- {
- size_t i;
- for (i = 0; i < n; i++) {
- unsigned char s = src[i];
- int is_lower = s >= 'a' && s <= 'z';
- int is_upper = s >= 'A' && s <= 'Z';
- int is_digit = s >= '0' && s <= '9';
- if (is_lower || is_upper || is_digit) {
- /* TODO: if any manufacturer uses Unicode within the
- * WPS header, it will get mangled here. */
- dst[i] = s;
- } else {
- /* Note that even spaces will be transformed to
- * underscores, so 'Nexus 7' will turn into 'Nexus_7'.
- * This is deliberate, to make the string easier to
- * parse. */
- dst[i] = '_';
- }
- }
- }
- static int get_wps_name(char *name, size_t name_len,
- const u8 *data, size_t data_len)
- {
- /* Inside the WPS IE are a series of attributes, using two byte IDs
- * and two byte lengths. We're looking for the model name, if
- * present. */
- while (data_len >= 4) {
- u16 id, elen;
- id = WPA_GET_BE16(data);
- elen = WPA_GET_BE16(data + 2);
- data += 4;
- data_len -= 4;
- if (elen > data_len)
- return 0;
- if (id == 0x1023) {
- /* Model name, like 'Nexus 7' */
- size_t n = (elen < name_len) ? elen : name_len;
- no_mischief_strncpy(name, (const char *) data, n);
- return n;
- }
- data += elen;
- data_len -= elen;
- }
- return 0;
- }
- static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies)
- {
- char *fpos = fstr;
- char *fend = fstr + fstr_len;
- char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */
- char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */
- char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */
- char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */
- char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */
- char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */
- #define MAX_EXTCAP 254
- char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL
- */
- char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */
- #define WPS_NAME_LEN 32
- char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing
- * NUL */
- int num = 0;
- const u8 *ie;
- size_t ie_len;
- int ret;
- os_memset(htcap, 0, sizeof(htcap));
- os_memset(htagg, 0, sizeof(htagg));
- os_memset(htmcs, 0, sizeof(htmcs));
- os_memset(vhtcap, 0, sizeof(vhtcap));
- os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs));
- os_memset(vhttxmcs, 0, sizeof(vhttxmcs));
- os_memset(extcap, 0, sizeof(extcap));
- os_memset(txpow, 0, sizeof(txpow));
- os_memset(wps, 0, sizeof(wps));
- *fpos = '\0';
- if (!ies)
- return;
- ie = wpabuf_head(ies);
- ie_len = wpabuf_len(ies);
- while (ie_len >= 2) {
- u8 id, elen;
- char *sep = (num++ == 0) ? "" : ",";
- id = *ie++;
- elen = *ie++;
- ie_len -= 2;
- if (elen > ie_len)
- break;
- if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) {
- /* Vendor specific */
- if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) {
- /* WPS */
- char model_name[WPS_NAME_LEN + 1];
- const u8 *data = &ie[4];
- size_t data_len = elen - 4;
- os_memset(model_name, 0, sizeof(model_name));
- if (get_wps_name(model_name, WPS_NAME_LEN, data,
- data_len)) {
- os_snprintf(wps, sizeof(wps),
- ",wps:%s", model_name);
- }
- }
- ret = os_snprintf(fpos, fend - fpos,
- "%s%d(%02x%02x%02x,%d)",
- sep, id, ie[0], ie[1], ie[2], ie[3]);
- } else {
- if (id == WLAN_EID_HT_CAP && elen >= 2) {
- /* HT Capabilities (802.11n) */
- os_snprintf(htcap, sizeof(htcap),
- ",htcap:%04hx",
- WPA_GET_LE16(ie));
- }
- if (id == WLAN_EID_HT_CAP && elen >= 3) {
- /* HT Capabilities (802.11n), A-MPDU information
- */
- os_snprintf(htagg, sizeof(htagg),
- ",htagg:%02hx", (u16) ie[2]);
- }
- if (id == WLAN_EID_HT_CAP && elen >= 7) {
- /* HT Capabilities (802.11n), MCS information */
- os_snprintf(htmcs, sizeof(htmcs),
- ",htmcs:%08hx",
- (u16) WPA_GET_LE32(ie + 3));
- }
- if (id == WLAN_EID_VHT_CAP && elen >= 4) {
- /* VHT Capabilities (802.11ac) */
- os_snprintf(vhtcap, sizeof(vhtcap),
- ",vhtcap:%08x",
- WPA_GET_LE32(ie));
- }
- if (id == WLAN_EID_VHT_CAP && elen >= 8) {
- /* VHT Capabilities (802.11ac), RX MCS
- * information */
- os_snprintf(vhtrxmcs, sizeof(vhtrxmcs),
- ",vhtrxmcs:%08x",
- WPA_GET_LE32(ie + 4));
- }
- if (id == WLAN_EID_VHT_CAP && elen >= 12) {
- /* VHT Capabilities (802.11ac), TX MCS
- * information */
- os_snprintf(vhttxmcs, sizeof(vhttxmcs),
- ",vhttxmcs:%08x",
- WPA_GET_LE32(ie + 8));
- }
- if (id == WLAN_EID_EXT_CAPAB) {
- /* Extended Capabilities */
- int i;
- int len = (elen < MAX_EXTCAP) ? elen :
- MAX_EXTCAP;
- char *p = extcap;
- p += os_snprintf(extcap, sizeof(extcap),
- ",extcap:");
- for (i = 0; i < len; i++) {
- int lim;
- lim = sizeof(extcap) -
- os_strlen(extcap);
- if (lim <= 0)
- break;
- p += os_snprintf(p, lim, "%02x",
- *(ie + i));
- }
- }
- if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) {
- /* TX Power */
- os_snprintf(txpow, sizeof(txpow),
- ",txpow:%04hx",
- WPA_GET_LE16(ie));
- }
- ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id);
- }
- if (os_snprintf_error(fend - fpos, ret))
- goto fail;
- fpos += ret;
- ie += elen;
- ie_len -= elen;
- }
- ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s",
- htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs,
- txpow, extcap, wps);
- if (os_snprintf_error(fend - fpos, ret)) {
- fail:
- fstr[0] = '\0';
- }
- }
- int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
- struct sta_info *sta, char *buf, size_t buflen)
- {
- int ret;
- char *pos, *end;
- if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy)
- return 0;
- ret = os_snprintf(buf, buflen, "wifi4|probe:");
- if (os_snprintf_error(buflen, ret))
- return 0;
- pos = buf + ret;
- end = buf + buflen;
- ie_to_string(pos, end - pos, sta->probe_ie_taxonomy);
- pos = os_strchr(pos, '\0');
- if (pos >= end)
- return 0;
- ret = os_snprintf(pos, end - pos, "|assoc:");
- if (os_snprintf_error(end - pos, ret))
- return 0;
- pos += ret;
- ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy);
- pos = os_strchr(pos, '\0');
- return pos - buf;
- }
- void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
- struct sta_info *sta,
- const u8 *ie, size_t ie_len)
- {
- wpabuf_free(sta->probe_ie_taxonomy);
- sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
- }
- void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
- struct hostapd_sta_info *info,
- const u8 *ie, size_t ie_len)
- {
- wpabuf_free(info->probe_ie_taxonomy);
- info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
- }
- void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
- struct sta_info *sta,
- const u8 *ie, size_t ie_len)
- {
- wpabuf_free(sta->assoc_ie_taxonomy);
- sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
- }
|