Parcourir la source

wlantest: Add counters and AP/direct path validation for TDLS

These can be used to write automated test scripts for verifying
that TDLS STAs are using correct data path.
Jouni Malinen il y a 14 ans
Parent
commit
0e42fff3de
5 fichiers modifiés avec 393 ajouts et 2 suppressions
  1. 119 2
      wlantest/ctrl.c
  2. 63 0
      wlantest/rx_data.c
  3. 1 0
      wlantest/wlantest.h
  4. 198 0
      wlantest/wlantest_cli.c
  5. 12 0
      wlantest/wlantest_ctrl.h

+ 119 - 2
wlantest/ctrl.c

@@ -192,6 +192,33 @@ static struct wlantest_sta * ctrl_get_sta(struct wlantest *wt, int sock,
 }
 
 
+static struct wlantest_sta * ctrl_get_sta2(struct wlantest *wt, int sock,
+					   u8 *cmd, size_t clen,
+					   struct wlantest_bss *bss)
+{
+	struct wlantest_sta *sta;
+	u8 *pos;
+	size_t len;
+
+	if (bss == NULL)
+		return NULL;
+
+	pos = attr_get(cmd, clen, WLANTEST_ATTR_STA2_ADDR, &len);
+	if (pos == NULL || len != ETH_ALEN) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return NULL;
+	}
+
+	sta = sta_find(bss, pos);
+	if (sta == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return NULL;
+	}
+
+	return sta;
+}
+
+
 static void ctrl_list_bss(struct wlantest *wt, int sock)
 {
 	u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos, *len;
@@ -263,8 +290,10 @@ static void ctrl_clear_sta_counters(struct wlantest *wt, int sock, u8 *cmd,
 
 	bss = ctrl_get_bss(wt, sock, cmd, clen);
 	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
-	if (sta == NULL)
+	if (sta == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
 		return;
+	}
 
 	os_memset(sta->counters, 0, sizeof(sta->counters));
 	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
@@ -277,14 +306,41 @@ static void ctrl_clear_bss_counters(struct wlantest *wt, int sock, u8 *cmd,
 	struct wlantest_bss *bss;
 
 	bss = ctrl_get_bss(wt, sock, cmd, clen);
-	if (bss == NULL)
+	if (bss == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
 		return;
+	}
 
 	os_memset(bss->counters, 0, sizeof(bss->counters));
 	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
 }
 
 
+static void ctrl_clear_tdls_counters(struct wlantest *wt, int sock, u8 *cmd,
+				     size_t clen)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	struct wlantest_sta *sta2;
+	struct wlantest_tdls *tdls;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	sta2 = ctrl_get_sta2(wt, sock, cmd, clen, bss);
+	if (sta == NULL || sta2 == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if ((tdls->init == sta && tdls->resp == sta2) ||
+		    (tdls->init == sta2 && tdls->resp == sta))
+			os_memset(tdls->counters, 0, sizeof(tdls->counters));
+	}
+	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
 static void ctrl_get_sta_counter(struct wlantest *wt, int sock, u8 *cmd,
 				 size_t clen)
 {
@@ -355,6 +411,61 @@ static void ctrl_get_bss_counter(struct wlantest *wt, int sock, u8 *cmd,
 }
 
 
+static void ctrl_get_tdls_counter(struct wlantest *wt, int sock, u8 *cmd,
+				  size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	struct wlantest_sta *sta2;
+	struct wlantest_tdls *tdls;
+	u32 counter;
+	u8 buf[4 + 12], *end, *pos;
+	int found = 0;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	sta2 = ctrl_get_sta2(wt, sock, cmd, clen, bss);
+	if (sta == NULL || sta2 == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_TDLS_COUNTER, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	counter = WPA_GET_BE32(addr);
+	if (counter >= NUM_WLANTEST_TDLS_COUNTER) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if ((tdls->init == sta && tdls->resp == sta2) ||
+		    (tdls->init == sta2 && tdls->resp == sta)) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+			    tdls->counters[counter]);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
 static void build_mgmt_hdr(struct ieee80211_mgmt *mgmt,
 			   struct wlantest_bss *bss, struct wlantest_sta *sta,
 			   int sender_ap, int stype)
@@ -1096,12 +1207,18 @@ static void ctrl_read(int sock, void *eloop_ctx, void *sock_ctx)
 	case WLANTEST_CTRL_CLEAR_BSS_COUNTERS:
 		ctrl_clear_bss_counters(wt, sock, buf + 4, len - 4);
 		break;
+	case WLANTEST_CTRL_CLEAR_TDLS_COUNTERS:
+		ctrl_clear_tdls_counters(wt, sock, buf + 4, len - 4);
+		break;
 	case WLANTEST_CTRL_GET_STA_COUNTER:
 		ctrl_get_sta_counter(wt, sock, buf + 4, len - 4);
 		break;
 	case WLANTEST_CTRL_GET_BSS_COUNTER:
 		ctrl_get_bss_counter(wt, sock, buf + 4, len - 4);
 		break;
+	case WLANTEST_CTRL_GET_TDLS_COUNTER:
+		ctrl_get_tdls_counter(wt, sock, buf + 4, len - 4);
+		break;
 	case WLANTEST_CTRL_INJECT:
 		ctrl_inject(wt, sock, buf + 4, len - 4);
 		break;

+ 63 - 0
wlantest/rx_data.c

@@ -359,6 +359,66 @@ static void rx_data_bss(struct wlantest *wt, const struct ieee80211_hdr *hdr,
 }
 
 
+static struct wlantest_tdls * get_tdls(struct wlantest *wt, const u8 *bssid,
+				       const u8 *sta1_addr,
+				       const u8 *sta2_addr)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta1, *sta2;
+	struct wlantest_tdls *tdls;
+
+	bss = bss_find(wt, bssid);
+	if (bss == NULL)
+		return NULL;
+	sta1 = sta_find(bss, sta1_addr);
+	if (sta1 == NULL)
+		return NULL;
+	sta2 = sta_find(bss, sta2_addr);
+	if (sta2 == NULL)
+		return NULL;
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if ((tdls->init == sta1 && tdls->resp == sta2) ||
+		    (tdls->init == sta2 && tdls->resp == sta1))
+			return tdls;
+	}
+
+	return NULL;
+}
+
+
+static void add_direct_link(struct wlantest *wt, const u8 *bssid,
+			    const u8 *sta1_addr, const u8 *sta2_addr)
+{
+	struct wlantest_tdls *tdls;
+
+	tdls = get_tdls(wt, bssid, sta1_addr, sta2_addr);
+	if (tdls == NULL)
+		return;
+
+	if (tdls->link_up)
+		tdls->counters[WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK]++;
+	else
+		tdls->counters[WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK]++;
+}
+
+
+static void add_ap_path(struct wlantest *wt, const u8 *bssid,
+			const u8 *sta1_addr, const u8 *sta2_addr)
+{
+	struct wlantest_tdls *tdls;
+
+	tdls = get_tdls(wt, bssid, sta1_addr, sta2_addr);
+	if (tdls == NULL)
+		return;
+
+	if (tdls->link_up)
+		tdls->counters[WLANTEST_TDLS_COUNTER_INVALID_AP_PATH]++;
+	else
+		tdls->counters[WLANTEST_TDLS_COUNTER_VALID_AP_PATH]++;
+}
+
+
 void rx_data(struct wlantest *wt, const u8 *data, size_t len)
 {
 	const struct ieee80211_hdr *hdr;
@@ -393,6 +453,7 @@ void rx_data(struct wlantest *wt, const u8 *data, size_t len)
 			   fc & WLAN_FC_ISWEP ? " Prot" : "",
 			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
 			   MAC2STR(hdr->addr3));
+		add_direct_link(wt, hdr->addr3, hdr->addr1, hdr->addr2);
 		rx_data_bss(wt, hdr, qos, hdr->addr1, hdr->addr2,
 			    data + hdrlen, len - hdrlen);
 		break;
@@ -404,6 +465,7 @@ void rx_data(struct wlantest *wt, const u8 *data, size_t len)
 			   fc & WLAN_FC_ISWEP ? " Prot" : "",
 			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
 			   MAC2STR(hdr->addr3));
+		add_ap_path(wt, hdr->addr2, hdr->addr1, hdr->addr3);
 		rx_data_bss(wt, hdr, qos, hdr->addr1, hdr->addr2,
 			    data + hdrlen, len - hdrlen);
 		break;
@@ -415,6 +477,7 @@ void rx_data(struct wlantest *wt, const u8 *data, size_t len)
 			   fc & WLAN_FC_ISWEP ? " Prot" : "",
 			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
 			   MAC2STR(hdr->addr3));
+		add_ap_path(wt, hdr->addr1, hdr->addr3, hdr->addr2);
 		rx_data_bss(wt, hdr, qos, hdr->addr3, hdr->addr2,
 			    data + hdrlen, len - hdrlen);
 		break;

+ 1 - 0
wlantest/wlantest.h

@@ -96,6 +96,7 @@ struct wlantest_tdls {
 	u8 dialog_token;
 	u8 rsc_init[16 + 1][6];
 	u8 rsc_resp[16 + 1][6];
+	u32 counters[NUM_WLANTEST_TDLS_COUNTER];
 };
 
 struct wlantest_bss {

+ 198 - 0
wlantest/wlantest_cli.c

@@ -430,6 +430,81 @@ static char ** complete_clear_bss_counters(int s, const char *str, int pos)
 }
 
 
+static int cmd_clear_tdls_counters(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos;
+	int rlen;
+
+	if (argc < 3) {
+		printf("clear_tdls_counters needs three arguments: BSSID, "
+		       "STA1 address, STA2 address\n");
+		return -1;
+	}
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_TDLS_COUNTERS);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid STA1 address '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_STA2_ADDR);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[2], pos) < 0) {
+		printf("Invalid STA2 address '%s'\n", argv[2]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	printf("OK\n");
+	return 0;
+}
+
+
+static char ** complete_clear_tdls_counters(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		res = get_bssid_list(s);
+		break;
+	case 2:
+	case 3:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
 struct sta_counters {
 	const char *name;
 	enum wlantest_sta_counter num;
@@ -654,6 +729,120 @@ static char ** complete_get_bss_counter(int s, const char *str, int pos)
 }
 
 
+struct tdls_counters {
+	const char *name;
+	enum wlantest_tdls_counter num;
+};
+
+static const struct tdls_counters tdls_counters[] = {
+	{ "valid_direct_link", WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK },
+	{ "invalid_direct_link", WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK },
+	{ "valid_ap_path", WLANTEST_TDLS_COUNTER_VALID_AP_PATH },
+	{ "invalid_ap_path", WLANTEST_TDLS_COUNTER_INVALID_AP_PATH },
+	{ NULL, 0 }
+};
+
+static int cmd_get_tdls_counter(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	size_t len;
+
+	if (argc != 4) {
+		printf("get_tdls_counter needs four arguments: "
+		       "counter name, BSSID, STA1 address, STA2 address\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_TDLS_COUNTER);
+	pos += 4;
+
+	for (i = 0; tdls_counters[i].name; i++) {
+		if (os_strcasecmp(tdls_counters[i].name, argv[0]) == 0)
+			break;
+	}
+	if (tdls_counters[i].name == NULL) {
+		printf("Unknown TDLS counter '%s'\n", argv[0]);
+		printf("Counters:");
+		for (i = 0; tdls_counters[i].name; i++)
+			printf(" %s", tdls_counters[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_TDLS_COUNTER,
+			    tdls_counters[i].num);
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[2], pos) < 0) {
+		printf("Invalid STA1 address '%s'\n", argv[2]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA2_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[3], pos) < 0) {
+		printf("Invalid STA2 address '%s'\n", argv[3]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	printf("%u\n", WPA_GET_BE32(pos));
+	return 0;
+}
+
+
+static char ** complete_get_tdls_counter(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		/* counter list */
+		count = sizeof(tdls_counters) / sizeof(tdls_counters[0]);
+		res = os_zalloc(count * sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; tdls_counters[i].name; i++) {
+			res[i] = os_strdup(tdls_counters[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = get_bssid_list(s);
+		break;
+	case 3:
+	case 4:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
 struct inject_frames {
 	const char *name;
 	enum wlantest_inject_frame frame;
@@ -1254,6 +1443,15 @@ static const struct wlantest_cli_cmd wlantest_cli_commands[] = {
 	{ "info_bss", cmd_info_bss,
 	  "<field> <BSSID> = get BSS information",
 	  complete_info_bss },
+	{ "clear_tdls_counters", cmd_clear_tdls_counters,
+	  "<BSSID> <STA1> <STA2> = clear TDLS counters",
+	  complete_clear_tdls_counters },
+	{ "get_tdls_counter", cmd_get_tdls_counter,
+	  "<counter> <BSSID> <STA1> <STA2> = get TDLS counter value",
+	  complete_get_tdls_counter },
+	{ "get_bss_counter", cmd_get_bss_counter,
+	  "<counter> <BSSID> = get BSS counter value",
+	  complete_get_bss_counter },
 	{ NULL, NULL, NULL, NULL }
 };
 

+ 12 - 0
wlantest/wlantest_ctrl.h

@@ -39,6 +39,8 @@ enum wlantest_ctrl_cmd {
 	WLANTEST_CTRL_INFO_STA,
 	WLANTEST_CTRL_INFO_BSS,
 	WLANTEST_CTRL_SEND,
+	WLANTEST_CTRL_CLEAR_TDLS_COUNTERS,
+	WLANTEST_CTRL_GET_TDLS_COUNTER,
 };
 
 enum wlantest_ctrl_attr {
@@ -56,6 +58,8 @@ enum wlantest_ctrl_attr {
 	WLANTEST_ATTR_BSS_INFO,
 	WLANTEST_ATTR_INFO,
 	WLANTEST_ATTR_FRAME,
+	WLANTEST_ATTR_TDLS_COUNTER,
+	WLANTEST_ATTR_STA2_ADDR,
 };
 
 enum wlantest_bss_counter {
@@ -95,6 +99,14 @@ enum wlantest_sta_counter {
 	NUM_WLANTEST_STA_COUNTER
 };
 
+enum wlantest_tdls_counter {
+	WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK,
+	WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK,
+	WLANTEST_TDLS_COUNTER_VALID_AP_PATH,
+	WLANTEST_TDLS_COUNTER_INVALID_AP_PATH,
+	NUM_WLANTEST_TDLS_COUNTER
+};
+
 enum wlantest_inject_frame {
 	WLANTEST_FRAME_AUTH,
 	WLANTEST_FRAME_ASSOCREQ,