taxonomy.c 7.4 KB

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