tests: MBO and ignoring disallowed association
[mech_eap.git] / wlantest / rx_tdls.c
index 1602468..0c012a9 100644 (file)
@@ -2,14 +2,8 @@
  * Received Data frame processing for TDLS packets
  * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
 #include "wlantest.h"
 
 
-static struct wlantest_tdls * get_tdls(struct wlantest *wt, const u8 *linkid)
+static struct wlantest_tdls * get_tdls(struct wlantest *wt, const u8 *linkid,
+                                      int create_new, const u8 *bssid)
 {
        struct wlantest_bss *bss;
        struct wlantest_sta *init, *resp;
        struct wlantest_tdls *tdls;
 
        bss = bss_find(wt, linkid);
+       if (bss == NULL && bssid) {
+               bss = bss_find(wt, bssid);
+               if (bss)
+                       add_note(wt, MSG_INFO, "TDLS: Incorrect BSSID " MACSTR
+                                " in LinkId?! (init=" MACSTR " resp="
+                                MACSTR ")",
+                                MAC2STR(linkid), MAC2STR(linkid + ETH_ALEN),
+                                MAC2STR(linkid + 2 * ETH_ALEN));
+       }
        if (bss == NULL)
                return NULL;
 
@@ -46,6 +50,15 @@ static struct wlantest_tdls * get_tdls(struct wlantest *wt, const u8 *linkid)
                        return tdls;
        }
 
+       if (!create_new)
+               return NULL;
+
+       add_note(wt, MSG_DEBUG, "Add new TDLS link context: initiator " MACSTR
+                " responder " MACSTR " BSSID " MACSTR,
+                MAC2STR(linkid + ETH_ALEN),
+                MAC2STR(linkid + 2 * ETH_ALEN),
+                MAC2STR(bssid));
+
        tdls = os_zalloc(sizeof(*tdls));
        if (tdls == NULL)
                return NULL;
@@ -118,8 +131,8 @@ static int tdls_derive_tpk(struct wlantest_tdls *tdls, const u8 *bssid,
 }
 
 
-static int tdls_verify_mic(struct wlantest_tdls *tdls, u8 trans_seq,
-                          struct ieee802_11_elems *elems)
+static int tdls_verify_mic(struct wlantest *wt, struct wlantest_tdls *tdls,
+                          u8 trans_seq, struct ieee802_11_elems *elems)
 {
        u8 *buf, *pos;
        int len;
@@ -129,11 +142,12 @@ static int tdls_verify_mic(struct wlantest_tdls *tdls, u8 trans_seq,
        struct rsn_ftie *tmp_ftie;
 
        if (elems->link_id == NULL || elems->rsn_ie == NULL ||
-           elems->timeout_int == NULL || elems->ftie == NULL)
+           elems->timeout_int == NULL || elems->ftie == NULL ||
+           elems->ftie_len < sizeof(struct rsn_ftie))
                return -1;
 
        len = 2 * ETH_ALEN + 1 + 2 + 18 + 2 + elems->rsn_ie_len +
-               2 + elems->timeout_int_len + 2 + elems->ftie_len;
+               2 + 5 + 2 + elems->ftie_len;
 
        buf = os_zalloc(len);
        if (buf == NULL)
@@ -155,8 +169,8 @@ static int tdls_verify_mic(struct wlantest_tdls *tdls, u8 trans_seq,
        os_memcpy(pos, elems->rsn_ie - 2, 2 + elems->rsn_ie_len);
        pos += 2 + elems->rsn_ie_len;
        /* 6) Timeout Interval IE */
-       os_memcpy(pos, elems->timeout_int - 2, 2 + elems->timeout_int_len);
-       pos += 2 + elems->timeout_int_len;
+       os_memcpy(pos, elems->timeout_int - 2, 2 + 5);
+       pos += 2 + 5;
        /* 7) FTIE, with the MIC field of the FTIE set to 0 */
        os_memcpy(pos, elems->ftie - 2, 2 + elems->ftie_len);
        pos += 2;
@@ -174,10 +188,10 @@ static int tdls_verify_mic(struct wlantest_tdls *tdls, u8 trans_seq,
        rx_ftie = (const struct rsn_ftie *) elems->ftie;
 
        if (os_memcmp(mic, rx_ftie->mic, 16) == 0) {
-               wpa_printf(MSG_DEBUG, "TDLS: Valid MIC");
+               add_note(wt, MSG_DEBUG, "TDLS: Valid MIC");
                return 0;
        }
-       wpa_printf(MSG_DEBUG, "TDLS: Invalid MIC");
+       add_note(wt, MSG_DEBUG, "TDLS: Invalid MIC");
        return -1;
 }
 
@@ -188,9 +202,14 @@ static void rx_data_tdls_setup_request(struct wlantest *wt, const u8 *bssid,
                                       const u8 *data, size_t len)
 {
        struct ieee802_11_elems elems;
+       struct wlantest_tdls *tdls;
+       u8 linkid[3 * ETH_ALEN];
 
-       if (len < 3)
+       if (len < 3) {
+               add_note(wt, MSG_INFO, "Too short TDLS Setup Request " MACSTR
+                        " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
                return;
+       }
        wpa_printf(MSG_DEBUG, "TDLS Setup Request " MACSTR " -> "
                   MACSTR, MAC2STR(src), MAC2STR(dst));
 
@@ -201,6 +220,64 @@ static void rx_data_tdls_setup_request(struct wlantest *wt, const u8 *bssid,
                   " initiator STA " MACSTR " responder STA " MACSTR,
                   MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
                   MAC2STR(elems.link_id + 2 * ETH_ALEN));
+       tdls = get_tdls(wt, elems.link_id, 1, bssid);
+       if (tdls) {
+               tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_REQ]++;
+               tdls->dialog_token = data[0];
+               if (elems.ftie && elems.ftie_len >= sizeof(struct rsn_ftie)) {
+                       const struct rsn_ftie *f;
+                       f = (const struct rsn_ftie *) elems.ftie;
+                       os_memcpy(tdls->inonce, f->snonce, WPA_NONCE_LEN);
+               }
+       }
+
+       /* Check whether reverse direction context exists already */
+       os_memcpy(linkid, bssid, ETH_ALEN);
+       os_memcpy(linkid + ETH_ALEN, dst, ETH_ALEN);
+       os_memcpy(linkid + 2 * ETH_ALEN, src, ETH_ALEN);
+       tdls = get_tdls(wt, linkid, 0, bssid);
+       if (tdls)
+               add_note(wt, MSG_INFO, "Reverse direction TDLS context exists");
+}
+
+
+static void rx_data_tdls_setup_response_failure(struct wlantest *wt,
+                                               const u8 *bssid,
+                                               const u8 *sta_addr,
+                                               u8 dialog_token, u16 status)
+{
+       struct wlantest_bss *bss;
+       struct wlantest_tdls *tdls;
+       struct wlantest_sta *sta;
+
+       if (status == WLAN_STATUS_SUCCESS) {
+               add_note(wt, MSG_INFO, "TDLS: Invalid TDLS Setup Response from "
+                        MACSTR, MAC2STR(sta_addr));
+               return;
+       }
+
+       bss = bss_find(wt, bssid);
+       if (!bss)
+               return;
+       sta = sta_find(bss, sta_addr);
+       if (!sta)
+               return;
+
+       dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+               if (tdls->resp == sta) {
+                       if (dialog_token != tdls->dialog_token) {
+                               add_note(wt, MSG_DEBUG, "TDLS: Dialog token "
+                                        "mismatch in TDLS Setup Response "
+                                        "(failure)");
+                               break;
+                       }
+                       add_note(wt, MSG_DEBUG, "TDLS: Found matching TDLS "
+                                "setup session based on dialog token");
+                       tdls->counters[
+                               WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL]++;
+                       break;
+               }
+       }
 }
 
 
@@ -213,32 +290,103 @@ static void rx_data_tdls_setup_response(struct wlantest *wt, const u8 *bssid,
        struct ieee802_11_elems elems;
        struct wlantest_tdls *tdls;
 
-       if (len < 5)
+       if (len < 3) {
+               add_note(wt, MSG_INFO, "Too short TDLS Setup Response " MACSTR
+                        " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
                return;
+       }
        status = WPA_GET_LE16(data);
        wpa_printf(MSG_DEBUG, "TDLS Setup Response " MACSTR " -> "
                   MACSTR " (status %d)",
                   MAC2STR(src), MAC2STR(dst), status);
-       if (status != WLAN_STATUS_SUCCESS)
+       if (len < 5 && status == 0) {
+               add_note(wt, MSG_INFO, "Too short TDLS Setup Response " MACSTR
+                        " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
                return;
+       }
 
-       if (ieee802_11_parse_elems(data + 5, len - 5, &elems, 1) ==
-           ParseFailed || elems.link_id == NULL)
+       if (len < 5 ||
+           ieee802_11_parse_elems(data + 5, len - 5, &elems, 1) ==
+           ParseFailed || elems.link_id == NULL) {
+               /* Need to match TDLS link based on Dialog Token */
+               rx_data_tdls_setup_response_failure(wt, bssid, sta_addr,
+                                                   data[2], status);
                return;
+       }
        wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
                   " initiator STA " MACSTR " responder STA " MACSTR,
                   MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
                   MAC2STR(elems.link_id + 2 * ETH_ALEN));
 
-       tdls = get_tdls(wt, elems.link_id);
-       if (!tdls)
+       tdls = get_tdls(wt, elems.link_id, 1, bssid);
+       if (!tdls) {
+               add_note(wt, MSG_INFO, "No match TDLS context found");
                return;
+       }
+       if (status)
+               tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL]++;
+       else
+               tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_RESP_OK]++;
+
+       if (status != WLAN_STATUS_SUCCESS)
+               return;
+
+       if (elems.ftie && elems.ftie_len >= sizeof(struct rsn_ftie)) {
+               const struct rsn_ftie *f;
+               f = (const struct rsn_ftie *) elems.ftie;
+               if (os_memcmp(tdls->inonce, f->snonce, WPA_NONCE_LEN) != 0) {
+                       add_note(wt, MSG_INFO, "Mismatch in TDLS initiator "
+                                "nonce");
+               }
+               os_memcpy(tdls->rnonce, f->anonce, WPA_NONCE_LEN);
+       }
+
        if (tdls_derive_tpk(tdls, bssid, elems.ftie, elems.ftie_len) < 1)
                return;
-       if (tdls_verify_mic(tdls, 2, &elems) == 0) {
+       if (tdls_verify_mic(wt, tdls, 2, &elems) == 0) {
                tdls->dialog_token = data[2];
-               wpa_printf(MSG_DEBUG, "TDLS: Dialog Token for the link: %u",
-                          tdls->dialog_token);
+               add_note(wt, MSG_DEBUG, "TDLS: Dialog Token for the link: %u",
+                        tdls->dialog_token);
+       }
+}
+
+
+static void rx_data_tdls_setup_confirm_failure(struct wlantest *wt,
+                                              const u8 *bssid,
+                                              const u8 *src,
+                                              u8 dialog_token, u16 status)
+{
+       struct wlantest_bss *bss;
+       struct wlantest_tdls *tdls;
+       struct wlantest_sta *sta;
+
+       if (status == WLAN_STATUS_SUCCESS) {
+               add_note(wt, MSG_INFO, "TDLS: Invalid TDLS Setup Confirm from "
+                        MACSTR, MAC2STR(src));
+               return;
+       }
+
+       bss = bss_find(wt, bssid);
+       if (!bss)
+               return;
+       sta = sta_find(bss, src);
+       if (!sta)
+               return;
+
+       dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+               if (tdls->init == sta) {
+                       if (dialog_token != tdls->dialog_token) {
+                               add_note(wt, MSG_DEBUG, "TDLS: Dialog token "
+                                        "mismatch in TDLS Setup Confirm "
+                                        "(failure)");
+                               break;
+                       }
+                       add_note(wt, MSG_DEBUG, "TDLS: Found matching TDLS "
+                                "setup session based on dialog token");
+                       tdls->counters[
+                               WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL]++;
+                       break;
+               }
        }
 }
 
@@ -251,38 +399,86 @@ static void rx_data_tdls_setup_confirm(struct wlantest *wt, const u8 *bssid,
        u16 status;
        struct ieee802_11_elems elems;
        struct wlantest_tdls *tdls;
+       u8 link_id[3 * ETH_ALEN];
 
-       if (len < 3)
+       if (len < 3) {
+               add_note(wt, MSG_INFO, "Too short TDLS Setup Confirm " MACSTR
+                        " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
                return;
+       }
        status = WPA_GET_LE16(data);
        wpa_printf(MSG_DEBUG, "TDLS Setup Confirm " MACSTR " -> "
                   MACSTR " (status %d)",
                   MAC2STR(src), MAC2STR(dst), status);
-       if (status != WLAN_STATUS_SUCCESS)
-               return;
 
        if (ieee802_11_parse_elems(data + 3, len - 3, &elems, 1) ==
-           ParseFailed || elems.link_id == NULL)
+           ParseFailed || elems.link_id == NULL) {
+               /* Need to match TDLS link based on Dialog Token */
+               rx_data_tdls_setup_confirm_failure(wt, bssid, src,
+                                                  data[2], status);
                return;
+       }
        wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
                   " initiator STA " MACSTR " responder STA " MACSTR,
                   MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
                   MAC2STR(elems.link_id + 2 * ETH_ALEN));
 
-       tdls = get_tdls(wt, elems.link_id);
-       if (tdls)
-               tdls->link_up = 1;
-       if (tdls_derive_tpk(tdls, bssid, elems.ftie, elems.ftie_len) < 1)
+       tdls = get_tdls(wt, elems.link_id, 1, bssid);
+       if (tdls == NULL)
+               return;
+       if (status)
+               tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL]++;
+       else
+               tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_CONF_OK]++;
+
+       if (status != WLAN_STATUS_SUCCESS)
+               return;
+
+       if (elems.ftie && elems.ftie_len >= sizeof(struct rsn_ftie)) {
+               const struct rsn_ftie *f;
+               f = (const struct rsn_ftie *) elems.ftie;
+               if (os_memcmp(tdls->inonce, f->snonce, WPA_NONCE_LEN) != 0) {
+                       add_note(wt, MSG_INFO, "Mismatch in TDLS initiator "
+                                "nonce");
+               }
+               if (os_memcmp(tdls->rnonce, f->anonce, WPA_NONCE_LEN) != 0) {
+                       add_note(wt, MSG_INFO, "Mismatch in TDLS responder "
+                                "nonce");
+               }
+       }
+
+       tdls->link_up = 1;
+       if (tdls_derive_tpk(tdls, bssid, elems.ftie, elems.ftie_len) < 1) {
+               if (elems.ftie == NULL)
+                       goto remove_reverse;
                return;
-       if (tdls_verify_mic(tdls, 3, &elems) == 0) {
+       }
+       if (tdls_verify_mic(wt, tdls, 3, &elems) == 0) {
                tdls->dialog_token = data[2];
-               wpa_printf(MSG_DEBUG, "TDLS: Dialog Token for the link: %u",
-                          tdls->dialog_token);
+               add_note(wt, MSG_DEBUG, "TDLS: Link up - Dialog Token: %u",
+                        tdls->dialog_token);
+       }
+
+remove_reverse:
+       /*
+        * The TDLS link itself is bidirectional, but there is explicit
+        * initiator/responder roles. Remove the other direction of the link
+        * (if it exists) to make sure that the link counters are stored for
+        * the current TDLS entery.
+        */
+       os_memcpy(link_id, elems.link_id, ETH_ALEN);
+       os_memcpy(link_id + ETH_ALEN, elems.link_id + 2 * ETH_ALEN, ETH_ALEN);
+       os_memcpy(link_id + 2 * ETH_ALEN, elems.link_id + ETH_ALEN, ETH_ALEN);
+       tdls = get_tdls(wt, link_id, 0, bssid);
+       if (tdls) {
+               add_note(wt, MSG_DEBUG, "TDLS: Remove reverse link entry");
+               tdls_deinit(tdls);
        }
 }
 
 
-static int tdls_verify_mic_teardown(struct wlantest_tdls *tdls, u8 trans_seq,
+static int tdls_verify_mic_teardown(struct wlantest *wt,
+                                   struct wlantest_tdls *tdls, u8 trans_seq,
                                    const u8 *reason_code,
                                    struct ieee802_11_elems *elems)
 {
@@ -293,7 +489,8 @@ static int tdls_verify_mic_teardown(struct wlantest_tdls *tdls, u8 trans_seq,
        const struct rsn_ftie *rx_ftie;
        struct rsn_ftie *tmp_ftie;
 
-       if (elems->link_id == NULL || elems->ftie == NULL)
+       if (elems->link_id == NULL || elems->ftie == NULL ||
+           elems->ftie_len < sizeof(struct rsn_ftie))
                return -1;
 
        len = 2 + 18 + 2 + 1 + 1 + 2 + elems->ftie_len;
@@ -330,10 +527,10 @@ static int tdls_verify_mic_teardown(struct wlantest_tdls *tdls, u8 trans_seq,
        rx_ftie = (const struct rsn_ftie *) elems->ftie;
 
        if (os_memcmp(mic, rx_ftie->mic, 16) == 0) {
-               wpa_printf(MSG_DEBUG, "TDLS: Valid MIC");
+               add_note(wt, MSG_DEBUG, "TDLS: Valid MIC");
                return 0;
        }
-       wpa_printf(MSG_DEBUG, "TDLS: Invalid MIC");
+       add_note(wt, MSG_DEBUG, "TDLS: Invalid MIC");
        return -1;
 }
 
@@ -362,10 +559,14 @@ static void rx_data_tdls_teardown(struct wlantest *wt, const u8 *bssid,
                   MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
                   MAC2STR(elems.link_id + 2 * ETH_ALEN));
 
-       tdls = get_tdls(wt, elems.link_id);
-       if (tdls)
+       tdls = get_tdls(wt, elems.link_id, 1, bssid);
+       if (tdls) {
+               if (tdls->link_up)
+                       add_note(wt, MSG_DEBUG, "TDLS: Link down");
                tdls->link_up = 0;
-       tdls_verify_mic_teardown(tdls, 4, data, &elems);
+               tdls->counters[WLANTEST_TDLS_COUNTER_TEARDOWN]++;
+               tdls_verify_mic_teardown(wt, tdls, 4, data, &elems);
+       }
 }