TDLS: Update peer STA as soon as full peer info is available
[mech_eap.git] / src / rsn_supp / tdls.c
index 84b7c1b..c1d0c62 100644 (file)
@@ -631,7 +631,33 @@ static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx)
 }
 
 
-static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm,
+                                          struct wpa_tdls_peer *peer)
+{
+       struct wpa_tdls_peer *cur, *prev;
+
+       cur = sm->tdls;
+       prev = NULL;
+       while (cur && cur != peer) {
+               prev = cur;
+               cur = cur->next;
+       }
+
+       if (cur != peer) {
+               wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR
+                          " to remove it from the list",
+                          MAC2STR(peer->addr));
+               return;
+       }
+
+       if (prev)
+               prev->next = peer->next;
+       else
+               sm->tdls = peer->next;
+}
+
+
+static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 {
        wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
                   MAC2STR(peer->addr));
@@ -663,6 +689,14 @@ static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 }
 
 
+static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+       wpa_tdls_peer_clear(sm, peer);
+       wpa_tdls_peer_remove_from_list(sm, peer);
+       os_free(peer);
+}
+
+
 static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
                            struct wpa_tdls_lnkid *lnkid)
 {
@@ -802,7 +836,7 @@ static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
 }
 
 
-void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr)
+void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr)
 {
        struct wpa_tdls_peer *peer;
 
@@ -811,8 +845,25 @@ void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr)
                        break;
        }
 
-       if (peer)
+       if (!peer || !peer->tpk_success) {
+               wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+                          " not connected - cannot teardown unreachable link",
+                          MAC2STR(addr));
+               return;
+       }
+
+       if (wpa_tdls_is_external_setup(sm)) {
+               /*
+                * Disable the link, send a teardown packet through the
+                * AP, and then reset link data.
+                */
+               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
+               wpa_tdls_send_teardown(sm, addr,
+                                      WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE);
+               wpa_tdls_peer_free(sm, peer);
+       } else {
                wpa_tdls_disable_peer_link(sm, peer);
+       }
 }
 
 
@@ -1361,10 +1412,17 @@ wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
 
        dialog_token = buf[sizeof(struct wpa_tdls_frame)];
 
+       /*
+        * Some APs will tack on a weird IE to the end of a TDLS
+        * discovery request packet. This needn't fail the response,
+        * since the required IE are verified separately.
+        */
        if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
                                     len - (sizeof(struct wpa_tdls_frame) + 1),
-                                    &kde) < 0)
-               return -1;
+                                    &kde) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround");
+       }
 
        if (!kde.lnkid) {
                wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
@@ -1620,16 +1678,16 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
                        wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
                                   "direct link is enabled - tear down the "
                                   "old link first");
-                       wpa_tdls_disable_peer_link(sm, peer);
-               }
-
-               /*
-                * An entry is already present, so check if we already sent a
-                * TDLS Setup Request. If so, compare MAC addresses and let the
-                * STA with the lower MAC address continue as the initiator.
-                * The other negotiation is terminated.
-                */
-               if (peer->initiator) {
+                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+                       wpa_tdls_peer_clear(sm, peer);
+               } else if (peer->initiator) {
+                       /*
+                        * An entry is already present, so check if we already
+                        * sent a TDLS Setup Request. If so, compare MAC
+                        * addresses and let the STA with the lower MAC address
+                        * continue as the initiator. The other negotiation is
+                        * terminated.
+                        */
                        if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
                                wpa_printf(MSG_DEBUG, "TDLS: Discard request "
                                           "from peer with higher address "
@@ -1641,7 +1699,9 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
                                           MACSTR " (terminate previously "
                                           "initiated negotiation",
                                           MAC2STR(src_addr));
-                               wpa_tdls_disable_peer_link(sm, peer);
+                               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
+                                                peer->addr);
+                               wpa_tdls_peer_clear(sm, peer);
                        }
                }
        }
@@ -1831,7 +1891,6 @@ skip_rsn:
                if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
                        wpa_msg(sm->ctx->ctx, MSG_WARNING,
                                "TDLS: Failed to get random data for responder nonce");
-                       wpa_tdls_peer_free(sm, peer);
                        goto error;
                }
        }
@@ -1887,13 +1946,24 @@ skip_rsn:
 
 skip_rsn_check:
        /* add the peer to the driver as a "setup in progress" peer */
-       wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0,
-                               NULL, 0, NULL, 0, NULL, 0);
+       if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, peer->aid,
+                                   peer->capability,
+                                   peer->supp_rates, peer->supp_rates_len,
+                                   peer->ht_capabilities,
+                                   peer->vht_capabilities,
+                                   peer->qos_info, peer->ext_capab,
+                                   peer->ext_capab_len,
+                                   peer->supp_channels,
+                                   peer->supp_channels_len,
+                                   peer->supp_oper_classes,
+                                   peer->supp_oper_classes_len))
+               goto error;
+
        peer->tpk_in_progress = 1;
 
        wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
        if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
-               wpa_tdls_disable_peer_link(sm, peer);
+               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
                goto error;
        }
 
@@ -1902,6 +1972,8 @@ skip_rsn_check:
 error:
        wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken,
                            status);
+       if (peer)
+               wpa_tdls_peer_free(sm, peer);
        return -1;
 }
 
@@ -1930,20 +2002,6 @@ static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 #endif /* CONFIG_TDLS_TESTING */
        }
 
-       /* add supported rates, capabilities, and qos_info to the TDLS peer */
-       if (wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->aid,
-                                   peer->capability,
-                                   peer->supp_rates, peer->supp_rates_len,
-                                   peer->ht_capabilities,
-                                   peer->vht_capabilities,
-                                   peer->qos_info, peer->ext_capab,
-                                   peer->ext_capab_len,
-                                   peer->supp_channels,
-                                   peer->supp_channels_len,
-                                   peer->supp_oper_classes,
-                                   peer->supp_oper_classes_len) < 0)
-               return -1;
-
        if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) {
                wpa_printf(MSG_INFO, "TDLS: Could not configure key to the "
                           "driver");
@@ -2197,12 +2255,24 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
 skip_rsn:
        peer->dtoken = dtoken;
 
+       /* add supported rates, capabilities, and qos_info to the TDLS peer */
+       if (wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->aid,
+                                   peer->capability,
+                                   peer->supp_rates, peer->supp_rates_len,
+                                   peer->ht_capabilities,
+                                   peer->vht_capabilities,
+                                   peer->qos_info, peer->ext_capab,
+                                   peer->ext_capab_len,
+                                   peer->supp_channels,
+                                   peer->supp_channels_len,
+                                   peer->supp_oper_classes,
+                                   peer->supp_oper_classes_len) < 0)
+               goto error;
+
        wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
                   "TPK Handshake Message 3");
-       if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0) {
-               wpa_tdls_disable_peer_link(sm, peer);
-               return -1;
-       }
+       if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
+               goto error;
 
        if (!peer->tpk_success) {
                /*
@@ -2270,9 +2340,16 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
        pos += 2 /* status code */ + 1 /* dialog token */;
 
        ielen = len - (pos - buf); /* start of IE in buf */
+
+       /*
+        * Don't reject the message if failing to parse IEs. The IEs we need are
+        * explicitly checked below. Some APs piggy-back broken IEs to the end
+        * of a TDLS Confirm packet, which will fail the link if we don't ignore
+        * this error.
+        */
        if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
-               wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3");
-               goto error;
+               wpa_printf(MSG_DEBUG,
+                          "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
        }
 
        if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
@@ -2357,6 +2434,20 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
        }
 
 skip_rsn:
+       /* add supported rates, capabilities, and qos_info to the TDLS peer */
+       if (wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->aid,
+                                   peer->capability,
+                                   peer->supp_rates, peer->supp_rates_len,
+                                   peer->ht_capabilities,
+                                   peer->vht_capabilities,
+                                   peer->qos_info, peer->ext_capab,
+                                   peer->ext_capab_len,
+                                   peer->supp_channels,
+                                   peer->supp_channels_len,
+                                   peer->supp_oper_classes,
+                                   peer->supp_oper_classes_len) < 0)
+               goto error;
+
        if (!peer->tpk_success) {
                /*
                 * Enable Link only when tpk_success is 0, signifying that this
@@ -2587,13 +2678,14 @@ int wpa_tdls_init(struct wpa_sm *sm)
 
 void wpa_tdls_teardown_peers(struct wpa_sm *sm)
 {
-       struct wpa_tdls_peer *peer;
+       struct wpa_tdls_peer *peer, *tmp;
 
        peer = sm->tdls;
 
        wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
 
        while (peer) {
+               tmp = peer->next;
                wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR,
                           MAC2STR(peer->addr));
                if (sm->tdls_external_setup)
@@ -2602,7 +2694,7 @@ void wpa_tdls_teardown_peers(struct wpa_sm *sm)
                else
                        wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
 
-               peer = peer->next;
+               peer = tmp;
        }
 }
 
@@ -2612,7 +2704,6 @@ static void wpa_tdls_remove_peers(struct wpa_sm *sm)
        struct wpa_tdls_peer *peer, *tmp;
 
        peer = sm->tdls;
-       sm->tdls = NULL;
 
        while (peer) {
                int res;
@@ -2621,7 +2712,6 @@ static void wpa_tdls_remove_peers(struct wpa_sm *sm)
                wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
                           MAC2STR(peer->addr), res);
                wpa_tdls_peer_free(sm, peer);
-               os_free(peer);
                peer = tmp;
        }
 }