OSDN Git Service

Accumulative patch from commit b618a469c42120e984ab1c85ed6058504d1fca78
[android-x86/external-wpa_supplicant_8.git] / src / rsn_supp / tdls.c
index 27090e3..09adc19 100644 (file)
@@ -2,14 +2,8 @@
  * wpa_supplicant - TDLS
  * Copyright (c) 2010-2011, Atheros Communications
  *
- * 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"
@@ -126,6 +120,13 @@ struct wpa_tdls_peer {
 
        u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
        size_t supp_rates_len;
+
+       struct ieee80211_ht_capabilities *ht_capabilities;
+
+       u8 qos_info;
+
+       u8 *ext_capab;
+       size_t ext_capab_len;
 };
 
 
@@ -617,6 +618,10 @@ static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
        peer->initiator = 0;
        os_free(peer->sm_tmr.buf);
        peer->sm_tmr.buf = NULL;
+       os_free(peer->ht_capabilities);
+       peer->ht_capabilities = NULL;
+       os_free(peer->ext_capab);
+       peer->ext_capab = NULL;
        peer->rsnie_i_len = peer->rsnie_p_len = 0;
        peer->cipher = 0;
        peer->tpk_set = peer->tpk_success = 0;
@@ -686,8 +691,13 @@ int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
                return -1;
        pos = rbuf;
 
-       if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
+       if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success) {
+               if (reason_code != WLAN_REASON_DEAUTH_LEAVING) {
+                       /* Overwrite the reason code */
+                       reason_code = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
+               }
                goto skip_ies;
+       }
 
        ftie = (struct wpa_tdls_ftie *) pos;
        ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
@@ -721,8 +731,7 @@ skip_ies:
 
        /* request driver to send Teardown using this FTIE */
        wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
-                         WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, rbuf,
-                         pos - rbuf);
+                         reason_code, rbuf, pos - rbuf);
        os_free(rbuf);
 
        /* clear the Peerkey statemachine */
@@ -874,10 +883,20 @@ static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
 
 
 static struct wpa_tdls_peer *
-wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr)
+wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing)
 {
        struct wpa_tdls_peer *peer;
 
+       if (existing)
+               *existing = 0;
+       for (peer = sm->tdls; peer; peer = peer->next) {
+               if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) {
+                       if (existing)
+                               *existing = 1;
+                       return peer; /* re-use existing entry */
+               }
+       }
+
        wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR,
                   MAC2STR(addr));
 
@@ -1288,7 +1307,7 @@ wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
                return -1;
        }
 
-       peer = wpa_tdls_add_peer(sm, addr);
+       peer = wpa_tdls_add_peer(sm, addr, NULL);
        if (peer == NULL)
                return -1;
 
@@ -1315,21 +1334,62 @@ static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde,
                wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
                return -1;
        }
+       peer->supp_rates_len = merge_byte_arrays(
+               peer->supp_rates, sizeof(peer->supp_rates),
+               kde->supp_rates + 2, kde->supp_rates_len - 2,
+               kde->ext_supp_rates + 2, kde->ext_supp_rates_len - 2);
+       return 0;
+}
 
-       peer->supp_rates_len = kde->supp_rates_len - 2;
-       if (peer->supp_rates_len > IEEE80211_MAX_SUPP_RATES)
-               peer->supp_rates_len = IEEE80211_MAX_SUPP_RATES;
-       os_memcpy(peer->supp_rates, kde->supp_rates + 2, peer->supp_rates_len);
 
-       if (kde->ext_supp_rates) {
-               int clen = kde->ext_supp_rates_len - 2;
-               if (peer->supp_rates_len + clen > IEEE80211_MAX_SUPP_RATES)
-                       clen = IEEE80211_MAX_SUPP_RATES - peer->supp_rates_len;
-               os_memcpy(peer->supp_rates + peer->supp_rates_len,
-                         kde->ext_supp_rates + 2, clen);
-               peer->supp_rates_len += clen;
+static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde,
+                             struct wpa_tdls_peer *peer)
+{
+       if (!kde->ht_capabilities ||
+           kde->ht_capabilities_len <
+           sizeof(struct ieee80211_ht_capabilities) ) {
+               wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities "
+                          "received");
+               return 0;
        }
 
+       if (!peer->ht_capabilities) {
+               peer->ht_capabilities =
+                        os_zalloc(sizeof(struct ieee80211_ht_capabilities));
+               if (peer->ht_capabilities == NULL)
+                        return -1;
+       }
+
+       os_memcpy(peer->ht_capabilities, kde->ht_capabilities,
+                  sizeof(struct ieee80211_ht_capabilities));
+       wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities",
+                   (u8 *) peer->ht_capabilities,
+                   sizeof(struct ieee80211_ht_capabilities));
+
+       return 0;
+}
+
+
+static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde,
+                              struct wpa_tdls_peer *peer)
+{
+       if (!kde->ext_capab) {
+               wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities "
+                          "received");
+               return 0;
+       }
+
+       if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) {
+               /* Need to allocate buffer to fit the new information */
+               os_free(peer->ext_capab);
+               peer->ext_capab = os_zalloc(kde->ext_capab_len - 2);
+               if (peer->ext_capab == NULL)
+                       return -1;
+       }
+
+       peer->ext_capab_len = kde->ext_capab_len - 2;
+       os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len);
+
        return 0;
 }
 
@@ -1369,18 +1429,9 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
 
        wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken);
 
-       for (peer = sm->tdls; peer; peer = peer->next) {
-               if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) {
-                       existing_peer = 1;
-                       break;
-               }
-       }
-
-       if (peer == NULL) {
-               peer = wpa_tdls_add_peer(sm, src_addr);
-               if (peer == NULL)
-                       goto error;
-       }
+       peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer);
+       if (peer == NULL)
+               goto error;
 
        /* capability information */
        peer->capability = WPA_GET_LE16(cpos);
@@ -1412,17 +1463,19 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
        if (copy_supp_rates(&kde, peer) < 0)
                goto error;
 
+       if (copy_peer_ht_capab(&kde, peer) < 0)
+               goto error;
+
+       if (copy_peer_ext_capab(&kde, peer) < 0)
+               goto error;
+
+       peer->qos_info = kde.qosinfo;
+
 #ifdef CONFIG_TDLS_TESTING
        if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
-               for (peer = sm->tdls; peer; peer = peer->next) {
-                       if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
-                               break;
-               }
-               if (peer == NULL) {
-                       peer = wpa_tdls_add_peer(sm, src_addr);
-                       if (peer == NULL)
-                               goto error;
-               }
+               peer = wpa_tdls_add_peer(sm, src_addr, NULL);
+               if (peer == NULL)
+                       goto error;
                wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of "
                           "TDLS setup - send own request");
                peer->initiator = 1;
@@ -1641,7 +1694,8 @@ 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, NULL, 0);
+       wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0, NULL, 0,
+                               NULL, 0);
 
        wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
        if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
@@ -1681,9 +1735,11 @@ static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 #endif /* CONFIG_TDLS_TESTING */
        }
 
-       /* add supported rates and capabilities to the TDLS peer */
+       /* add supported rates, capabilities, and qos_info to the TDLS peer */
        wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability,
-                               peer->supp_rates, peer->supp_rates_len);
+                               peer->supp_rates, peer->supp_rates_len,
+                               peer->ht_capabilities, peer->qos_info,
+                               peer->ext_capab, peer->ext_capab_len);
 
        wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr);
 }
@@ -1779,6 +1835,14 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
        if (copy_supp_rates(&kde, peer) < 0)
                goto error;
 
+       if (copy_peer_ht_capab(&kde, peer) < 0)
+               goto error;
+
+       if (copy_peer_ext_capab(&kde, peer) < 0)
+               goto error;
+
+       peer->qos_info = kde.qosinfo;
+
        if (!wpa_tdls_get_privacy(sm)) {
                peer->rsnie_p_len = 0;
                peer->cipher = WPA_CIPHER_NONE;
@@ -2075,23 +2139,15 @@ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
                return -1;
        }
 
-       /* Find existing entry and if found, use that instead of adding
-        * a new one */
-       for (peer = sm->tdls; peer; peer = peer->next) {
-               if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
-                       break;
-       }
-
-       if (peer == NULL) {
-               peer = wpa_tdls_add_peer(sm, addr);
-               if (peer == NULL)
-                       return -1;
-       }
+       peer = wpa_tdls_add_peer(sm, addr, NULL);
+       if (peer == NULL)
+               return -1;
 
        peer->initiator = 1;
 
        /* add the peer to the driver as a "setup in progress" peer */
-       wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0);
+       wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0, NULL, 0,
+                               NULL, 0);
 
        if (wpa_tdls_send_tpk_m1(sm, peer) < 0) {
                wpa_tdls_disable_link(sm, peer->addr);
@@ -2102,12 +2158,12 @@ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
 }
 
 
-int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr)
+void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr)
 {
        struct wpa_tdls_peer *peer;
 
        if (sm->tdls_disabled || !sm->tdls_supported)
-               return -1;
+               return;
 
        for (peer = sm->tdls; peer; peer = peer->next) {
                if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
@@ -2115,7 +2171,7 @@ int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr)
        }
 
        if (peer == NULL || !peer->tpk_success)
-               return -1;
+               return;
 
        if (sm->tdls_external_setup) {
                /*
@@ -2124,8 +2180,6 @@ int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr)
                 */
                wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
        }
-
-       return wpa_tdls_start(sm, addr);
 }
 
 
@@ -2208,7 +2262,9 @@ int wpa_tdls_init(struct wpa_sm *sm)
        if (sm == NULL)
                return -1;
 
-       sm->l2_tdls = l2_packet_init(sm->ifname, sm->own_addr,
+       sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
+                                    sm->ifname,
+                                    sm->own_addr,
                                     ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
                                     sm, 0);
        if (sm->l2_tdls == NULL) {
@@ -2236,6 +2292,28 @@ int wpa_tdls_init(struct wpa_sm *sm)
 }
 
 
+void wpa_tdls_teardown_peers(struct wpa_sm *sm)
+{
+       struct wpa_tdls_peer *peer;
+
+       peer = sm->tdls;
+
+       wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
+
+       while (peer) {
+               wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR,
+                          MAC2STR(peer->addr));
+               if (sm->tdls_external_setup)
+                       wpa_tdls_send_teardown(sm, peer->addr,
+                                              WLAN_REASON_DEAUTH_LEAVING);
+               else
+                       wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
+
+               peer = peer->next;
+       }
+}
+
+
 static void wpa_tdls_remove_peers(struct wpa_sm *sm)
 {
        struct wpa_tdls_peer *peer, *tmp;