xref: /dragonfly/contrib/wpa_supplicant/src/ap/taxonomy.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
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 #include "taxonomy.h"
25 
26 
27 /* Copy a string with no funny schtuff allowed; only alphanumerics. */
no_mischief_strncpy(char * dst,const char * src,size_t n)28 static void no_mischief_strncpy(char *dst, const char *src, size_t n)
29 {
30           size_t i;
31 
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';
37 
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. */
41                               dst[i] = s;
42                     } else {
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
46                                * parse. */
47                               dst[i] = '_';
48                     }
49           }
50 }
51 
52 
get_wps_name(char * name,size_t name_len,const u8 * data,size_t data_len)53 static int get_wps_name(char *name, size_t name_len,
54                               const u8 *data, size_t data_len)
55 {
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
58            * present. */
59           while (data_len >= 4) {
60                     u16 id, elen;
61 
62                     id = WPA_GET_BE16(data);
63                     elen = WPA_GET_BE16(data + 2);
64                     data += 4;
65                     data_len -= 4;
66 
67                     if (elen > data_len)
68                               return 0;
69 
70                     if (id == 0x1023) {
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);
74                               return n;
75                     }
76 
77                     data += elen;
78                     data_len -= elen;
79           }
80 
81           return 0;
82 }
83 
84 
ie_to_string(char * fstr,size_t fstr_len,const struct wpabuf * ies)85 static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies)
86 {
87           char *fpos = fstr;
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
97                                                         */
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
101                                                    * NUL */
102           int num = 0;
103           const u8 *ie;
104           size_t ie_len;
105           int ret;
106 
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));
116           *fpos = '\0';
117 
118           if (!ies)
119                     return;
120           ie = wpabuf_head(ies);
121           ie_len = wpabuf_len(ies);
122 
123           while (ie_len >= 2) {
124                     u8 id, elen;
125                     char *sep = (num++ == 0) ? "" : ",";
126 
127                     id = *ie++;
128                     elen = *ie++;
129                     ie_len -= 2;
130 
131                     if (elen > ie_len)
132                               break;
133 
134                     if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) {
135                               /* Vendor specific */
136                               if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) {
137                                         /* WPS */
138                                         char model_name[WPS_NAME_LEN + 1];
139                                         const u8 *data = &ie[4];
140                                         size_t data_len = elen - 4;
141 
142                                         os_memset(model_name, 0, sizeof(model_name));
143                                         if (get_wps_name(model_name, WPS_NAME_LEN, data,
144                                                              data_len)) {
145                                                   os_snprintf(wps, sizeof(wps),
146                                                                 ",wps:%s", model_name);
147                                         }
148                               }
149 
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]);
153                     } else {
154                               if (id == WLAN_EID_HT_CAP && elen >= 2) {
155                                         /* HT Capabilities (802.11n) */
156                                         os_snprintf(htcap, sizeof(htcap),
157                                                       ",htcap:%04hx",
158                                                       WPA_GET_LE16(ie));
159                               }
160                               if (id == WLAN_EID_HT_CAP && elen >= 3) {
161                                         /* HT Capabilities (802.11n), A-MPDU information
162                                          */
163                                         os_snprintf(htagg, sizeof(htagg),
164                                                       ",htagg:%02hx", (u16) ie[2]);
165                               }
166                               if (id == WLAN_EID_HT_CAP && elen >= 7) {
167                                         /* HT Capabilities (802.11n), MCS information */
168                                         os_snprintf(htmcs, sizeof(htmcs),
169                                                       ",htmcs:%08hx",
170                                                       (u16) WPA_GET_LE32(ie + 3));
171                               }
172                               if (id == WLAN_EID_VHT_CAP && elen >= 4) {
173                                         /* VHT Capabilities (802.11ac) */
174                                         os_snprintf(vhtcap, sizeof(vhtcap),
175                                                       ",vhtcap:%08x",
176                                                       WPA_GET_LE32(ie));
177                               }
178                               if (id == WLAN_EID_VHT_CAP && elen >= 8) {
179                                         /* VHT Capabilities (802.11ac), RX MCS
180                                          * information */
181                                         os_snprintf(vhtrxmcs, sizeof(vhtrxmcs),
182                                                       ",vhtrxmcs:%08x",
183                                                       WPA_GET_LE32(ie + 4));
184                               }
185                               if (id == WLAN_EID_VHT_CAP && elen >= 12) {
186                                         /* VHT Capabilities (802.11ac), TX MCS
187                                          * information */
188                                         os_snprintf(vhttxmcs, sizeof(vhttxmcs),
189                                                       ",vhttxmcs:%08x",
190                                                       WPA_GET_LE32(ie + 8));
191                               }
192                               if (id == WLAN_EID_EXT_CAPAB) {
193                                         /* Extended Capabilities */
194                                         int i;
195                                         int len = (elen < MAX_EXTCAP) ? elen :
196                                                   MAX_EXTCAP;
197                                         char *p = extcap;
198 
199                                         p += os_snprintf(extcap, sizeof(extcap),
200                                                              ",extcap:");
201                                         for (i = 0; i < len; i++) {
202                                                   int lim;
203 
204                                                   lim = sizeof(extcap) -
205                                                             os_strlen(extcap);
206                                                   if (lim <= 0)
207                                                             break;
208                                                   p += os_snprintf(p, lim, "%02x",
209                                                                        *(ie + i));
210                                         }
211                               }
212                               if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) {
213                                         /* TX Power */
214                                         os_snprintf(txpow, sizeof(txpow),
215                                                       ",txpow:%04hx",
216                                                       WPA_GET_LE16(ie));
217                               }
218 
219                               ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id);
220                     }
221                     if (os_snprintf_error(fend - fpos, ret))
222                               goto fail;
223                     fpos += ret;
224 
225                     ie += elen;
226                     ie_len -= elen;
227           }
228 
229           ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s",
230                                 htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs,
231                                 txpow, extcap, wps);
232           if (os_snprintf_error(fend - fpos, ret)) {
233           fail:
234                     fstr[0] = '\0';
235           }
236 }
237 
238 
retrieve_sta_taxonomy(const struct hostapd_data * hapd,struct sta_info * sta,char * buf,size_t buflen)239 int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
240                                 struct sta_info *sta, char *buf, size_t buflen)
241 {
242           int ret;
243           char *pos, *end;
244 
245           if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy)
246                     return 0;
247 
248           ret = os_snprintf(buf, buflen, "wifi4|probe:");
249           if (os_snprintf_error(buflen, ret))
250                     return 0;
251           pos = buf + ret;
252           end = buf + buflen;
253 
254           ie_to_string(pos, end - pos, sta->probe_ie_taxonomy);
255           pos = os_strchr(pos, '\0');
256           if (pos >= end)
257                     return 0;
258           ret = os_snprintf(pos, end - pos, "|assoc:");
259           if (os_snprintf_error(end - pos, ret))
260                     return 0;
261           pos += ret;
262           ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy);
263           pos = os_strchr(pos, '\0');
264           return pos - buf;
265 }
266 
267 
taxonomy_sta_info_probe_req(const struct hostapd_data * hapd,struct sta_info * sta,const u8 * ie,size_t ie_len)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)
271 {
272           wpabuf_free(sta->probe_ie_taxonomy);
273           sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
274 }
275 
276 
taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data * hapd,struct hostapd_sta_info * info,const u8 * ie,size_t 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)
280 {
281           wpabuf_free(info->probe_ie_taxonomy);
282           info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
283 }
284 
285 
taxonomy_sta_info_assoc_req(const struct hostapd_data * hapd,struct sta_info * sta,const u8 * ie,size_t 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)
289 {
290           wpabuf_free(sta->assoc_ie_taxonomy);
291           sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
292 }
293