2 * hostapd / Client taxonomy
3 * Copyright (c) 2015 Google, Inc.
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
8 * Parse a series of IEs, as in Probe Request or (Re)Association Request frames,
9 * and render them to a descriptive string. The tag number of standard options
10 * is written to the string, while the vendor ID and subtag are written for
14 * 0,1,50,45,221(00904c,51)
15 * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2)
18 #include "utils/includes.h"
20 #include "utils/common.h"
21 #include "common/wpa_ctrl.h"
27 /* Copy a string with no funny schtuff allowed; only alphanumerics. */
28 static void no_mischief_strncpy(char *dst, const char *src, size_t n)
32 for (i = 0; i < n; i++) {
33 unsigned char s = src[i];
34 int is_lower = s >= 'a' && s <= 'z';
35 int is_upper = s >= 'A' && s <= 'Z';
36 int is_digit = s >= '0' && s <= '9';
38 if (is_lower || is_upper || is_digit) {
39 /* TODO: if any manufacturer uses Unicode within the
40 * WPS header, it will get mangled here. */
43 /* Note that even spaces will be transformed to
44 * underscores, so 'Nexus 7' will turn into 'Nexus_7'.
45 * This is deliberate, to make the string easier to
53 static int get_wps_name(char *name, size_t name_len,
54 const u8 *data, size_t data_len)
56 /* Inside the WPS IE are a series of attributes, using two byte IDs
57 * and two byte lengths. We're looking for the model name, if
59 while (data_len >= 4) {
62 id = WPA_GET_BE16(data);
63 elen = WPA_GET_BE16(data + 2);
71 /* Model name, like 'Nexus 7' */
72 size_t n = (elen < name_len) ? elen : name_len;
73 no_mischief_strncpy(name, (const char *) data, n);
85 static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies)
88 char *fend = fstr + fstr_len;
89 char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */
90 char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */
91 char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */
92 char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */
93 char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */
94 char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */
95 #define MAX_EXTCAP 254
96 char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL
98 char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */
99 #define WPS_NAME_LEN 32
100 char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing
107 os_memset(htcap, 0, sizeof(htcap));
108 os_memset(htagg, 0, sizeof(htagg));
109 os_memset(htmcs, 0, sizeof(htmcs));
110 os_memset(vhtcap, 0, sizeof(vhtcap));
111 os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs));
112 os_memset(vhttxmcs, 0, sizeof(vhttxmcs));
113 os_memset(extcap, 0, sizeof(extcap));
114 os_memset(txpow, 0, sizeof(txpow));
115 os_memset(wps, 0, sizeof(wps));
120 ie = wpabuf_head(ies);
121 ie_len = wpabuf_len(ies);
123 while (ie_len >= 2) {
125 char *sep = (num++ == 0) ? "" : ",";
134 if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) {
135 /* Vendor specific */
136 if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) {
138 char model_name[WPS_NAME_LEN + 1];
139 const u8 *data = &ie[4];
140 size_t data_len = elen - 4;
142 os_memset(model_name, 0, sizeof(model_name));
143 if (get_wps_name(model_name, WPS_NAME_LEN, data,
145 os_snprintf(wps, sizeof(wps),
146 ",wps:%s", model_name);
150 ret = os_snprintf(fpos, fend - fpos,
151 "%s%d(%02x%02x%02x,%d)",
152 sep, id, ie[0], ie[1], ie[2], ie[3]);
154 if (id == WLAN_EID_HT_CAP && elen >= 2) {
155 /* HT Capabilities (802.11n) */
156 os_snprintf(htcap, sizeof(htcap),
160 if (id == WLAN_EID_HT_CAP && elen >= 3) {
161 /* HT Capabilities (802.11n), A-MPDU information
163 os_snprintf(htagg, sizeof(htagg),
164 ",htagg:%02hx", (u16) ie[2]);
166 if (id == WLAN_EID_HT_CAP && elen >= 7) {
167 /* HT Capabilities (802.11n), MCS information */
168 os_snprintf(htmcs, sizeof(htmcs),
170 (u16) WPA_GET_LE32(ie + 3));
172 if (id == WLAN_EID_VHT_CAP && elen >= 4) {
173 /* VHT Capabilities (802.11ac) */
174 os_snprintf(vhtcap, sizeof(vhtcap),
178 if (id == WLAN_EID_VHT_CAP && elen >= 8) {
179 /* VHT Capabilities (802.11ac), RX MCS
181 os_snprintf(vhtrxmcs, sizeof(vhtrxmcs),
183 WPA_GET_LE32(ie + 4));
185 if (id == WLAN_EID_VHT_CAP && elen >= 12) {
186 /* VHT Capabilities (802.11ac), TX MCS
188 os_snprintf(vhttxmcs, sizeof(vhttxmcs),
190 WPA_GET_LE32(ie + 8));
192 if (id == WLAN_EID_EXT_CAPAB) {
193 /* Extended Capabilities */
195 int len = (elen < MAX_EXTCAP) ? elen :
199 p += os_snprintf(extcap, sizeof(extcap),
201 for (i = 0; i < len; i++) {
204 lim = sizeof(extcap) -
208 p += os_snprintf(p, lim, "%02x",
212 if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) {
214 os_snprintf(txpow, sizeof(txpow),
219 ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id);
221 if (os_snprintf_error(fend - fpos, ret))
229 ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s",
230 htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs,
232 if (os_snprintf_error(fend - fpos, ret)) {
239 int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
240 struct sta_info *sta, char *buf, size_t buflen)
245 if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy)
248 ret = os_snprintf(buf, buflen, "wifi4|probe:");
249 if (os_snprintf_error(buflen, ret))
254 ie_to_string(pos, end - pos, sta->probe_ie_taxonomy);
255 pos = os_strchr(pos, '\0');
258 ret = os_snprintf(pos, end - pos, "|assoc:");
259 if (os_snprintf_error(end - pos, ret))
262 ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy);
263 pos = os_strchr(pos, '\0');
268 void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
269 struct sta_info *sta,
270 const u8 *ie, size_t ie_len)
272 wpabuf_free(sta->probe_ie_taxonomy);
273 sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
277 void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
278 struct hostapd_sta_info *info,
279 const u8 *ie, size_t ie_len)
281 wpabuf_free(info->probe_ie_taxonomy);
282 info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
286 void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
287 struct sta_info *sta,
288 const u8 *ie, size_t ie_len)
290 wpabuf_free(sta->assoc_ie_taxonomy);
291 sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);