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