Browse 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 14 years ago
parent
commit
0e42fff3de
5 changed files with 393 additions and 2 deletions
  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,