1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * hostapd / Client taxonomy 3e5b75505Sopenharmony_ci * Copyright (c) 2015 Google, Inc. 4e5b75505Sopenharmony_ci * 5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 6e5b75505Sopenharmony_ci * See README for more details. 7e5b75505Sopenharmony_ci * 8e5b75505Sopenharmony_ci * Parse a series of IEs, as in Probe Request or (Re)Association Request frames, 9e5b75505Sopenharmony_ci * and render them to a descriptive string. The tag number of standard options 10e5b75505Sopenharmony_ci * is written to the string, while the vendor ID and subtag are written for 11e5b75505Sopenharmony_ci * vendor options. 12e5b75505Sopenharmony_ci * 13e5b75505Sopenharmony_ci * Example strings: 14e5b75505Sopenharmony_ci * 0,1,50,45,221(00904c,51) 15e5b75505Sopenharmony_ci * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2) 16e5b75505Sopenharmony_ci */ 17e5b75505Sopenharmony_ci 18e5b75505Sopenharmony_ci#include "utils/includes.h" 19e5b75505Sopenharmony_ci 20e5b75505Sopenharmony_ci#include "utils/common.h" 21e5b75505Sopenharmony_ci#include "common/wpa_ctrl.h" 22e5b75505Sopenharmony_ci#include "hostapd.h" 23e5b75505Sopenharmony_ci#include "sta_info.h" 24e5b75505Sopenharmony_ci#include "taxonomy.h" 25e5b75505Sopenharmony_ci 26e5b75505Sopenharmony_ci 27e5b75505Sopenharmony_ci/* Copy a string with no funny schtuff allowed; only alphanumerics. */ 28e5b75505Sopenharmony_cistatic void no_mischief_strncpy(char *dst, const char *src, size_t n) 29e5b75505Sopenharmony_ci{ 30e5b75505Sopenharmony_ci size_t i; 31e5b75505Sopenharmony_ci 32e5b75505Sopenharmony_ci for (i = 0; i < n; i++) { 33e5b75505Sopenharmony_ci unsigned char s = src[i]; 34e5b75505Sopenharmony_ci int is_lower = s >= 'a' && s <= 'z'; 35e5b75505Sopenharmony_ci int is_upper = s >= 'A' && s <= 'Z'; 36e5b75505Sopenharmony_ci int is_digit = s >= '0' && s <= '9'; 37e5b75505Sopenharmony_ci 38e5b75505Sopenharmony_ci if (is_lower || is_upper || is_digit) { 39e5b75505Sopenharmony_ci /* TODO: if any manufacturer uses Unicode within the 40e5b75505Sopenharmony_ci * WPS header, it will get mangled here. */ 41e5b75505Sopenharmony_ci dst[i] = s; 42e5b75505Sopenharmony_ci } else { 43e5b75505Sopenharmony_ci /* Note that even spaces will be transformed to 44e5b75505Sopenharmony_ci * underscores, so 'Nexus 7' will turn into 'Nexus_7'. 45e5b75505Sopenharmony_ci * This is deliberate, to make the string easier to 46e5b75505Sopenharmony_ci * parse. */ 47e5b75505Sopenharmony_ci dst[i] = '_'; 48e5b75505Sopenharmony_ci } 49e5b75505Sopenharmony_ci } 50e5b75505Sopenharmony_ci} 51e5b75505Sopenharmony_ci 52e5b75505Sopenharmony_ci 53e5b75505Sopenharmony_cistatic int get_wps_name(char *name, size_t name_len, 54e5b75505Sopenharmony_ci const u8 *data, size_t data_len) 55e5b75505Sopenharmony_ci{ 56e5b75505Sopenharmony_ci /* Inside the WPS IE are a series of attributes, using two byte IDs 57e5b75505Sopenharmony_ci * and two byte lengths. We're looking for the model name, if 58e5b75505Sopenharmony_ci * present. */ 59e5b75505Sopenharmony_ci while (data_len >= 4) { 60e5b75505Sopenharmony_ci u16 id, elen; 61e5b75505Sopenharmony_ci 62e5b75505Sopenharmony_ci id = WPA_GET_BE16(data); 63e5b75505Sopenharmony_ci elen = WPA_GET_BE16(data + 2); 64e5b75505Sopenharmony_ci data += 4; 65e5b75505Sopenharmony_ci data_len -= 4; 66e5b75505Sopenharmony_ci 67e5b75505Sopenharmony_ci if (elen > data_len) 68e5b75505Sopenharmony_ci return 0; 69e5b75505Sopenharmony_ci 70e5b75505Sopenharmony_ci if (id == 0x1023) { 71e5b75505Sopenharmony_ci /* Model name, like 'Nexus 7' */ 72e5b75505Sopenharmony_ci size_t n = (elen < name_len) ? elen : name_len; 73e5b75505Sopenharmony_ci no_mischief_strncpy(name, (const char *) data, n); 74e5b75505Sopenharmony_ci return n; 75e5b75505Sopenharmony_ci } 76e5b75505Sopenharmony_ci 77e5b75505Sopenharmony_ci data += elen; 78e5b75505Sopenharmony_ci data_len -= elen; 79e5b75505Sopenharmony_ci } 80e5b75505Sopenharmony_ci 81e5b75505Sopenharmony_ci return 0; 82e5b75505Sopenharmony_ci} 83e5b75505Sopenharmony_ci 84e5b75505Sopenharmony_ci 85e5b75505Sopenharmony_cistatic void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies) 86e5b75505Sopenharmony_ci{ 87e5b75505Sopenharmony_ci char *fpos = fstr; 88e5b75505Sopenharmony_ci char *fend = fstr + fstr_len; 89e5b75505Sopenharmony_ci char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */ 90e5b75505Sopenharmony_ci char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */ 91e5b75505Sopenharmony_ci char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */ 92e5b75505Sopenharmony_ci char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */ 93e5b75505Sopenharmony_ci char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */ 94e5b75505Sopenharmony_ci char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */ 95e5b75505Sopenharmony_ci#define MAX_EXTCAP 254 96e5b75505Sopenharmony_ci char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL 97e5b75505Sopenharmony_ci */ 98e5b75505Sopenharmony_ci char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */ 99e5b75505Sopenharmony_ci#define WPS_NAME_LEN 32 100e5b75505Sopenharmony_ci char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing 101e5b75505Sopenharmony_ci * NUL */ 102e5b75505Sopenharmony_ci int num = 0; 103e5b75505Sopenharmony_ci const u8 *ie; 104e5b75505Sopenharmony_ci size_t ie_len; 105e5b75505Sopenharmony_ci int ret; 106e5b75505Sopenharmony_ci 107e5b75505Sopenharmony_ci os_memset(htcap, 0, sizeof(htcap)); 108e5b75505Sopenharmony_ci os_memset(htagg, 0, sizeof(htagg)); 109e5b75505Sopenharmony_ci os_memset(htmcs, 0, sizeof(htmcs)); 110e5b75505Sopenharmony_ci os_memset(vhtcap, 0, sizeof(vhtcap)); 111e5b75505Sopenharmony_ci os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs)); 112e5b75505Sopenharmony_ci os_memset(vhttxmcs, 0, sizeof(vhttxmcs)); 113e5b75505Sopenharmony_ci os_memset(extcap, 0, sizeof(extcap)); 114e5b75505Sopenharmony_ci os_memset(txpow, 0, sizeof(txpow)); 115e5b75505Sopenharmony_ci os_memset(wps, 0, sizeof(wps)); 116e5b75505Sopenharmony_ci *fpos = '\0'; 117e5b75505Sopenharmony_ci 118e5b75505Sopenharmony_ci if (!ies) 119e5b75505Sopenharmony_ci return; 120e5b75505Sopenharmony_ci ie = wpabuf_head(ies); 121e5b75505Sopenharmony_ci ie_len = wpabuf_len(ies); 122e5b75505Sopenharmony_ci 123e5b75505Sopenharmony_ci while (ie_len >= 2) { 124e5b75505Sopenharmony_ci u8 id, elen; 125e5b75505Sopenharmony_ci char *sep = (num++ == 0) ? "" : ","; 126e5b75505Sopenharmony_ci 127e5b75505Sopenharmony_ci id = *ie++; 128e5b75505Sopenharmony_ci elen = *ie++; 129e5b75505Sopenharmony_ci ie_len -= 2; 130e5b75505Sopenharmony_ci 131e5b75505Sopenharmony_ci if (elen > ie_len) 132e5b75505Sopenharmony_ci break; 133e5b75505Sopenharmony_ci 134e5b75505Sopenharmony_ci if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) { 135e5b75505Sopenharmony_ci /* Vendor specific */ 136e5b75505Sopenharmony_ci if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) { 137e5b75505Sopenharmony_ci /* WPS */ 138e5b75505Sopenharmony_ci char model_name[WPS_NAME_LEN + 1]; 139e5b75505Sopenharmony_ci const u8 *data = &ie[4]; 140e5b75505Sopenharmony_ci size_t data_len = elen - 4; 141e5b75505Sopenharmony_ci 142e5b75505Sopenharmony_ci os_memset(model_name, 0, sizeof(model_name)); 143e5b75505Sopenharmony_ci if (get_wps_name(model_name, WPS_NAME_LEN, data, 144e5b75505Sopenharmony_ci data_len)) { 145e5b75505Sopenharmony_ci os_snprintf(wps, sizeof(wps), 146e5b75505Sopenharmony_ci ",wps:%s", model_name); 147e5b75505Sopenharmony_ci } 148e5b75505Sopenharmony_ci } 149e5b75505Sopenharmony_ci 150e5b75505Sopenharmony_ci ret = os_snprintf(fpos, fend - fpos, 151e5b75505Sopenharmony_ci "%s%d(%02x%02x%02x,%d)", 152e5b75505Sopenharmony_ci sep, id, ie[0], ie[1], ie[2], ie[3]); 153e5b75505Sopenharmony_ci } else { 154e5b75505Sopenharmony_ci if (id == WLAN_EID_HT_CAP && elen >= 2) { 155e5b75505Sopenharmony_ci /* HT Capabilities (802.11n) */ 156e5b75505Sopenharmony_ci os_snprintf(htcap, sizeof(htcap), 157e5b75505Sopenharmony_ci ",htcap:%04hx", 158e5b75505Sopenharmony_ci WPA_GET_LE16(ie)); 159e5b75505Sopenharmony_ci } 160e5b75505Sopenharmony_ci if (id == WLAN_EID_HT_CAP && elen >= 3) { 161e5b75505Sopenharmony_ci /* HT Capabilities (802.11n), A-MPDU information 162e5b75505Sopenharmony_ci */ 163e5b75505Sopenharmony_ci os_snprintf(htagg, sizeof(htagg), 164e5b75505Sopenharmony_ci ",htagg:%02hx", (u16) ie[2]); 165e5b75505Sopenharmony_ci } 166e5b75505Sopenharmony_ci if (id == WLAN_EID_HT_CAP && elen >= 7) { 167e5b75505Sopenharmony_ci /* HT Capabilities (802.11n), MCS information */ 168e5b75505Sopenharmony_ci os_snprintf(htmcs, sizeof(htmcs), 169e5b75505Sopenharmony_ci ",htmcs:%08hx", 170e5b75505Sopenharmony_ci (u16) WPA_GET_LE32(ie + 3)); 171e5b75505Sopenharmony_ci } 172e5b75505Sopenharmony_ci if (id == WLAN_EID_VHT_CAP && elen >= 4) { 173e5b75505Sopenharmony_ci /* VHT Capabilities (802.11ac) */ 174e5b75505Sopenharmony_ci os_snprintf(vhtcap, sizeof(vhtcap), 175e5b75505Sopenharmony_ci ",vhtcap:%08x", 176e5b75505Sopenharmony_ci WPA_GET_LE32(ie)); 177e5b75505Sopenharmony_ci } 178e5b75505Sopenharmony_ci if (id == WLAN_EID_VHT_CAP && elen >= 8) { 179e5b75505Sopenharmony_ci /* VHT Capabilities (802.11ac), RX MCS 180e5b75505Sopenharmony_ci * information */ 181e5b75505Sopenharmony_ci os_snprintf(vhtrxmcs, sizeof(vhtrxmcs), 182e5b75505Sopenharmony_ci ",vhtrxmcs:%08x", 183e5b75505Sopenharmony_ci WPA_GET_LE32(ie + 4)); 184e5b75505Sopenharmony_ci } 185e5b75505Sopenharmony_ci if (id == WLAN_EID_VHT_CAP && elen >= 12) { 186e5b75505Sopenharmony_ci /* VHT Capabilities (802.11ac), TX MCS 187e5b75505Sopenharmony_ci * information */ 188e5b75505Sopenharmony_ci os_snprintf(vhttxmcs, sizeof(vhttxmcs), 189e5b75505Sopenharmony_ci ",vhttxmcs:%08x", 190e5b75505Sopenharmony_ci WPA_GET_LE32(ie + 8)); 191e5b75505Sopenharmony_ci } 192e5b75505Sopenharmony_ci if (id == WLAN_EID_EXT_CAPAB) { 193e5b75505Sopenharmony_ci /* Extended Capabilities */ 194e5b75505Sopenharmony_ci int i; 195e5b75505Sopenharmony_ci int len = (elen < MAX_EXTCAP) ? elen : 196e5b75505Sopenharmony_ci MAX_EXTCAP; 197e5b75505Sopenharmony_ci char *p = extcap; 198e5b75505Sopenharmony_ci 199e5b75505Sopenharmony_ci p += os_snprintf(extcap, sizeof(extcap), 200e5b75505Sopenharmony_ci ",extcap:"); 201e5b75505Sopenharmony_ci for (i = 0; i < len; i++) { 202e5b75505Sopenharmony_ci int lim; 203e5b75505Sopenharmony_ci 204e5b75505Sopenharmony_ci lim = sizeof(extcap) - 205e5b75505Sopenharmony_ci os_strlen(extcap); 206e5b75505Sopenharmony_ci if (lim <= 0) 207e5b75505Sopenharmony_ci break; 208e5b75505Sopenharmony_ci p += os_snprintf(p, lim, "%02x", 209e5b75505Sopenharmony_ci *(ie + i)); 210e5b75505Sopenharmony_ci } 211e5b75505Sopenharmony_ci } 212e5b75505Sopenharmony_ci if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) { 213e5b75505Sopenharmony_ci /* TX Power */ 214e5b75505Sopenharmony_ci os_snprintf(txpow, sizeof(txpow), 215e5b75505Sopenharmony_ci ",txpow:%04hx", 216e5b75505Sopenharmony_ci WPA_GET_LE16(ie)); 217e5b75505Sopenharmony_ci } 218e5b75505Sopenharmony_ci 219e5b75505Sopenharmony_ci ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id); 220e5b75505Sopenharmony_ci } 221e5b75505Sopenharmony_ci if (os_snprintf_error(fend - fpos, ret)) 222e5b75505Sopenharmony_ci goto fail; 223e5b75505Sopenharmony_ci fpos += ret; 224e5b75505Sopenharmony_ci 225e5b75505Sopenharmony_ci ie += elen; 226e5b75505Sopenharmony_ci ie_len -= elen; 227e5b75505Sopenharmony_ci } 228e5b75505Sopenharmony_ci 229e5b75505Sopenharmony_ci ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s", 230e5b75505Sopenharmony_ci htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs, 231e5b75505Sopenharmony_ci txpow, extcap, wps); 232e5b75505Sopenharmony_ci if (os_snprintf_error(fend - fpos, ret)) { 233e5b75505Sopenharmony_ci fail: 234e5b75505Sopenharmony_ci fstr[0] = '\0'; 235e5b75505Sopenharmony_ci } 236e5b75505Sopenharmony_ci} 237e5b75505Sopenharmony_ci 238e5b75505Sopenharmony_ci 239e5b75505Sopenharmony_ciint retrieve_sta_taxonomy(const struct hostapd_data *hapd, 240e5b75505Sopenharmony_ci struct sta_info *sta, char *buf, size_t buflen) 241e5b75505Sopenharmony_ci{ 242e5b75505Sopenharmony_ci int ret; 243e5b75505Sopenharmony_ci char *pos, *end; 244e5b75505Sopenharmony_ci 245e5b75505Sopenharmony_ci if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy) 246e5b75505Sopenharmony_ci return 0; 247e5b75505Sopenharmony_ci 248e5b75505Sopenharmony_ci ret = os_snprintf(buf, buflen, "wifi4|probe:"); 249e5b75505Sopenharmony_ci if (os_snprintf_error(buflen, ret)) 250e5b75505Sopenharmony_ci return 0; 251e5b75505Sopenharmony_ci pos = buf + ret; 252e5b75505Sopenharmony_ci end = buf + buflen; 253e5b75505Sopenharmony_ci 254e5b75505Sopenharmony_ci ie_to_string(pos, end - pos, sta->probe_ie_taxonomy); 255e5b75505Sopenharmony_ci pos = os_strchr(pos, '\0'); 256e5b75505Sopenharmony_ci if (pos >= end) 257e5b75505Sopenharmony_ci return 0; 258e5b75505Sopenharmony_ci ret = os_snprintf(pos, end - pos, "|assoc:"); 259e5b75505Sopenharmony_ci if (os_snprintf_error(end - pos, ret)) 260e5b75505Sopenharmony_ci return 0; 261e5b75505Sopenharmony_ci pos += ret; 262e5b75505Sopenharmony_ci ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy); 263e5b75505Sopenharmony_ci pos = os_strchr(pos, '\0'); 264e5b75505Sopenharmony_ci return pos - buf; 265e5b75505Sopenharmony_ci} 266e5b75505Sopenharmony_ci 267e5b75505Sopenharmony_ci 268e5b75505Sopenharmony_civoid taxonomy_sta_info_probe_req(const struct hostapd_data *hapd, 269e5b75505Sopenharmony_ci struct sta_info *sta, 270e5b75505Sopenharmony_ci const u8 *ie, size_t ie_len) 271e5b75505Sopenharmony_ci{ 272e5b75505Sopenharmony_ci wpabuf_free(sta->probe_ie_taxonomy); 273e5b75505Sopenharmony_ci sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); 274e5b75505Sopenharmony_ci} 275e5b75505Sopenharmony_ci 276e5b75505Sopenharmony_ci 277e5b75505Sopenharmony_civoid taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd, 278e5b75505Sopenharmony_ci struct hostapd_sta_info *info, 279e5b75505Sopenharmony_ci const u8 *ie, size_t ie_len) 280e5b75505Sopenharmony_ci{ 281e5b75505Sopenharmony_ci wpabuf_free(info->probe_ie_taxonomy); 282e5b75505Sopenharmony_ci info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); 283e5b75505Sopenharmony_ci} 284e5b75505Sopenharmony_ci 285e5b75505Sopenharmony_ci 286e5b75505Sopenharmony_civoid taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd, 287e5b75505Sopenharmony_ci struct sta_info *sta, 288e5b75505Sopenharmony_ci const u8 *ie, size_t ie_len) 289e5b75505Sopenharmony_ci{ 290e5b75505Sopenharmony_ci wpabuf_free(sta->assoc_ie_taxonomy); 291e5b75505Sopenharmony_ci sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); 292e5b75505Sopenharmony_ci} 293