taxonomy: export more information.

With the experience of a few months gathering signatures,
add more fields to the signature:
1. 802.11n A-MPDU information (can be distinguishing)
2. 802.11n MCS and spatial stream information
3. 802.11ac MCS and spatial stream information, TX and RX
4. Interworking (Hotspot 2.0)
5. Extended Capabilities

This version2 signature can be transformed into a v1 signature
by removing the new fields, so we don't immediately have to
discard all existing signatures.

Change-Id: I7c7403b51966a49042f124d7425d67848a0ddc76
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index ce8d9fb..bd88c91 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -169,7 +169,7 @@
 	u8 last_subtype;
 
 #ifdef CONFIG_CLIENT_TAXONOMY
-#define TAXONOMY_STRING_LEN 384
+#define TAXONOMY_STRING_LEN 768
 	char probe_ie_taxonomy[TAXONOMY_STRING_LEN];
 	char assoc_ie_taxonomy[TAXONOMY_STRING_LEN];
 #endif /* CONFIG_CLIENT_TAXONOMY */
diff --git a/src/ap/taxonomy.c b/src/ap/taxonomy.c
index 9a8812d..6ff25ac 100644
--- a/src/ap/taxonomy.c
+++ b/src/ap/taxonomy.c
@@ -1,5 +1,5 @@
 /*
- * hostapd / Station client taxonomy
+ * hostapd / Client taxonomy
  * Copyright (c) 2015 Google, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
@@ -79,13 +79,25 @@
 {
 	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
+	char intwrk[8 + 2 + 1];  // ",intwrk:" + %02hx + trailing NUL
+	char extcap[8 + 8 + 1];  // ",extcap:" + %08x + 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(intwrk, 0, sizeof(intwrk));
+	memset(extcap, 0, sizeof(extcap));
 	memset(wps, 0, sizeof(wps));
 	fstr[0] = '\0';
 
@@ -119,20 +131,59 @@
 			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)) {
+			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 == 191) && (elen > 4)) {
+			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", 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",
+						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 == 107) && (elen >= 1)) {
+				/* Interworking */
+				snprintf(intwrk, sizeof(intwrk), ",intwrk:%02hx", ie);
+			}
+			if ((id == 127) && (elen >= 4)) {
+				/* Extended Capabilities */
+				u32 ext;
+				memcpy(&ext, ie, sizeof(ext));
+				snprintf(extcap, sizeof(extcap), ",extcap:%08x",
+				         le_to_host32(ext));
+			}
+
 			snprintf(tagbuf, sizeof(tagbuf), "%s%d", sep, id);
 		}
 
@@ -147,10 +198,34 @@
 		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(intwrk)) {
+		strncat(fstr, intwrk, 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;