[PATCH v2 1/2] Passive Client Taxonomy

Denton Gentry dgentry at google.com
Sun Aug 14 21:42:48 PDT 2016


Implement the signature mechanism described in the paper
"Passive Taxonomy of Wifi Clients using MLME Frame Contents"
published by Denton Gentry and Avery Pennarun.

http://research.google.com/pubs/pub45429.html
https://arxiv.org/abs/1608.01725

This involves:
1. Add a CONFIG_TAXONOMY compile option. Enabling taxonomy incurs
   a memory overhead of up to several kilobytes per associated
   station.
2. If enabled, store the Probe Request and Associate Request in
   the sta_info_t.
3. Implement code to extract the ID of each Information Element,
   plus selected fields and bitmasks from certain IEs, into a
   descriptive text string. This is done in a new source file,
   src/ap/taxonomy.c.
4. Implement a "signature qq:rr:ss:tt:uu:vv" command
   in hostapd_cli to retrieve the signature.

Signatures take the form of a text string. For example, a signature
for the Nexus 5X is:
  wifi4|probe:0,1,127,45,191,htcap:01ef,htagg:03,htmcs:0000ffff,vhtcap:338061b2,
  vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:00000a0201000040|assoc:0,1,48,45,
  221(0050f2,2),191,127,htcap:01ef,htagg:03,htmcs:0000ffff,vhtcap:339071b2,
  vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:0000000000000040

Change-Id: I311ea24a5cc2d84a15a543f84ddea32538e83abd
Signed-off-by: dgentry at google.com (Denton Gentry)
Signed-off-by: denny at geekhold.com (Denton Gentry)
Signed-off-by: rofrankel at google.com (Richard Frankel)
Signed-off-by: richard at frankel.tv (Richard Frankel)
---
 hostapd/Makefile       |   5 +
 hostapd/ctrl_iface.c   |   5 +
 hostapd/defconfig      |   6 +
 hostapd/hostapd_cli.c  |  20 ++++
 src/ap/beacon.c        |  12 ++
 src/ap/ctrl_iface_ap.c |  30 +++++
 src/ap/ctrl_iface_ap.h |   5 +
 src/ap/ieee802_11.c    |   7 ++
 src/ap/sta_info.c      |  13 +++
 src/ap/sta_info.h      |   7 ++
 src/ap/taxonomy.c      | 298 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/ap/taxonomy.h      |  19 ++++
 12 files changed, 427 insertions(+)
 create mode 100644 src/ap/taxonomy.c
 create mode 100644 src/ap/taxonomy.h

diff --git a/hostapd/Makefile b/hostapd/Makefile
index ba094ba..46dffe5 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -100,6 +100,11 @@ NEED_SHA1=y
 OBJS += ../src/drivers/drivers.o
 CFLAGS += -DHOSTAPD
 
+ifdef CONFIG_TAXONOMY
+CFLAGS += -DCONFIG_TAXONOMY
+OBJS += ../src/ap/taxonomy.o
+endif
+
 ifdef CONFIG_MODULE_TESTS
 CFLAGS += -DCONFIG_MODULE_TESTS
 OBJS += hapd_module_tests.o
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 4e7b58e..b66fb50 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -2364,6 +2364,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
 	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
 		if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
 			reply_len = -1;
+#ifdef CONFIG_TAXONOMY
+	} else if (os_strncmp(buf, "SIGNATURE ", 10) == 0) {
+		reply_len = hostapd_ctrl_iface_signature(hapd, buf + 10,
+							 reply, reply_size);
+#endif /* CONFIG_TAXONOMY */
 	} else if (os_strncmp(buf, "POLL_STA ", 9) == 0) {
 		if (hostapd_ctrl_iface_poll_sta(hapd, buf + 9))
 			reply_len = -1;
diff --git a/hostapd/defconfig b/hostapd/defconfig
index f7b60e0..1759402 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -337,3 +337,9 @@ CONFIG_IPV6=y
 # These extentions facilitate efficient use of multiple frequency bands
 # available to the AP and the devices that may associate with it.
 #CONFIG_MBO=y
+#
+# Client Taxonomy
+# Has the AP retain the Probe Request and Association Request MLME frames from
+# a client, from which a signature can be produced which can identify the model
+# of client device like "Nexus 6P" or "iPhone 5s"
+CONFIG_TAXONOMY=y
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 04819d1..dbfcf22 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -366,6 +366,22 @@ static char ** hostapd_complete_disassociate(const char *str, int pos)
 }
 
 
+#ifdef CONFIG_TAXONOMY
+static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	char buf[64];
+	if (argc != 1) {
+		printf("Invalid 'signature' command - exactly one "
+		       "argument, STA address, is required.\n");
+		return -1;
+	}
+	os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
+	return wpa_ctrl_command(ctrl, buf);
+}
+#endif /* CONFIG_TAXONOMY */
+
+
 #ifdef CONFIG_IEEE80211W
 static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
 				    char *argv[])
@@ -1271,6 +1287,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
 	{ "disassociate", hostapd_cli_cmd_disassociate,
 	  hostapd_complete_disassociate,
 	  "<addr> = disassociate a station" },
+#ifdef CONFIG_TAXONOMY
+	{ "signature", hostapd_cli_cmd_signature, NULL,
+	  "<addr> = get taxonomy signature for a station" },
+#endif /* CONFIG_TAXONOMY */
 #ifdef CONFIG_IEEE80211W
 	{ "sa_query", hostapd_cli_cmd_sa_query, NULL,
 	  "<addr> = send SA Query to a station" },
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 0570ab7..d6c96a3 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -29,6 +29,9 @@
 #include "beacon.h"
 #include "hs20.h"
 #include "dfs.h"
+#ifdef CONFIG_TAXONOMY
+#include "taxonomy.h"
+#endif
 
 
 #ifdef NEED_AP_MLME
@@ -782,6 +785,15 @@ void handle_probe_req(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_TAXONOMY
+	{
+		struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);
+		if (sta) {
+			hostapd_taxonomy_probe_req(hapd, sta, ie, ie_len);
+		}
+	}
+#endif /* CONFIG_TAXONOMY */
+
 	res = ssid_match(hapd, elems.ssid, elems.ssid_len,
 			 elems.ssid_list, elems.ssid_list_len);
 	if (res == NO_SSID_MATCH) {
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 17a3ea4..fc9d1d1 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -23,6 +23,9 @@
 #include "ctrl_iface_ap.h"
 #include "ap_drv_ops.h"
 #include "mbo_ap.h"
+#ifdef CONFIG_TAXONOMY
+#include "taxonomy.h"
+#endif /* CONFIG_TAXONOMY */
 
 
 static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
@@ -429,6 +432,33 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
 }
 
 
+#ifdef CONFIG_TAXONOMY
+int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
+				 const char *txtaddr,
+				 char *buf, size_t buflen)
+{
+	u8 addr[ETH_ALEN];
+	int ret;
+	struct sta_info *sta;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr)) {
+		ret = os_snprintf(buf, buflen, "FAIL\n");
+		if (os_snprintf_error(buflen, ret))
+			return 0;
+		return ret;
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL)
+		return -1;
+
+	return retrieve_sta_taxonomy(hapd, sta, buf, buflen);
+}
+#endif /* CONFIG_TAXONOMY */
+
+
 int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
 				const char *txtaddr)
 {
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index 6095d7d..8124134 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -19,6 +19,11 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
 				      const char *txtaddr);
 int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
 				    const char *txtaddr);
+#ifdef CONFIG_TAXONOMY
+int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
+				 const char *txtaddr,
+				 char *buf, size_t buflen);
+#endif /* CONFIG_TAXONOMY */
 int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
 				const char *txtaddr);
 int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 555a731..355b3c0 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -44,6 +44,9 @@
 #include "dfs.h"
 #include "mbo_ap.h"
 #include "rrm.h"
+#ifdef CONFIG_TAXONOMY
+#include "taxonomy.h"
+#endif /* CONFIG_TAXONOMY */
 
 
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
@@ -2250,6 +2253,10 @@ static void handle_assoc(struct hostapd_data *hapd,
 	 * remove the STA immediately. */
 	sta->timeout_next = STA_NULLFUNC;
 
+#ifdef CONFIG_TAXONOMY
+	hostapd_taxonomy_assoc_req(hapd, sta, pos, left);
+#endif /* CONFIG_TAXONOMY */
+
  fail:
 	/*
 	 * In case of a successful response, add the station to the driver.
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index c36842b..c9f4858 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -222,6 +222,19 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 		hapd->iface->num_sta_ht_20mhz--;
 	}
 
+#ifdef CONFIG_TAXONOMY
+	if (sta->probe_ie_taxonomy) {
+		os_free((void *)sta->probe_ie_taxonomy);
+		sta->probe_ie_taxonomy = NULL;
+		sta->probe_ie_taxonomy_len = 0;
+	}
+	if (sta->assoc_ie_taxonomy) {
+		os_free((void *)sta->assoc_ie_taxonomy);
+		sta->assoc_ie_taxonomy = NULL;
+		sta->assoc_ie_taxonomy_len = 0;
+	}
+#endif /* CONFIG_TAXONOMY */
+
 #ifdef CONFIG_IEEE80211N
 	ht40_intolerant_remove(hapd->iface, sta);
 #endif /* CONFIG_IEEE80211N */
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index cf3fbb1..622c8fe 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -214,6 +214,13 @@ struct sta_info {
 			      * received, starting from the Length field */
 
 	u8 rrm_enabled_capa[5];
+
+#ifdef CONFIG_TAXONOMY
+	const u8 *probe_ie_taxonomy;
+	size_t probe_ie_taxonomy_len;
+	const u8 *assoc_ie_taxonomy;
+	size_t assoc_ie_taxonomy_len;
+#endif /* CONFIG_TAXONOMY */
 };
 
 
diff --git a/src/ap/taxonomy.c b/src/ap/taxonomy.c
new file mode 100644
index 0000000..d0deb05
--- /dev/null
+++ b/src/ap/taxonomy.c
@@ -0,0 +1,298 @@
+/*
+ * hostapd / Client taxonomy
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+/*
+ * Parse a series of IEs, as in Probe or Association packets,
+ * and render them to a descriptive string. The tag number of
+ * standard options is written to the string, while the vendor
+ * ID and subtag are written for vendor options.
+ *
+ * Example strings:
+ * 0,1,50,45,221(00904c,51)
+ * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2)
+ */
+
+#include "includes.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include "common/wpa_ctrl.h"
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "hostapd.h"
+#include "sta_info.h"
+
+
+/* Copy a string with no funny schtuff allowed; only alphanumerics. */
+static void no_mischief_strncpy(char *dst, const char *src, size_t n)
+{
+	size_t i;
+	for (i = 0; i < n; i++) {
+		unsigned char s = src[i];
+		int is_lower = (s >= 'a' && s <= 'z');
+		int is_upper = (s >= 'A' && s <= 'Z');
+		int is_digit = (s >= '0' && s <= '9');
+		if (is_lower || is_upper || is_digit) {
+			/* TODO: if any manufacturer uses Unicode within the
+			 * WPS header, it will get mangled here. */
+			dst[i] = s;
+		} else {
+			/* note that even spaces will be transformed to underscores,
+			 * so 'Nexus 7' will turn into 'Nexus_7'. This is deliberate,
+			 * to make the string easier to parse. */
+			dst[i] = '_';
+		}
+	}
+}
+
+static int get_wps_name(char *name, size_t name_len,
+		const u8 *data, size_t data_len)
+{
+	/* Inside the WPS IE are a series of sub-IEs, using two byte IDs
+	 * and two byte lengths. We're looking for the model name, if
+	 * present. */
+	while (data_len >= 4) {
+		u16 id, elen;
+		id = (data[0] << 8) | data[1];
+		elen = (data[2] << 8) | data[3];
+		data += 4;
+		data_len -= 4;
+
+		if (elen > data_len) {
+			return 0;
+		}
+
+		if (id == 0x1023) {
+			/* Model name, like 'Nexus 7' */
+			size_t n = (elen < name_len) ? elen : name_len;
+			no_mischief_strncpy(name, (const char *)data, n);
+			return n;
+		}
+
+		data += elen;
+		data_len -= elen;
+	}
+
+	return 0;
+}
+
+static void ie_to_string(char *fstr, size_t fstr_len,
+                         const u8 *ie, size_t ie_len)
+{
+	size_t flen = fstr_len - 1;
+	char htcap[7 + 4 + 1];  // ",htcap:" + %04hx + trailing NUL
+	char htagg[7 + 2 + 1];  // ",htagg:" + %02hx + trailing NUL
+	char htmcs[7 + 8 + 1];  // ",htmcs:" + %08x + trailing NUL
+	char vhtcap[8 + 8 + 1];  // ",vhtcap:" + %08x + trailing NUL
+	char vhtrxmcs[10 + 8 + 1];  // ",vhtrxmcs:" + %08x + trailing NUL
+	char vhttxmcs[10 + 8 + 1];  // ",vhttxmcs:" + %08x + trailing NUL
+	#define MAX_EXTCAP	254
+	char extcap[8 + (2 * MAX_EXTCAP) + 1];  // ",extcap:" + hex + trailing NUL
+	char txpow[7 + 4 + 1];  // ",txpow:" + %04hx + trailing NUL
+	#define WPS_NAME_LEN		32
+	char wps[WPS_NAME_LEN + 5 + 1];  // room to prepend ",wps:" + trailing NUL
+	int num = 0;
+
+	memset(htcap, 0, sizeof(htcap));
+	memset(htagg, 0, sizeof(htagg));
+	memset(htmcs, 0, sizeof(htmcs));
+	memset(vhtcap, 0, sizeof(vhtcap));
+	memset(vhtrxmcs, 0, sizeof(vhtrxmcs));
+	memset(vhttxmcs, 0, sizeof(vhttxmcs));
+	memset(extcap, 0, sizeof(extcap));
+	memset(txpow, 0, sizeof(txpow));
+	memset(wps, 0, sizeof(wps));
+	fstr[0] = '\0';
+
+	while (ie_len >= 2) {
+		u8 id, elen;
+		char tagbuf[32];
+		char *sep = (num++ == 0) ? "" : ",";
+
+		id = *ie++;
+		elen = *ie++;
+		ie_len -= 2;
+
+		if (elen > ie_len) {
+			break;
+		}
+
+		if ((id == 221) && (elen >= 4)) {
+			/* Vendor specific */
+			int is_MSFT = (ie[0] == 0x00 && ie[1] == 0x50 && ie[2] == 0xf2);
+			if (is_MSFT && ie[3] == 0x04) {
+				/* WPS */
+				char model_name[WPS_NAME_LEN + 1];
+				const u8 *data = &ie[4];
+				size_t data_len = elen - 4;
+				memset(model_name, 0, sizeof(model_name));
+				if (get_wps_name(model_name, WPS_NAME_LEN, data, data_len)) {
+					snprintf(wps, sizeof(wps), ",wps:%s", model_name);
+				}
+			}
+
+			snprintf(tagbuf, sizeof(tagbuf), "%s%d(%02x%02x%02x,%d)",
+			         sep, id, ie[0], ie[1], ie[2], ie[3]);
+		} else {
+			if ((id == 45) && (elen >= 2)) {
+				/* HT Capabilities (802.11n) */
+				u16 cap;
+				memcpy(&cap, ie, sizeof(cap));
+				snprintf(htcap, sizeof(htcap), ",htcap:%04hx",
+				         le_to_host16(cap));
+			}
+			if ((id == 45) && (elen >= 3)) {
+				/* HT Capabilities (802.11n), A-MPDU information */
+				u8 agg;
+				memcpy(&agg, ie + 2, sizeof(agg));
+				snprintf(htagg, sizeof(htagg), ",htagg:%02hx", (u16)agg);
+			}
+			if ((id == 45) && (elen >= 7)) {
+				/* HT Capabilities (802.11n), MCS information */
+				u32 mcs;
+				memcpy(&mcs, ie + 3, sizeof(mcs));
+				snprintf(htmcs, sizeof(htmcs), ",htmcs:%08hx",
+						(u16)le_to_host32(mcs));
+			}
+			if ((id == 191) && (elen >= 4)) {
+				/* VHT Capabilities (802.11ac) */
+				u32 cap;
+				memcpy(&cap, ie, sizeof(cap));
+				snprintf(vhtcap, sizeof(vhtcap), ",vhtcap:%08x",
+				         le_to_host32(cap));
+			}
+			if ((id == 191) && (elen >= 8)) {
+				/* VHT Capabilities (802.11ac), RX MCS information */
+				u32 mcs;
+				memcpy(&mcs, ie + 4, sizeof(mcs));
+				snprintf(vhtrxmcs, sizeof(vhtrxmcs), ",vhtrxmcs:%08x",
+				         le_to_host32(mcs));
+			}
+			if ((id == 191) && (elen >= 12)) {
+				/* VHT Capabilities (802.11ac), TX MCS information */
+				u32 mcs;
+				memcpy(&mcs, ie + 8, sizeof(mcs));
+				snprintf(vhttxmcs, sizeof(vhttxmcs), ",vhttxmcs:%08x",
+				         le_to_host32(mcs));
+			}
+			if (id == 127) {
+				/* Extended Capabilities */
+				int i;
+				int len = (elen < MAX_EXTCAP) ? elen : MAX_EXTCAP;
+				char *p = extcap;
+
+				p += snprintf(extcap, sizeof(extcap), ",extcap:");
+				for (i = 0; i < len; ++i) {
+					int lim = sizeof(extcap) - strlen(extcap);
+					p += snprintf(p, lim, "%02x", *(ie + i));
+				}
+			}
+			if ((id == 33) && (elen == 2)) {
+				/* TX Power */
+				u16 p;
+				memcpy(&p, ie, sizeof(p));
+				snprintf(txpow, sizeof(txpow), ",txpow:%04hx",
+				         le_to_host16(p));
+			}
+
+			snprintf(tagbuf, sizeof(tagbuf), "%s%d", sep, id);
+		}
+
+		strncat(fstr, tagbuf, flen);
+		flen = fstr_len - strlen(fstr) - 1;
+
+		ie += elen;
+		ie_len -= elen;
+	}
+
+	if (strlen(htcap)) {
+		strncat(fstr, htcap, flen);
+		flen = fstr_len - strlen(fstr) - 1;
+	}
+	if (strlen(htagg)) {
+		strncat(fstr, htagg, flen);
+		flen = fstr_len - strlen(fstr) - 1;
+	}
+	if (strlen(htmcs)) {
+		strncat(fstr, htmcs, flen);
+		flen = fstr_len - strlen(fstr) - 1;
+	}
+	if (strlen(vhtcap)) {
+		strncat(fstr, vhtcap, flen);
+		flen = fstr_len - strlen(fstr) - 1;
+	}
+	if (strlen(vhtrxmcs)) {
+		strncat(fstr, vhtrxmcs, flen);
+		flen = fstr_len - strlen(fstr) - 1;
+	}
+	if (strlen(vhttxmcs)) {
+		strncat(fstr, vhttxmcs, flen);
+		flen = fstr_len - strlen(fstr) - 1;
+	}
+	if (strlen(txpow)) {
+		strncat(fstr, txpow, flen);
+		flen = fstr_len - strlen(fstr) - 1;
+	}
+	if (strlen(extcap)) {
+		strncat(fstr, extcap, flen);
+		flen = fstr_len - strlen(fstr) - 1;
+	}
+	if (strlen(wps)) {
+		strncat(fstr, wps, flen);
+		flen = fstr_len - strlen(fstr) - 1;
+	}
+
+	fstr[fstr_len - 1] = '\0';
+}
+
+int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
+	struct sta_info *sta, char *buf, size_t buflen)
+{
+	if (sta->probe_ie_taxonomy && sta->assoc_ie_taxonomy) {
+		int ret;
+		char probe_signature[768];
+		char assoc_signature[768];
+		ie_to_string(probe_signature, sizeof(probe_signature),
+			sta->probe_ie_taxonomy, sta->probe_ie_taxonomy_len);
+		ie_to_string(assoc_signature, sizeof(assoc_signature),
+			sta->assoc_ie_taxonomy, sta->assoc_ie_taxonomy_len);
+		ret = os_snprintf(buf, buflen, "wifi4|probe:%s|assoc:%s",
+				  probe_signature, assoc_signature);
+		if (os_snprintf_error(buflen, ret))
+			return 0;
+		return ret;
+	}
+	return 0;
+}
+
+void hostapd_taxonomy_probe_req(const struct hostapd_data *hapd,
+	struct sta_info *sta, const u8 *ie, size_t ie_len)
+{
+	if (sta->probe_ie_taxonomy) {
+		os_free(sta->probe_ie_taxonomy);
+		sta->probe_ie_taxonomy = NULL;
+		sta->probe_ie_taxonomy_len = 0;
+	}
+	sta->probe_ie_taxonomy = os_malloc(ie_len);
+	os_memcpy(sta->probe_ie_taxonomy, ie, ie_len);
+	sta->probe_ie_taxonomy_len = ie_len;
+}
+
+void hostapd_taxonomy_assoc_req(const struct hostapd_data *hapd,
+	struct sta_info *sta, const u8 *ie, size_t ie_len)
+{
+	if (sta->assoc_ie_taxonomy) {
+		os_free(sta->assoc_ie_taxonomy);
+		sta->assoc_ie_taxonomy = NULL;
+		sta->assoc_ie_taxonomy_len = 0;
+	}
+	sta->assoc_ie_taxonomy = os_malloc(ie_len);
+	os_memcpy(sta->assoc_ie_taxonomy, ie, ie_len);
+	sta->assoc_ie_taxonomy_len = ie_len;
+}
diff --git a/src/ap/taxonomy.h b/src/ap/taxonomy.h
new file mode 100644
index 0000000..6d2ec39
--- /dev/null
+++ b/src/ap/taxonomy.h
@@ -0,0 +1,19 @@
+/*
+ * hostapd / Station client taxonomy
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TAXONOMY_H
+#define TAXONOMY_H
+
+void hostapd_taxonomy_probe_req(const struct hostapd_data *hapd,
+	struct sta_info *sta, const u8 *ie, size_t ie_len);
+void hostapd_taxonomy_assoc_req(const struct hostapd_data *hapd,
+	struct sta_info *sta, const u8 *ie, size_t ie_len);
+int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
+	struct sta_info *sta, char *buf, size_t buflen);
+
+#endif /* TAXONOMY_H */
-- 
2.8.0.rc3.226.g39d4020




More information about the Hostap mailing list