13ff40c12SJohn Marino /*
23ff40c12SJohn Marino  * wpa_supplicant - TDLS
33ff40c12SJohn Marino  * Copyright (c) 2010-2011, Atheros Communications
43ff40c12SJohn Marino  *
53ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino  * See README for more details.
73ff40c12SJohn Marino  */
83ff40c12SJohn Marino 
93ff40c12SJohn Marino #include "utils/includes.h"
103ff40c12SJohn Marino 
113ff40c12SJohn Marino #include "utils/common.h"
123ff40c12SJohn Marino #include "utils/eloop.h"
133ff40c12SJohn Marino #include "utils/os.h"
143ff40c12SJohn Marino #include "common/ieee802_11_defs.h"
15*a1157835SDaniel Fojt #include "common/ieee802_11_common.h"
163ff40c12SJohn Marino #include "crypto/sha256.h"
173ff40c12SJohn Marino #include "crypto/crypto.h"
183ff40c12SJohn Marino #include "crypto/aes_wrap.h"
193ff40c12SJohn Marino #include "rsn_supp/wpa.h"
203ff40c12SJohn Marino #include "rsn_supp/wpa_ie.h"
213ff40c12SJohn Marino #include "rsn_supp/wpa_i.h"
223ff40c12SJohn Marino #include "drivers/driver.h"
233ff40c12SJohn Marino #include "l2_packet/l2_packet.h"
243ff40c12SJohn Marino 
253ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
263ff40c12SJohn Marino #define TDLS_TESTING_LONG_FRAME BIT(0)
273ff40c12SJohn Marino #define TDLS_TESTING_ALT_RSN_IE BIT(1)
283ff40c12SJohn Marino #define TDLS_TESTING_DIFF_BSSID BIT(2)
293ff40c12SJohn Marino #define TDLS_TESTING_SHORT_LIFETIME BIT(3)
303ff40c12SJohn Marino #define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4)
313ff40c12SJohn Marino #define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5)
323ff40c12SJohn Marino #define TDLS_TESTING_LONG_LIFETIME BIT(6)
333ff40c12SJohn Marino #define TDLS_TESTING_CONCURRENT_INIT BIT(7)
343ff40c12SJohn Marino #define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8)
353ff40c12SJohn Marino #define TDLS_TESTING_DECLINE_RESP BIT(9)
363ff40c12SJohn Marino #define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
37*a1157835SDaniel Fojt #define TDLS_TESTING_WRONG_MIC BIT(11)
38*a1157835SDaniel Fojt #define TDLS_TESTING_DOUBLE_TPK_M2 BIT(12)
393ff40c12SJohn Marino unsigned int tdls_testing = 0;
403ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
413ff40c12SJohn Marino 
423ff40c12SJohn Marino #define TPK_LIFETIME 43200 /* 12 hours */
433ff40c12SJohn Marino #define TPK_M1_RETRY_COUNT 3
443ff40c12SJohn Marino #define TPK_M1_TIMEOUT 5000 /* in milliseconds */
453ff40c12SJohn Marino #define TPK_M2_RETRY_COUNT 10
463ff40c12SJohn Marino #define TPK_M2_TIMEOUT 500 /* in milliseconds */
473ff40c12SJohn Marino 
483ff40c12SJohn Marino #define TDLS_MIC_LEN		16
493ff40c12SJohn Marino 
503ff40c12SJohn Marino #define TDLS_TIMEOUT_LEN	4
513ff40c12SJohn Marino 
523ff40c12SJohn Marino struct wpa_tdls_ftie {
533ff40c12SJohn Marino 	u8 ie_type; /* FTIE */
543ff40c12SJohn Marino 	u8 ie_len;
553ff40c12SJohn Marino 	u8 mic_ctrl[2];
563ff40c12SJohn Marino 	u8 mic[TDLS_MIC_LEN];
573ff40c12SJohn Marino 	u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */
583ff40c12SJohn Marino 	u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */
593ff40c12SJohn Marino 	/* followed by optional elements */
603ff40c12SJohn Marino } STRUCT_PACKED;
613ff40c12SJohn Marino 
623ff40c12SJohn Marino struct wpa_tdls_timeoutie {
633ff40c12SJohn Marino 	u8 ie_type; /* Timeout IE */
643ff40c12SJohn Marino 	u8 ie_len;
653ff40c12SJohn Marino 	u8 interval_type;
663ff40c12SJohn Marino 	u8 value[TDLS_TIMEOUT_LEN];
673ff40c12SJohn Marino } STRUCT_PACKED;
683ff40c12SJohn Marino 
693ff40c12SJohn Marino struct wpa_tdls_lnkid {
703ff40c12SJohn Marino 	u8 ie_type; /* Link Identifier IE */
713ff40c12SJohn Marino 	u8 ie_len;
723ff40c12SJohn Marino 	u8 bssid[ETH_ALEN];
733ff40c12SJohn Marino 	u8 init_sta[ETH_ALEN];
743ff40c12SJohn Marino 	u8 resp_sta[ETH_ALEN];
753ff40c12SJohn Marino } STRUCT_PACKED;
763ff40c12SJohn Marino 
773ff40c12SJohn Marino /* TDLS frame headers as per IEEE Std 802.11z-2010 */
783ff40c12SJohn Marino struct wpa_tdls_frame {
793ff40c12SJohn Marino 	u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */
803ff40c12SJohn Marino 	u8 category; /* Category */
813ff40c12SJohn Marino 	u8 action; /* Action (enum tdls_frame_type) */
823ff40c12SJohn Marino } STRUCT_PACKED;
833ff40c12SJohn Marino 
843ff40c12SJohn Marino static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs);
853ff40c12SJohn Marino static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx);
863ff40c12SJohn Marino static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer);
873ff40c12SJohn Marino static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
883ff40c12SJohn Marino 				       struct wpa_tdls_peer *peer);
89*a1157835SDaniel Fojt static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
90*a1157835SDaniel Fojt 				  u16 reason_code);
913ff40c12SJohn Marino 
923ff40c12SJohn Marino 
933ff40c12SJohn Marino #define TDLS_MAX_IE_LEN 80
943ff40c12SJohn Marino #define IEEE80211_MAX_SUPP_RATES 32
953ff40c12SJohn Marino 
963ff40c12SJohn Marino struct wpa_tdls_peer {
973ff40c12SJohn Marino 	struct wpa_tdls_peer *next;
983ff40c12SJohn Marino 	unsigned int reconfig_key:1;
993ff40c12SJohn Marino 	int initiator; /* whether this end was initiator for TDLS setup */
1003ff40c12SJohn Marino 	u8 addr[ETH_ALEN]; /* other end MAC address */
1013ff40c12SJohn Marino 	u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
1023ff40c12SJohn Marino 	u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */
1033ff40c12SJohn Marino 	u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */
1043ff40c12SJohn Marino 	size_t rsnie_i_len;
1053ff40c12SJohn Marino 	u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */
1063ff40c12SJohn Marino 	size_t rsnie_p_len;
1073ff40c12SJohn Marino 	u32 lifetime;
1083ff40c12SJohn Marino 	int cipher; /* Selected cipher (WPA_CIPHER_*) */
1093ff40c12SJohn Marino 	u8 dtoken;
1103ff40c12SJohn Marino 
1113ff40c12SJohn Marino 	struct tpk {
1123ff40c12SJohn Marino 		u8 kck[16]; /* TPK-KCK */
1133ff40c12SJohn Marino 		u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */
1143ff40c12SJohn Marino 	} tpk;
1153ff40c12SJohn Marino 	int tpk_set;
116*a1157835SDaniel Fojt 	int tk_set; /* TPK-TK configured to the driver */
1173ff40c12SJohn Marino 	int tpk_success;
1183ff40c12SJohn Marino 	int tpk_in_progress;
1193ff40c12SJohn Marino 
1203ff40c12SJohn Marino 	struct tpk_timer {
1213ff40c12SJohn Marino 		u8 dest[ETH_ALEN];
1223ff40c12SJohn Marino 		int count;      /* Retry Count */
1233ff40c12SJohn Marino 		int timer;      /* Timeout in milliseconds */
1243ff40c12SJohn Marino 		u8 action_code; /* TDLS frame type */
1253ff40c12SJohn Marino 		u8 dialog_token;
1263ff40c12SJohn Marino 		u16 status_code;
127*a1157835SDaniel Fojt 		u32 peer_capab;
1283ff40c12SJohn Marino 		int buf_len;    /* length of TPK message for retransmission */
1293ff40c12SJohn Marino 		u8 *buf;        /* buffer for TPK message */
1303ff40c12SJohn Marino 	} sm_tmr;
1313ff40c12SJohn Marino 
1323ff40c12SJohn Marino 	u16 capability;
1333ff40c12SJohn Marino 
1343ff40c12SJohn Marino 	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
1353ff40c12SJohn Marino 	size_t supp_rates_len;
1363ff40c12SJohn Marino 
1373ff40c12SJohn Marino 	struct ieee80211_ht_capabilities *ht_capabilities;
1383ff40c12SJohn Marino 	struct ieee80211_vht_capabilities *vht_capabilities;
1393ff40c12SJohn Marino 
1403ff40c12SJohn Marino 	u8 qos_info;
1413ff40c12SJohn Marino 
1423ff40c12SJohn Marino 	u16 aid;
1433ff40c12SJohn Marino 
1443ff40c12SJohn Marino 	u8 *ext_capab;
1453ff40c12SJohn Marino 	size_t ext_capab_len;
1463ff40c12SJohn Marino 
1473ff40c12SJohn Marino 	u8 *supp_channels;
1483ff40c12SJohn Marino 	size_t supp_channels_len;
1493ff40c12SJohn Marino 
1503ff40c12SJohn Marino 	u8 *supp_oper_classes;
1513ff40c12SJohn Marino 	size_t supp_oper_classes_len;
152*a1157835SDaniel Fojt 
153*a1157835SDaniel Fojt 	u8 wmm_capable;
154*a1157835SDaniel Fojt 
155*a1157835SDaniel Fojt 	/* channel switch currently enabled */
156*a1157835SDaniel Fojt 	int chan_switch_enabled;
1573ff40c12SJohn Marino };
1583ff40c12SJohn Marino 
1593ff40c12SJohn Marino 
wpa_tdls_get_privacy(struct wpa_sm * sm)1603ff40c12SJohn Marino static int wpa_tdls_get_privacy(struct wpa_sm *sm)
1613ff40c12SJohn Marino {
1623ff40c12SJohn Marino 	/*
1633ff40c12SJohn Marino 	 * Get info needed from supplicant to check if the current BSS supports
1643ff40c12SJohn Marino 	 * security. Other than OPEN mode, rest are considered secured
1653ff40c12SJohn Marino 	 * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake.
1663ff40c12SJohn Marino 	 */
1673ff40c12SJohn Marino 	return sm->pairwise_cipher != WPA_CIPHER_NONE;
1683ff40c12SJohn Marino }
1693ff40c12SJohn Marino 
1703ff40c12SJohn Marino 
wpa_add_ie(u8 * pos,const u8 * ie,size_t ie_len)1713ff40c12SJohn Marino static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
1723ff40c12SJohn Marino {
1733ff40c12SJohn Marino 	os_memcpy(pos, ie, ie_len);
1743ff40c12SJohn Marino 	return pos + ie_len;
1753ff40c12SJohn Marino }
1763ff40c12SJohn Marino 
1773ff40c12SJohn Marino 
wpa_tdls_del_key(struct wpa_sm * sm,struct wpa_tdls_peer * peer)1783ff40c12SJohn Marino static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
1793ff40c12SJohn Marino {
1803ff40c12SJohn Marino 	if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
1813ff40c12SJohn Marino 			   0, 0, NULL, 0, NULL, 0) < 0) {
1823ff40c12SJohn Marino 		wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
1833ff40c12SJohn Marino 			   "the driver");
1843ff40c12SJohn Marino 		return -1;
1853ff40c12SJohn Marino 	}
1863ff40c12SJohn Marino 
1873ff40c12SJohn Marino 	return 0;
1883ff40c12SJohn Marino }
1893ff40c12SJohn Marino 
1903ff40c12SJohn Marino 
wpa_tdls_set_key(struct wpa_sm * sm,struct wpa_tdls_peer * peer)1913ff40c12SJohn Marino static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
1923ff40c12SJohn Marino {
1933ff40c12SJohn Marino 	u8 key_len;
1943ff40c12SJohn Marino 	u8 rsc[6];
1953ff40c12SJohn Marino 	enum wpa_alg alg;
1963ff40c12SJohn Marino 
197*a1157835SDaniel Fojt 	if (peer->tk_set) {
198*a1157835SDaniel Fojt 		/*
199*a1157835SDaniel Fojt 		 * This same TPK-TK has already been configured to the driver
200*a1157835SDaniel Fojt 		 * and this new configuration attempt (likely due to an
201*a1157835SDaniel Fojt 		 * unexpected retransmitted frame) would result in clearing
202*a1157835SDaniel Fojt 		 * the TX/RX sequence number which can break security, so must
203*a1157835SDaniel Fojt 		 * not allow that to happen.
204*a1157835SDaniel Fojt 		 */
205*a1157835SDaniel Fojt 		wpa_printf(MSG_INFO, "TDLS: TPK-TK for the peer " MACSTR
206*a1157835SDaniel Fojt 			   " has already been configured to the driver - do not reconfigure",
207*a1157835SDaniel Fojt 			   MAC2STR(peer->addr));
208*a1157835SDaniel Fojt 		return -1;
209*a1157835SDaniel Fojt 	}
210*a1157835SDaniel Fojt 
2113ff40c12SJohn Marino 	os_memset(rsc, 0, 6);
2123ff40c12SJohn Marino 
2133ff40c12SJohn Marino 	switch (peer->cipher) {
2143ff40c12SJohn Marino 	case WPA_CIPHER_CCMP:
2153ff40c12SJohn Marino 		alg = WPA_ALG_CCMP;
2163ff40c12SJohn Marino 		key_len = 16;
2173ff40c12SJohn Marino 		break;
2183ff40c12SJohn Marino 	case WPA_CIPHER_NONE:
2193ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: "
2203ff40c12SJohn Marino 			   "NONE - do not use pairwise keys");
2213ff40c12SJohn Marino 		return -1;
2223ff40c12SJohn Marino 	default:
2233ff40c12SJohn Marino 		wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d",
2243ff40c12SJohn Marino 			   sm->pairwise_cipher);
2253ff40c12SJohn Marino 		return -1;
2263ff40c12SJohn Marino 	}
2273ff40c12SJohn Marino 
228*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "TDLS: Configure pairwise key for peer " MACSTR,
229*a1157835SDaniel Fojt 		   MAC2STR(peer->addr));
2303ff40c12SJohn Marino 	if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1,
2313ff40c12SJohn Marino 			   rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) {
2323ff40c12SJohn Marino 		wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
2333ff40c12SJohn Marino 			   "driver");
2343ff40c12SJohn Marino 		return -1;
2353ff40c12SJohn Marino 	}
236*a1157835SDaniel Fojt 	peer->tk_set = 1;
2373ff40c12SJohn Marino 	return 0;
2383ff40c12SJohn Marino }
2393ff40c12SJohn Marino 
2403ff40c12SJohn Marino 
wpa_tdls_send_tpk_msg(struct wpa_sm * sm,const u8 * dst,u8 action_code,u8 dialog_token,u16 status_code,u32 peer_capab,int initiator,const u8 * buf,size_t len)2413ff40c12SJohn Marino static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
2423ff40c12SJohn Marino 				 u8 action_code, u8 dialog_token,
243*a1157835SDaniel Fojt 				 u16 status_code, u32 peer_capab,
244*a1157835SDaniel Fojt 				 int initiator, const u8 *buf, size_t len)
2453ff40c12SJohn Marino {
2463ff40c12SJohn Marino 	return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
247*a1157835SDaniel Fojt 				     status_code, peer_capab, initiator, buf,
248*a1157835SDaniel Fojt 				     len);
2493ff40c12SJohn Marino }
2503ff40c12SJohn Marino 
2513ff40c12SJohn Marino 
wpa_tdls_tpk_send(struct wpa_sm * sm,const u8 * dest,u8 action_code,u8 dialog_token,u16 status_code,u32 peer_capab,int initiator,const u8 * msg,size_t msg_len)2523ff40c12SJohn Marino static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
253*a1157835SDaniel Fojt 			     u8 dialog_token, u16 status_code, u32 peer_capab,
254*a1157835SDaniel Fojt 			     int initiator, const u8 *msg, size_t msg_len)
2553ff40c12SJohn Marino {
2563ff40c12SJohn Marino 	struct wpa_tdls_peer *peer;
2573ff40c12SJohn Marino 
2583ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u "
259*a1157835SDaniel Fojt 		   "dialog_token=%u status_code=%u peer_capab=%u initiator=%d "
260*a1157835SDaniel Fojt 		   "msg_len=%u",
2613ff40c12SJohn Marino 		   MAC2STR(dest), action_code, dialog_token, status_code,
262*a1157835SDaniel Fojt 		   peer_capab, initiator, (unsigned int) msg_len);
2633ff40c12SJohn Marino 
2643ff40c12SJohn Marino 	if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
265*a1157835SDaniel Fojt 				  status_code, peer_capab, initiator, msg,
266*a1157835SDaniel Fojt 				  msg_len)) {
2673ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Failed to send message "
2683ff40c12SJohn Marino 			   "(action_code=%u)", action_code);
2693ff40c12SJohn Marino 		return -1;
2703ff40c12SJohn Marino 	}
2713ff40c12SJohn Marino 
2723ff40c12SJohn Marino 	if (action_code == WLAN_TDLS_SETUP_CONFIRM ||
2733ff40c12SJohn Marino 	    action_code == WLAN_TDLS_TEARDOWN ||
2743ff40c12SJohn Marino 	    action_code == WLAN_TDLS_DISCOVERY_REQUEST ||
2753ff40c12SJohn Marino 	    action_code == WLAN_TDLS_DISCOVERY_RESPONSE)
2763ff40c12SJohn Marino 		return 0; /* No retries */
2773ff40c12SJohn Marino 
2783ff40c12SJohn Marino 	for (peer = sm->tdls; peer; peer = peer->next) {
2793ff40c12SJohn Marino 		if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0)
2803ff40c12SJohn Marino 			break;
2813ff40c12SJohn Marino 	}
2823ff40c12SJohn Marino 
2833ff40c12SJohn Marino 	if (peer == NULL) {
2843ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
2853ff40c12SJohn Marino 			   "retry " MACSTR, MAC2STR(dest));
2863ff40c12SJohn Marino 		return 0;
2873ff40c12SJohn Marino 	}
2883ff40c12SJohn Marino 
2893ff40c12SJohn Marino 	eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
2903ff40c12SJohn Marino 
2913ff40c12SJohn Marino 	if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
2923ff40c12SJohn Marino 		peer->sm_tmr.count = TPK_M2_RETRY_COUNT;
2933ff40c12SJohn Marino 		peer->sm_tmr.timer = TPK_M2_TIMEOUT;
2943ff40c12SJohn Marino 	} else {
2953ff40c12SJohn Marino 		peer->sm_tmr.count = TPK_M1_RETRY_COUNT;
2963ff40c12SJohn Marino 		peer->sm_tmr.timer = TPK_M1_TIMEOUT;
2973ff40c12SJohn Marino 	}
2983ff40c12SJohn Marino 
2993ff40c12SJohn Marino 	/* Copy message to resend on timeout */
3003ff40c12SJohn Marino 	os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN);
3013ff40c12SJohn Marino 	peer->sm_tmr.action_code = action_code;
3023ff40c12SJohn Marino 	peer->sm_tmr.dialog_token = dialog_token;
3033ff40c12SJohn Marino 	peer->sm_tmr.status_code = status_code;
304*a1157835SDaniel Fojt 	peer->sm_tmr.peer_capab = peer_capab;
3053ff40c12SJohn Marino 	peer->sm_tmr.buf_len = msg_len;
3063ff40c12SJohn Marino 	os_free(peer->sm_tmr.buf);
307*a1157835SDaniel Fojt 	peer->sm_tmr.buf = os_memdup(msg, msg_len);
3083ff40c12SJohn Marino 	if (peer->sm_tmr.buf == NULL)
3093ff40c12SJohn Marino 		return -1;
3103ff40c12SJohn Marino 
3113ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
3123ff40c12SJohn Marino 		   "(action_code=%u)", action_code);
3133ff40c12SJohn Marino 	eloop_register_timeout(peer->sm_tmr.timer / 1000,
3143ff40c12SJohn Marino 			       (peer->sm_tmr.timer % 1000) * 1000,
3153ff40c12SJohn Marino 			       wpa_tdls_tpk_retry_timeout, sm, peer);
3163ff40c12SJohn Marino 	return 0;
3173ff40c12SJohn Marino }
3183ff40c12SJohn Marino 
3193ff40c12SJohn Marino 
wpa_tdls_do_teardown(struct wpa_sm * sm,struct wpa_tdls_peer * peer,u16 reason_code)3203ff40c12SJohn Marino static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
3213ff40c12SJohn Marino 				u16 reason_code)
3223ff40c12SJohn Marino {
3233ff40c12SJohn Marino 	int ret;
3243ff40c12SJohn Marino 
3253ff40c12SJohn Marino 	ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code);
3263ff40c12SJohn Marino 	/* disable the link after teardown was sent */
3273ff40c12SJohn Marino 	wpa_tdls_disable_peer_link(sm, peer);
3283ff40c12SJohn Marino 
3293ff40c12SJohn Marino 	return ret;
3303ff40c12SJohn Marino }
3313ff40c12SJohn Marino 
3323ff40c12SJohn Marino 
wpa_tdls_tpk_retry_timeout(void * eloop_ctx,void * timeout_ctx)3333ff40c12SJohn Marino static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx)
3343ff40c12SJohn Marino {
3353ff40c12SJohn Marino 
3363ff40c12SJohn Marino 	struct wpa_sm *sm = eloop_ctx;
3373ff40c12SJohn Marino 	struct wpa_tdls_peer *peer = timeout_ctx;
3383ff40c12SJohn Marino 
3393ff40c12SJohn Marino 	if (peer->sm_tmr.count) {
3403ff40c12SJohn Marino 		peer->sm_tmr.count--;
3413ff40c12SJohn Marino 
3423ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Retrying sending of message "
3433ff40c12SJohn Marino 			   "(action_code=%u)",
3443ff40c12SJohn Marino 			   peer->sm_tmr.action_code);
3453ff40c12SJohn Marino 
3463ff40c12SJohn Marino 		if (peer->sm_tmr.buf == NULL) {
3473ff40c12SJohn Marino 			wpa_printf(MSG_INFO, "TDLS: No retry buffer available "
3483ff40c12SJohn Marino 				   "for action_code=%u",
3493ff40c12SJohn Marino 				   peer->sm_tmr.action_code);
3503ff40c12SJohn Marino 			eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm,
3513ff40c12SJohn Marino 					     peer);
3523ff40c12SJohn Marino 			return;
3533ff40c12SJohn Marino 		}
3543ff40c12SJohn Marino 
3553ff40c12SJohn Marino 		/* resend TPK Handshake Message to Peer */
3563ff40c12SJohn Marino 		if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest,
3573ff40c12SJohn Marino 					  peer->sm_tmr.action_code,
3583ff40c12SJohn Marino 					  peer->sm_tmr.dialog_token,
3593ff40c12SJohn Marino 					  peer->sm_tmr.status_code,
360*a1157835SDaniel Fojt 					  peer->sm_tmr.peer_capab,
361*a1157835SDaniel Fojt 					  peer->initiator,
3623ff40c12SJohn Marino 					  peer->sm_tmr.buf,
3633ff40c12SJohn Marino 					  peer->sm_tmr.buf_len)) {
3643ff40c12SJohn Marino 			wpa_printf(MSG_INFO, "TDLS: Failed to retry "
3653ff40c12SJohn Marino 				   "transmission");
3663ff40c12SJohn Marino 		}
3673ff40c12SJohn Marino 
3683ff40c12SJohn Marino 		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
3693ff40c12SJohn Marino 		eloop_register_timeout(peer->sm_tmr.timer / 1000,
3703ff40c12SJohn Marino 				       (peer->sm_tmr.timer % 1000) * 1000,
3713ff40c12SJohn Marino 				       wpa_tdls_tpk_retry_timeout, sm, peer);
3723ff40c12SJohn Marino 	} else {
3733ff40c12SJohn Marino 		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
3743ff40c12SJohn Marino 
3753ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request");
3763ff40c12SJohn Marino 		wpa_tdls_do_teardown(sm, peer,
3773ff40c12SJohn Marino 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
3783ff40c12SJohn Marino 	}
3793ff40c12SJohn Marino }
3803ff40c12SJohn Marino 
3813ff40c12SJohn Marino 
wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm * sm,struct wpa_tdls_peer * peer,u8 action_code)3823ff40c12SJohn Marino static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm,
3833ff40c12SJohn Marino 					      struct wpa_tdls_peer *peer,
3843ff40c12SJohn Marino 					      u8 action_code)
3853ff40c12SJohn Marino {
3863ff40c12SJohn Marino 	if (action_code == peer->sm_tmr.action_code) {
3873ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for "
3883ff40c12SJohn Marino 			   "action_code=%u", action_code);
3893ff40c12SJohn Marino 
3903ff40c12SJohn Marino 		/* Cancel Timeout registered */
3913ff40c12SJohn Marino 		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
3923ff40c12SJohn Marino 
3933ff40c12SJohn Marino 		/* free all resources meant for retry */
3943ff40c12SJohn Marino 		os_free(peer->sm_tmr.buf);
3953ff40c12SJohn Marino 		peer->sm_tmr.buf = NULL;
3963ff40c12SJohn Marino 
3973ff40c12SJohn Marino 		peer->sm_tmr.count = 0;
3983ff40c12SJohn Marino 		peer->sm_tmr.timer = 0;
3993ff40c12SJohn Marino 		peer->sm_tmr.buf_len = 0;
4003ff40c12SJohn Marino 		peer->sm_tmr.action_code = 0xff;
4013ff40c12SJohn Marino 	} else {
4023ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout "
4033ff40c12SJohn Marino 			   "(Unknown action_code=%u)", action_code);
4043ff40c12SJohn Marino 	}
4053ff40c12SJohn Marino }
4063ff40c12SJohn Marino 
4073ff40c12SJohn Marino 
wpa_tdls_generate_tpk(struct wpa_tdls_peer * peer,const u8 * own_addr,const u8 * bssid)4083ff40c12SJohn Marino static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer,
4093ff40c12SJohn Marino 				  const u8 *own_addr, const u8 *bssid)
4103ff40c12SJohn Marino {
4113ff40c12SJohn Marino 	u8 key_input[SHA256_MAC_LEN];
4123ff40c12SJohn Marino 	const u8 *nonce[2];
4133ff40c12SJohn Marino 	size_t len[2];
4143ff40c12SJohn Marino 	u8 data[3 * ETH_ALEN];
4153ff40c12SJohn Marino 
416*a1157835SDaniel Fojt 	/* IEEE Std 802.11-2016 12.7.9.2:
417*a1157835SDaniel Fojt 	 * TPK-Key-Input = Hash(min(SNonce, ANonce) || max(SNonce, ANonce))
418*a1157835SDaniel Fojt 	 * Hash = SHA-256 for TDLS
4193ff40c12SJohn Marino 	 */
4203ff40c12SJohn Marino 	len[0] = WPA_NONCE_LEN;
4213ff40c12SJohn Marino 	len[1] = WPA_NONCE_LEN;
4223ff40c12SJohn Marino 	if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) {
4233ff40c12SJohn Marino 		nonce[0] = peer->inonce;
4243ff40c12SJohn Marino 		nonce[1] = peer->rnonce;
4253ff40c12SJohn Marino 	} else {
4263ff40c12SJohn Marino 		nonce[0] = peer->rnonce;
4273ff40c12SJohn Marino 		nonce[1] = peer->inonce;
4283ff40c12SJohn Marino 	}
4293ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN);
4303ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN);
4313ff40c12SJohn Marino 	sha256_vector(2, nonce, len, key_input);
4323ff40c12SJohn Marino 	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
4333ff40c12SJohn Marino 			key_input, SHA256_MAC_LEN);
4343ff40c12SJohn Marino 
4353ff40c12SJohn Marino 	/*
436*a1157835SDaniel Fojt 	 * TPK = KDF-Hash-Length(TPK-Key-Input, "TDLS PMK",
437*a1157835SDaniel Fojt 	 *	min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID)
4383ff40c12SJohn Marino 	 */
4393ff40c12SJohn Marino 
4403ff40c12SJohn Marino 	if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
4413ff40c12SJohn Marino 		os_memcpy(data, own_addr, ETH_ALEN);
4423ff40c12SJohn Marino 		os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN);
4433ff40c12SJohn Marino 	} else {
4443ff40c12SJohn Marino 		os_memcpy(data, peer->addr, ETH_ALEN);
4453ff40c12SJohn Marino 		os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN);
4463ff40c12SJohn Marino 	}
4473ff40c12SJohn Marino 	os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
4483ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
4493ff40c12SJohn Marino 
4503ff40c12SJohn Marino 	sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
4513ff40c12SJohn Marino 		   (u8 *) &peer->tpk, sizeof(peer->tpk));
4523ff40c12SJohn Marino 	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
4533ff40c12SJohn Marino 			peer->tpk.kck, sizeof(peer->tpk.kck));
4543ff40c12SJohn Marino 	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
4553ff40c12SJohn Marino 			peer->tpk.tk, sizeof(peer->tpk.tk));
4563ff40c12SJohn Marino 	peer->tpk_set = 1;
4573ff40c12SJohn Marino }
4583ff40c12SJohn Marino 
4593ff40c12SJohn Marino 
4603ff40c12SJohn Marino /**
4613ff40c12SJohn Marino  * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
4623ff40c12SJohn Marino  * @kck: TPK-KCK
4633ff40c12SJohn Marino  * @lnkid: Pointer to the beginning of Link Identifier IE
4643ff40c12SJohn Marino  * @rsnie: Pointer to the beginning of RSN IE used for handshake
4653ff40c12SJohn Marino  * @timeoutie: Pointer to the beginning of Timeout IE used for handshake
4663ff40c12SJohn Marino  * @ftie: Pointer to the beginning of FT IE
4673ff40c12SJohn Marino  * @mic: Pointer for writing MIC
4683ff40c12SJohn Marino  *
4693ff40c12SJohn Marino  * Calculate MIC for TDLS frame.
4703ff40c12SJohn Marino  */
wpa_tdls_ftie_mic(const u8 * kck,u8 trans_seq,const u8 * lnkid,const u8 * rsnie,const u8 * timeoutie,const u8 * ftie,u8 * mic)4713ff40c12SJohn Marino static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
4723ff40c12SJohn Marino 			     const u8 *rsnie, const u8 *timeoutie,
4733ff40c12SJohn Marino 			     const u8 *ftie, u8 *mic)
4743ff40c12SJohn Marino {
4753ff40c12SJohn Marino 	u8 *buf, *pos;
4763ff40c12SJohn Marino 	struct wpa_tdls_ftie *_ftie;
4773ff40c12SJohn Marino 	const struct wpa_tdls_lnkid *_lnkid;
4783ff40c12SJohn Marino 	int ret;
4793ff40c12SJohn Marino 	int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] +
4803ff40c12SJohn Marino 		2 + timeoutie[1] + 2 + ftie[1];
4813ff40c12SJohn Marino 	buf = os_zalloc(len);
4823ff40c12SJohn Marino 	if (!buf) {
4833ff40c12SJohn Marino 		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
4843ff40c12SJohn Marino 		return -1;
4853ff40c12SJohn Marino 	}
4863ff40c12SJohn Marino 
4873ff40c12SJohn Marino 	pos = buf;
4883ff40c12SJohn Marino 	_lnkid = (const struct wpa_tdls_lnkid *) lnkid;
4893ff40c12SJohn Marino 	/* 1) TDLS initiator STA MAC address */
4903ff40c12SJohn Marino 	os_memcpy(pos, _lnkid->init_sta, ETH_ALEN);
4913ff40c12SJohn Marino 	pos += ETH_ALEN;
4923ff40c12SJohn Marino 	/* 2) TDLS responder STA MAC address */
4933ff40c12SJohn Marino 	os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN);
4943ff40c12SJohn Marino 	pos += ETH_ALEN;
4953ff40c12SJohn Marino 	/* 3) Transaction Sequence number */
4963ff40c12SJohn Marino 	*pos++ = trans_seq;
4973ff40c12SJohn Marino 	/* 4) Link Identifier IE */
4983ff40c12SJohn Marino 	os_memcpy(pos, lnkid, 2 + lnkid[1]);
4993ff40c12SJohn Marino 	pos += 2 + lnkid[1];
5003ff40c12SJohn Marino 	/* 5) RSN IE */
5013ff40c12SJohn Marino 	os_memcpy(pos, rsnie, 2 + rsnie[1]);
5023ff40c12SJohn Marino 	pos += 2 + rsnie[1];
5033ff40c12SJohn Marino 	/* 6) Timeout Interval IE */
5043ff40c12SJohn Marino 	os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
5053ff40c12SJohn Marino 	pos += 2 + timeoutie[1];
5063ff40c12SJohn Marino 	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
5073ff40c12SJohn Marino 	os_memcpy(pos, ftie, 2 + ftie[1]);
5083ff40c12SJohn Marino 	_ftie = (struct wpa_tdls_ftie *) pos;
5093ff40c12SJohn Marino 	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
5103ff40c12SJohn Marino 	pos += 2 + ftie[1];
5113ff40c12SJohn Marino 
5123ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
5133ff40c12SJohn Marino 	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
5143ff40c12SJohn Marino 	ret = omac1_aes_128(kck, buf, pos - buf, mic);
5153ff40c12SJohn Marino 	os_free(buf);
5163ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
5173ff40c12SJohn Marino 	return ret;
5183ff40c12SJohn Marino }
5193ff40c12SJohn Marino 
5203ff40c12SJohn Marino 
5213ff40c12SJohn Marino /**
5223ff40c12SJohn Marino  * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame
5233ff40c12SJohn Marino  * @kck: TPK-KCK
5243ff40c12SJohn Marino  * @trans_seq: Transaction Sequence Number (4 - Teardown)
5253ff40c12SJohn Marino  * @rcode: Reason code for Teardown
5263ff40c12SJohn Marino  * @dtoken: Dialog Token used for that particular link
5273ff40c12SJohn Marino  * @lnkid: Pointer to the beginning of Link Identifier IE
5283ff40c12SJohn Marino  * @ftie: Pointer to the beginning of FT IE
5293ff40c12SJohn Marino  * @mic: Pointer for writing MIC
5303ff40c12SJohn Marino  *
5313ff40c12SJohn Marino  * Calculate MIC for TDLS frame.
5323ff40c12SJohn Marino  */
wpa_tdls_key_mic_teardown(const u8 * kck,u8 trans_seq,u16 rcode,u8 dtoken,const u8 * lnkid,const u8 * ftie,u8 * mic)5333ff40c12SJohn Marino static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
5343ff40c12SJohn Marino 				     u8 dtoken, const u8 *lnkid,
5353ff40c12SJohn Marino 				     const u8 *ftie, u8 *mic)
5363ff40c12SJohn Marino {
5373ff40c12SJohn Marino 	u8 *buf, *pos;
5383ff40c12SJohn Marino 	struct wpa_tdls_ftie *_ftie;
5393ff40c12SJohn Marino 	int ret;
5403ff40c12SJohn Marino 	int len;
5413ff40c12SJohn Marino 
5423ff40c12SJohn Marino 	if (lnkid == NULL)
5433ff40c12SJohn Marino 		return -1;
5443ff40c12SJohn Marino 
5453ff40c12SJohn Marino 	len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
5463ff40c12SJohn Marino 		sizeof(trans_seq) + 2 + ftie[1];
5473ff40c12SJohn Marino 
5483ff40c12SJohn Marino 	buf = os_zalloc(len);
5493ff40c12SJohn Marino 	if (!buf) {
5503ff40c12SJohn Marino 		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
5513ff40c12SJohn Marino 		return -1;
5523ff40c12SJohn Marino 	}
5533ff40c12SJohn Marino 
5543ff40c12SJohn Marino 	pos = buf;
5553ff40c12SJohn Marino 	/* 1) Link Identifier IE */
5563ff40c12SJohn Marino 	os_memcpy(pos, lnkid, 2 + lnkid[1]);
5573ff40c12SJohn Marino 	pos += 2 + lnkid[1];
5583ff40c12SJohn Marino 	/* 2) Reason Code */
5593ff40c12SJohn Marino 	WPA_PUT_LE16(pos, rcode);
5603ff40c12SJohn Marino 	pos += sizeof(rcode);
5613ff40c12SJohn Marino 	/* 3) Dialog token */
5623ff40c12SJohn Marino 	*pos++ = dtoken;
5633ff40c12SJohn Marino 	/* 4) Transaction Sequence number */
5643ff40c12SJohn Marino 	*pos++ = trans_seq;
5653ff40c12SJohn Marino 	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
5663ff40c12SJohn Marino 	os_memcpy(pos, ftie, 2 + ftie[1]);
5673ff40c12SJohn Marino 	_ftie = (struct wpa_tdls_ftie *) pos;
5683ff40c12SJohn Marino 	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
5693ff40c12SJohn Marino 	pos += 2 + ftie[1];
5703ff40c12SJohn Marino 
5713ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
5723ff40c12SJohn Marino 	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
5733ff40c12SJohn Marino 	ret = omac1_aes_128(kck, buf, pos - buf, mic);
5743ff40c12SJohn Marino 	os_free(buf);
5753ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
5763ff40c12SJohn Marino 	return ret;
5773ff40c12SJohn Marino }
5783ff40c12SJohn Marino 
5793ff40c12SJohn Marino 
wpa_supplicant_verify_tdls_mic(u8 trans_seq,struct wpa_tdls_peer * peer,const u8 * lnkid,const u8 * timeoutie,const struct wpa_tdls_ftie * ftie)5803ff40c12SJohn Marino static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
5813ff40c12SJohn Marino 					  struct wpa_tdls_peer *peer,
5823ff40c12SJohn Marino 					  const u8 *lnkid, const u8 *timeoutie,
5833ff40c12SJohn Marino 					  const struct wpa_tdls_ftie *ftie)
5843ff40c12SJohn Marino {
5853ff40c12SJohn Marino 	u8 mic[16];
5863ff40c12SJohn Marino 
5873ff40c12SJohn Marino 	if (peer->tpk_set) {
5883ff40c12SJohn Marino 		wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
5893ff40c12SJohn Marino 				  peer->rsnie_p, timeoutie, (u8 *) ftie,
5903ff40c12SJohn Marino 				  mic);
591*a1157835SDaniel Fojt 		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
5923ff40c12SJohn Marino 			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
5933ff40c12SJohn Marino 				   "dropping packet");
5943ff40c12SJohn Marino 			wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC",
5953ff40c12SJohn Marino 				    ftie->mic, 16);
5963ff40c12SJohn Marino 			wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC",
5973ff40c12SJohn Marino 				    mic, 16);
5983ff40c12SJohn Marino 			return -1;
5993ff40c12SJohn Marino 		}
6003ff40c12SJohn Marino 	} else {
6013ff40c12SJohn Marino 		wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, "
6023ff40c12SJohn Marino 			   "TPK not set - dropping packet");
6033ff40c12SJohn Marino 		return -1;
6043ff40c12SJohn Marino 	}
6053ff40c12SJohn Marino 	return 0;
6063ff40c12SJohn Marino }
6073ff40c12SJohn Marino 
6083ff40c12SJohn Marino 
wpa_supplicant_verify_tdls_mic_teardown(u8 trans_seq,u16 rcode,u8 dtoken,struct wpa_tdls_peer * peer,const u8 * lnkid,const struct wpa_tdls_ftie * ftie)6093ff40c12SJohn Marino static int wpa_supplicant_verify_tdls_mic_teardown(
6103ff40c12SJohn Marino 	u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
6113ff40c12SJohn Marino 	const u8 *lnkid, const struct wpa_tdls_ftie *ftie)
6123ff40c12SJohn Marino {
6133ff40c12SJohn Marino 	u8 mic[16];
6143ff40c12SJohn Marino 
6153ff40c12SJohn Marino 	if (peer->tpk_set) {
6163ff40c12SJohn Marino 		wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
6173ff40c12SJohn Marino 					  dtoken, lnkid, (u8 *) ftie, mic);
618*a1157835SDaniel Fojt 		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
6193ff40c12SJohn Marino 			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
6203ff40c12SJohn Marino 				   "dropping packet");
6213ff40c12SJohn Marino 			return -1;
6223ff40c12SJohn Marino 		}
6233ff40c12SJohn Marino 	} else {
6243ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown "
6253ff40c12SJohn Marino 			   "MIC, TPK not set - dropping packet");
6263ff40c12SJohn Marino 		return -1;
6273ff40c12SJohn Marino 	}
6283ff40c12SJohn Marino 	return 0;
6293ff40c12SJohn Marino }
6303ff40c12SJohn Marino 
6313ff40c12SJohn Marino 
wpa_tdls_tpk_timeout(void * eloop_ctx,void * timeout_ctx)6323ff40c12SJohn Marino static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx)
6333ff40c12SJohn Marino {
6343ff40c12SJohn Marino 	struct wpa_sm *sm = eloop_ctx;
6353ff40c12SJohn Marino 	struct wpa_tdls_peer *peer = timeout_ctx;
6363ff40c12SJohn Marino 
6373ff40c12SJohn Marino 	/*
6383ff40c12SJohn Marino 	 * On TPK lifetime expiration, we have an option of either tearing down
6393ff40c12SJohn Marino 	 * the direct link or trying to re-initiate it. The selection of what
6403ff40c12SJohn Marino 	 * to do is not strictly speaking controlled by our role in the expired
6413ff40c12SJohn Marino 	 * link, but for now, use that to select whether to renew or tear down
6423ff40c12SJohn Marino 	 * the link.
6433ff40c12SJohn Marino 	 */
6443ff40c12SJohn Marino 
6453ff40c12SJohn Marino 	if (peer->initiator) {
646*a1157835SDaniel Fojt 		u8 addr[ETH_ALEN];
647*a1157835SDaniel Fojt 
6483ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
6493ff40c12SJohn Marino 			   " - try to renew", MAC2STR(peer->addr));
650*a1157835SDaniel Fojt 		/* cache the peer address before do_teardown */
651*a1157835SDaniel Fojt 		os_memcpy(addr, peer->addr, ETH_ALEN);
652*a1157835SDaniel Fojt 		wpa_tdls_do_teardown(sm, peer,
653*a1157835SDaniel Fojt 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
654*a1157835SDaniel Fojt 		wpa_tdls_start(sm, addr);
6553ff40c12SJohn Marino 	} else {
6563ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
6573ff40c12SJohn Marino 			   " - tear down", MAC2STR(peer->addr));
6583ff40c12SJohn Marino 		wpa_tdls_do_teardown(sm, peer,
6593ff40c12SJohn Marino 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
6603ff40c12SJohn Marino 	}
6613ff40c12SJohn Marino }
6623ff40c12SJohn Marino 
6633ff40c12SJohn Marino 
wpa_tdls_peer_remove_from_list(struct wpa_sm * sm,struct wpa_tdls_peer * peer)664*a1157835SDaniel Fojt static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm,
665*a1157835SDaniel Fojt 					   struct wpa_tdls_peer *peer)
666*a1157835SDaniel Fojt {
667*a1157835SDaniel Fojt 	struct wpa_tdls_peer *cur, *prev;
668*a1157835SDaniel Fojt 
669*a1157835SDaniel Fojt 	cur = sm->tdls;
670*a1157835SDaniel Fojt 	prev = NULL;
671*a1157835SDaniel Fojt 	while (cur && cur != peer) {
672*a1157835SDaniel Fojt 		prev = cur;
673*a1157835SDaniel Fojt 		cur = cur->next;
674*a1157835SDaniel Fojt 	}
675*a1157835SDaniel Fojt 
676*a1157835SDaniel Fojt 	if (cur != peer) {
677*a1157835SDaniel Fojt 		wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR
678*a1157835SDaniel Fojt 			   " to remove it from the list",
679*a1157835SDaniel Fojt 			   MAC2STR(peer->addr));
680*a1157835SDaniel Fojt 		return;
681*a1157835SDaniel Fojt 	}
682*a1157835SDaniel Fojt 
683*a1157835SDaniel Fojt 	if (prev)
684*a1157835SDaniel Fojt 		prev->next = peer->next;
685*a1157835SDaniel Fojt 	else
686*a1157835SDaniel Fojt 		sm->tdls = peer->next;
687*a1157835SDaniel Fojt }
688*a1157835SDaniel Fojt 
689*a1157835SDaniel Fojt 
wpa_tdls_peer_clear(struct wpa_sm * sm,struct wpa_tdls_peer * peer)690*a1157835SDaniel Fojt static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
6913ff40c12SJohn Marino {
6923ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
6933ff40c12SJohn Marino 		   MAC2STR(peer->addr));
6943ff40c12SJohn Marino 	eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
6953ff40c12SJohn Marino 	eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
6963ff40c12SJohn Marino 	peer->reconfig_key = 0;
6973ff40c12SJohn Marino 	peer->initiator = 0;
6983ff40c12SJohn Marino 	peer->tpk_in_progress = 0;
6993ff40c12SJohn Marino 	os_free(peer->sm_tmr.buf);
7003ff40c12SJohn Marino 	peer->sm_tmr.buf = NULL;
7013ff40c12SJohn Marino 	os_free(peer->ht_capabilities);
7023ff40c12SJohn Marino 	peer->ht_capabilities = NULL;
7033ff40c12SJohn Marino 	os_free(peer->vht_capabilities);
7043ff40c12SJohn Marino 	peer->vht_capabilities = NULL;
7053ff40c12SJohn Marino 	os_free(peer->ext_capab);
7063ff40c12SJohn Marino 	peer->ext_capab = NULL;
7073ff40c12SJohn Marino 	os_free(peer->supp_channels);
7083ff40c12SJohn Marino 	peer->supp_channels = NULL;
7093ff40c12SJohn Marino 	os_free(peer->supp_oper_classes);
7103ff40c12SJohn Marino 	peer->supp_oper_classes = NULL;
7113ff40c12SJohn Marino 	peer->rsnie_i_len = peer->rsnie_p_len = 0;
7123ff40c12SJohn Marino 	peer->cipher = 0;
713*a1157835SDaniel Fojt 	peer->qos_info = 0;
714*a1157835SDaniel Fojt 	peer->wmm_capable = 0;
715*a1157835SDaniel Fojt 	peer->tk_set = peer->tpk_set = peer->tpk_success = 0;
716*a1157835SDaniel Fojt 	peer->chan_switch_enabled = 0;
7173ff40c12SJohn Marino 	os_memset(&peer->tpk, 0, sizeof(peer->tpk));
7183ff40c12SJohn Marino 	os_memset(peer->inonce, 0, WPA_NONCE_LEN);
7193ff40c12SJohn Marino 	os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
7203ff40c12SJohn Marino }
7213ff40c12SJohn Marino 
7223ff40c12SJohn Marino 
wpa_tdls_peer_free(struct wpa_sm * sm,struct wpa_tdls_peer * peer)723*a1157835SDaniel Fojt static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
724*a1157835SDaniel Fojt {
725*a1157835SDaniel Fojt 	wpa_tdls_peer_clear(sm, peer);
726*a1157835SDaniel Fojt 	wpa_tdls_peer_remove_from_list(sm, peer);
727*a1157835SDaniel Fojt 	os_free(peer);
728*a1157835SDaniel Fojt }
729*a1157835SDaniel Fojt 
730*a1157835SDaniel Fojt 
wpa_tdls_linkid(struct wpa_sm * sm,struct wpa_tdls_peer * peer,struct wpa_tdls_lnkid * lnkid)7313ff40c12SJohn Marino static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
7323ff40c12SJohn Marino 			    struct wpa_tdls_lnkid *lnkid)
7333ff40c12SJohn Marino {
7343ff40c12SJohn Marino 	lnkid->ie_type = WLAN_EID_LINK_ID;
7353ff40c12SJohn Marino 	lnkid->ie_len = 3 * ETH_ALEN;
7363ff40c12SJohn Marino 	os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN);
7373ff40c12SJohn Marino 	if (peer->initiator) {
7383ff40c12SJohn Marino 		os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN);
7393ff40c12SJohn Marino 		os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN);
7403ff40c12SJohn Marino 	} else {
7413ff40c12SJohn Marino 		os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN);
7423ff40c12SJohn Marino 		os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN);
7433ff40c12SJohn Marino 	}
7443ff40c12SJohn Marino }
7453ff40c12SJohn Marino 
7463ff40c12SJohn Marino 
wpa_tdls_send_teardown(struct wpa_sm * sm,const u8 * addr,u16 reason_code)747*a1157835SDaniel Fojt static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
748*a1157835SDaniel Fojt 				  u16 reason_code)
7493ff40c12SJohn Marino {
7503ff40c12SJohn Marino 	struct wpa_tdls_peer *peer;
7513ff40c12SJohn Marino 	struct wpa_tdls_ftie *ftie;
7523ff40c12SJohn Marino 	struct wpa_tdls_lnkid lnkid;
7533ff40c12SJohn Marino 	u8 dialog_token;
7543ff40c12SJohn Marino 	u8 *rbuf, *pos;
7553ff40c12SJohn Marino 	int ielen;
7563ff40c12SJohn Marino 
7573ff40c12SJohn Marino 	if (sm->tdls_disabled || !sm->tdls_supported)
7583ff40c12SJohn Marino 		return -1;
7593ff40c12SJohn Marino 
7603ff40c12SJohn Marino 	/* Find the node and free from the list */
7613ff40c12SJohn Marino 	for (peer = sm->tdls; peer; peer = peer->next) {
7623ff40c12SJohn Marino 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
7633ff40c12SJohn Marino 			break;
7643ff40c12SJohn Marino 	}
7653ff40c12SJohn Marino 
7663ff40c12SJohn Marino 	if (peer == NULL) {
7673ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
7683ff40c12SJohn Marino 			   "Teardown " MACSTR, MAC2STR(addr));
7693ff40c12SJohn Marino 		return 0;
7703ff40c12SJohn Marino 	}
7713ff40c12SJohn Marino 
772*a1157835SDaniel Fojt 	/* Cancel active channel switch before teardown */
773*a1157835SDaniel Fojt 	if (peer->chan_switch_enabled) {
774*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR
775*a1157835SDaniel Fojt 			   " to base channel", MAC2STR(addr));
776*a1157835SDaniel Fojt 		wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
777*a1157835SDaniel Fojt 	}
778*a1157835SDaniel Fojt 
7793ff40c12SJohn Marino 	dialog_token = peer->dtoken;
7803ff40c12SJohn Marino 
7813ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR,
7823ff40c12SJohn Marino 		   MAC2STR(addr));
7833ff40c12SJohn Marino 
7843ff40c12SJohn Marino 	ielen = 0;
7853ff40c12SJohn Marino 	if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) {
7863ff40c12SJohn Marino 		/* To add FTIE for Teardown request and compute MIC */
7873ff40c12SJohn Marino 		ielen += sizeof(*ftie);
7883ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
7893ff40c12SJohn Marino 		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
7903ff40c12SJohn Marino 			ielen += 170;
7913ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
7923ff40c12SJohn Marino 	}
7933ff40c12SJohn Marino 
7943ff40c12SJohn Marino 	rbuf = os_zalloc(ielen + 1);
7953ff40c12SJohn Marino 	if (rbuf == NULL)
7963ff40c12SJohn Marino 		return -1;
7973ff40c12SJohn Marino 	pos = rbuf;
7983ff40c12SJohn Marino 
7993ff40c12SJohn Marino 	if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
8003ff40c12SJohn Marino 		goto skip_ies;
8013ff40c12SJohn Marino 
8023ff40c12SJohn Marino 	ftie = (struct wpa_tdls_ftie *) pos;
8033ff40c12SJohn Marino 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
8043ff40c12SJohn Marino 	/* Using the recent nonce which should be for CONFIRM frame */
8053ff40c12SJohn Marino 	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
8063ff40c12SJohn Marino 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
8073ff40c12SJohn Marino 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
8083ff40c12SJohn Marino 	pos = (u8 *) (ftie + 1);
8093ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
8103ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
8113ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
8123ff40c12SJohn Marino 			   "FTIE");
8133ff40c12SJohn Marino 		ftie->ie_len += 170;
8143ff40c12SJohn Marino 		*pos++ = 255; /* FTIE subelem */
8153ff40c12SJohn Marino 		*pos++ = 168; /* FTIE subelem length */
8163ff40c12SJohn Marino 		pos += 168;
8173ff40c12SJohn Marino 	}
8183ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
8193ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake",
8203ff40c12SJohn Marino 		    (u8 *) ftie, pos - (u8 *) ftie);
8213ff40c12SJohn Marino 
8223ff40c12SJohn Marino 	/* compute MIC before sending */
8233ff40c12SJohn Marino 	wpa_tdls_linkid(sm, peer, &lnkid);
8243ff40c12SJohn Marino 	wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
8253ff40c12SJohn Marino 				  dialog_token, (u8 *) &lnkid, (u8 *) ftie,
8263ff40c12SJohn Marino 				  ftie->mic);
8273ff40c12SJohn Marino 
8283ff40c12SJohn Marino skip_ies:
8293ff40c12SJohn Marino 	/* TODO: register for a Timeout handler, if Teardown is not received at
8303ff40c12SJohn Marino 	 * the other end, then try again another time */
8313ff40c12SJohn Marino 
8323ff40c12SJohn Marino 	/* request driver to send Teardown using this FTIE */
8333ff40c12SJohn Marino 	wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
834*a1157835SDaniel Fojt 			  reason_code, 0, peer->initiator, rbuf, pos - rbuf);
8353ff40c12SJohn Marino 	os_free(rbuf);
8363ff40c12SJohn Marino 
8373ff40c12SJohn Marino 	return 0;
8383ff40c12SJohn Marino }
8393ff40c12SJohn Marino 
8403ff40c12SJohn Marino 
wpa_tdls_teardown_link(struct wpa_sm * sm,const u8 * addr,u16 reason_code)8413ff40c12SJohn Marino int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
8423ff40c12SJohn Marino {
8433ff40c12SJohn Marino 	struct wpa_tdls_peer *peer;
8443ff40c12SJohn Marino 
8453ff40c12SJohn Marino 	if (sm->tdls_disabled || !sm->tdls_supported)
8463ff40c12SJohn Marino 		return -1;
8473ff40c12SJohn Marino 
8483ff40c12SJohn Marino 	for (peer = sm->tdls; peer; peer = peer->next) {
8493ff40c12SJohn Marino 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
8503ff40c12SJohn Marino 			break;
8513ff40c12SJohn Marino 	}
8523ff40c12SJohn Marino 
8533ff40c12SJohn Marino 	if (peer == NULL) {
8543ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR
8553ff40c12SJohn Marino 		   " for link Teardown", MAC2STR(addr));
8563ff40c12SJohn Marino 		return -1;
8573ff40c12SJohn Marino 	}
8583ff40c12SJohn Marino 
8593ff40c12SJohn Marino 	if (!peer->tpk_success) {
8603ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
8613ff40c12SJohn Marino 		   " not connected - cannot Teardown link", MAC2STR(addr));
8623ff40c12SJohn Marino 		return -1;
8633ff40c12SJohn Marino 	}
8643ff40c12SJohn Marino 
8653ff40c12SJohn Marino 	return wpa_tdls_do_teardown(sm, peer, reason_code);
8663ff40c12SJohn Marino }
8673ff40c12SJohn Marino 
8683ff40c12SJohn Marino 
wpa_tdls_disable_peer_link(struct wpa_sm * sm,struct wpa_tdls_peer * peer)8693ff40c12SJohn Marino static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
8703ff40c12SJohn Marino 				       struct wpa_tdls_peer *peer)
8713ff40c12SJohn Marino {
8723ff40c12SJohn Marino 	wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
8733ff40c12SJohn Marino 	wpa_tdls_peer_free(sm, peer);
8743ff40c12SJohn Marino }
8753ff40c12SJohn Marino 
8763ff40c12SJohn Marino 
wpa_tdls_disable_unreachable_link(struct wpa_sm * sm,const u8 * addr)877*a1157835SDaniel Fojt void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr)
8783ff40c12SJohn Marino {
8793ff40c12SJohn Marino 	struct wpa_tdls_peer *peer;
8803ff40c12SJohn Marino 
8813ff40c12SJohn Marino 	for (peer = sm->tdls; peer; peer = peer->next) {
8823ff40c12SJohn Marino 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
8833ff40c12SJohn Marino 			break;
8843ff40c12SJohn Marino 	}
8853ff40c12SJohn Marino 
886*a1157835SDaniel Fojt 	if (!peer || !peer->tpk_success) {
887*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
888*a1157835SDaniel Fojt 			   " not connected - cannot teardown unreachable link",
889*a1157835SDaniel Fojt 			   MAC2STR(addr));
890*a1157835SDaniel Fojt 		return;
891*a1157835SDaniel Fojt 	}
892*a1157835SDaniel Fojt 
893*a1157835SDaniel Fojt 	if (wpa_tdls_is_external_setup(sm)) {
894*a1157835SDaniel Fojt 		/*
895*a1157835SDaniel Fojt 		 * Get us on the base channel, disable the link, send a
896*a1157835SDaniel Fojt 		 * teardown packet through the AP, and then reset link data.
897*a1157835SDaniel Fojt 		 */
898*a1157835SDaniel Fojt 		if (peer->chan_switch_enabled)
899*a1157835SDaniel Fojt 			wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
900*a1157835SDaniel Fojt 		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
901*a1157835SDaniel Fojt 		wpa_tdls_send_teardown(sm, addr,
902*a1157835SDaniel Fojt 				       WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE);
903*a1157835SDaniel Fojt 		wpa_tdls_peer_free(sm, peer);
904*a1157835SDaniel Fojt 	} else {
9053ff40c12SJohn Marino 		wpa_tdls_disable_peer_link(sm, peer);
9063ff40c12SJohn Marino 	}
907*a1157835SDaniel Fojt }
9083ff40c12SJohn Marino 
9093ff40c12SJohn Marino 
wpa_tdls_get_link_status(struct wpa_sm * sm,const u8 * addr)9103ff40c12SJohn Marino const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr)
9113ff40c12SJohn Marino {
9123ff40c12SJohn Marino 	struct wpa_tdls_peer *peer;
9133ff40c12SJohn Marino 
9143ff40c12SJohn Marino 	if (sm->tdls_disabled || !sm->tdls_supported)
9153ff40c12SJohn Marino 		return "disabled";
9163ff40c12SJohn Marino 
9173ff40c12SJohn Marino 	for (peer = sm->tdls; peer; peer = peer->next) {
9183ff40c12SJohn Marino 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
9193ff40c12SJohn Marino 			break;
9203ff40c12SJohn Marino 	}
9213ff40c12SJohn Marino 
9223ff40c12SJohn Marino 	if (peer == NULL)
9233ff40c12SJohn Marino 		return "peer does not exist";
9243ff40c12SJohn Marino 
9253ff40c12SJohn Marino 	if (!peer->tpk_success)
9263ff40c12SJohn Marino 		return "peer not connected";
9273ff40c12SJohn Marino 
9283ff40c12SJohn Marino 	return "connected";
9293ff40c12SJohn Marino }
9303ff40c12SJohn Marino 
9313ff40c12SJohn Marino 
wpa_tdls_recv_teardown(struct wpa_sm * sm,const u8 * src_addr,const u8 * buf,size_t len)9323ff40c12SJohn Marino static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr,
9333ff40c12SJohn Marino 				  const u8 *buf, size_t len)
9343ff40c12SJohn Marino {
9353ff40c12SJohn Marino 	struct wpa_tdls_peer *peer = NULL;
9363ff40c12SJohn Marino 	struct wpa_tdls_ftie *ftie;
9373ff40c12SJohn Marino 	struct wpa_tdls_lnkid *lnkid;
9383ff40c12SJohn Marino 	struct wpa_eapol_ie_parse kde;
9393ff40c12SJohn Marino 	u16 reason_code;
9403ff40c12SJohn Marino 	const u8 *pos;
9413ff40c12SJohn Marino 	int ielen;
9423ff40c12SJohn Marino 
9433ff40c12SJohn Marino 	/* Find the node and free from the list */
9443ff40c12SJohn Marino 	for (peer = sm->tdls; peer; peer = peer->next) {
9453ff40c12SJohn Marino 		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
9463ff40c12SJohn Marino 			break;
9473ff40c12SJohn Marino 	}
9483ff40c12SJohn Marino 
9493ff40c12SJohn Marino 	if (peer == NULL) {
9503ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
9513ff40c12SJohn Marino 			   "Teardown " MACSTR, MAC2STR(src_addr));
9523ff40c12SJohn Marino 		return 0;
9533ff40c12SJohn Marino 	}
9543ff40c12SJohn Marino 
9553ff40c12SJohn Marino 	pos = buf;
9563ff40c12SJohn Marino 	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
9573ff40c12SJohn Marino 
9583ff40c12SJohn Marino 	reason_code = WPA_GET_LE16(pos);
9593ff40c12SJohn Marino 	pos += 2;
9603ff40c12SJohn Marino 
9613ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR
9623ff40c12SJohn Marino 		   " (reason code %u)", MAC2STR(src_addr), reason_code);
9633ff40c12SJohn Marino 
9643ff40c12SJohn Marino 	ielen = len - (pos - buf); /* start of IE in buf */
965*a1157835SDaniel Fojt 
966*a1157835SDaniel Fojt 	/*
967*a1157835SDaniel Fojt 	 * Don't reject the message if failing to parse IEs. The IEs we need are
968*a1157835SDaniel Fojt 	 * explicitly checked below. Some APs may add arbitrary padding to the
969*a1157835SDaniel Fojt 	 * end of short TDLS frames and that would look like invalid IEs.
970*a1157835SDaniel Fojt 	 */
971*a1157835SDaniel Fojt 	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0)
972*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
973*a1157835SDaniel Fojt 			   "TDLS: Failed to parse IEs in Teardown - ignore as an interop workaround");
9743ff40c12SJohn Marino 
9753ff40c12SJohn Marino 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
9763ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS "
9773ff40c12SJohn Marino 			   "Teardown");
9783ff40c12SJohn Marino 		return -1;
9793ff40c12SJohn Marino 	}
9803ff40c12SJohn Marino 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
9813ff40c12SJohn Marino 
9823ff40c12SJohn Marino 	if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
9833ff40c12SJohn Marino 		goto skip_ftie;
9843ff40c12SJohn Marino 
9853ff40c12SJohn Marino 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
9863ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown");
9873ff40c12SJohn Marino 		return -1;
9883ff40c12SJohn Marino 	}
9893ff40c12SJohn Marino 
9903ff40c12SJohn Marino 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
9913ff40c12SJohn Marino 
9923ff40c12SJohn Marino 	/* Process MIC check to see if TDLS Teardown is right */
9933ff40c12SJohn Marino 	if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
9943ff40c12SJohn Marino 						    peer->dtoken, peer,
9953ff40c12SJohn Marino 						    (u8 *) lnkid, ftie) < 0) {
9963ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
9973ff40c12SJohn Marino 			   "Teardown Request from " MACSTR, MAC2STR(src_addr));
9983ff40c12SJohn Marino 		return -1;
9993ff40c12SJohn Marino 	}
10003ff40c12SJohn Marino 
10013ff40c12SJohn Marino skip_ftie:
10023ff40c12SJohn Marino 	/*
10033ff40c12SJohn Marino 	 * Request the driver to disable the direct link and clear associated
10043ff40c12SJohn Marino 	 * keys.
10053ff40c12SJohn Marino 	 */
10063ff40c12SJohn Marino 	wpa_tdls_disable_peer_link(sm, peer);
10073ff40c12SJohn Marino 	return 0;
10083ff40c12SJohn Marino }
10093ff40c12SJohn Marino 
10103ff40c12SJohn Marino 
10113ff40c12SJohn Marino /**
10123ff40c12SJohn Marino  * wpa_tdls_send_error - To send suitable TDLS status response with
10133ff40c12SJohn Marino  *	appropriate status code mentioning reason for error/failure.
10143ff40c12SJohn Marino  * @dst 	- MAC addr of Peer station
10153ff40c12SJohn Marino  * @tdls_action - TDLS frame type for which error code is sent
1016*a1157835SDaniel Fojt  * @initiator   - was this end the initiator of the connection
10173ff40c12SJohn Marino  * @status 	- status code mentioning reason
10183ff40c12SJohn Marino  */
10193ff40c12SJohn Marino 
wpa_tdls_send_error(struct wpa_sm * sm,const u8 * dst,u8 tdls_action,u8 dialog_token,int initiator,u16 status)10203ff40c12SJohn Marino static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
1021*a1157835SDaniel Fojt 			       u8 tdls_action, u8 dialog_token, int initiator,
1022*a1157835SDaniel Fojt 			       u16 status)
10233ff40c12SJohn Marino {
10243ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR
10253ff40c12SJohn Marino 		   " (action=%u status=%u)",
10263ff40c12SJohn Marino 		   MAC2STR(dst), tdls_action, status);
10273ff40c12SJohn Marino 	return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
1028*a1157835SDaniel Fojt 				 0, initiator, NULL, 0);
10293ff40c12SJohn Marino }
10303ff40c12SJohn Marino 
10313ff40c12SJohn Marino 
10323ff40c12SJohn Marino static struct wpa_tdls_peer *
wpa_tdls_add_peer(struct wpa_sm * sm,const u8 * addr,int * existing)10333ff40c12SJohn Marino wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing)
10343ff40c12SJohn Marino {
10353ff40c12SJohn Marino 	struct wpa_tdls_peer *peer;
10363ff40c12SJohn Marino 
10373ff40c12SJohn Marino 	if (existing)
10383ff40c12SJohn Marino 		*existing = 0;
10393ff40c12SJohn Marino 	for (peer = sm->tdls; peer; peer = peer->next) {
10403ff40c12SJohn Marino 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) {
10413ff40c12SJohn Marino 			if (existing)
10423ff40c12SJohn Marino 				*existing = 1;
10433ff40c12SJohn Marino 			return peer; /* re-use existing entry */
10443ff40c12SJohn Marino 		}
10453ff40c12SJohn Marino 	}
10463ff40c12SJohn Marino 
10473ff40c12SJohn Marino 	wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR,
10483ff40c12SJohn Marino 		   MAC2STR(addr));
10493ff40c12SJohn Marino 
10503ff40c12SJohn Marino 	peer = os_zalloc(sizeof(*peer));
10513ff40c12SJohn Marino 	if (peer == NULL)
10523ff40c12SJohn Marino 		return NULL;
10533ff40c12SJohn Marino 
10543ff40c12SJohn Marino 	os_memcpy(peer->addr, addr, ETH_ALEN);
10553ff40c12SJohn Marino 	peer->next = sm->tdls;
10563ff40c12SJohn Marino 	sm->tdls = peer;
10573ff40c12SJohn Marino 
10583ff40c12SJohn Marino 	return peer;
10593ff40c12SJohn Marino }
10603ff40c12SJohn Marino 
10613ff40c12SJohn Marino 
wpa_tdls_send_tpk_m1(struct wpa_sm * sm,struct wpa_tdls_peer * peer)10623ff40c12SJohn Marino static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm,
10633ff40c12SJohn Marino 				struct wpa_tdls_peer *peer)
10643ff40c12SJohn Marino {
10653ff40c12SJohn Marino 	size_t buf_len;
10663ff40c12SJohn Marino 	struct wpa_tdls_timeoutie timeoutie;
10673ff40c12SJohn Marino 	u16 rsn_capab;
10683ff40c12SJohn Marino 	struct wpa_tdls_ftie *ftie;
10693ff40c12SJohn Marino 	u8 *rbuf, *pos, *count_pos;
10703ff40c12SJohn Marino 	u16 count;
10713ff40c12SJohn Marino 	struct rsn_ie_hdr *hdr;
10723ff40c12SJohn Marino 	int status;
10733ff40c12SJohn Marino 
10743ff40c12SJohn Marino 	if (!wpa_tdls_get_privacy(sm)) {
10753ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: No security used on the link");
10763ff40c12SJohn Marino 		peer->rsnie_i_len = 0;
10773ff40c12SJohn Marino 		goto skip_rsnie;
10783ff40c12SJohn Marino 	}
10793ff40c12SJohn Marino 
10803ff40c12SJohn Marino 	/*
10813ff40c12SJohn Marino 	 * TPK Handshake Message 1:
10823ff40c12SJohn Marino 	 * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I,
10833ff40c12SJohn Marino 	 * Timeout Interval IE))
10843ff40c12SJohn Marino 	 */
10853ff40c12SJohn Marino 
10863ff40c12SJohn Marino 	/* Filling RSN IE */
10873ff40c12SJohn Marino 	hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
10883ff40c12SJohn Marino 	hdr->elem_id = WLAN_EID_RSN;
10893ff40c12SJohn Marino 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
10903ff40c12SJohn Marino 
10913ff40c12SJohn Marino 	pos = (u8 *) (hdr + 1);
10923ff40c12SJohn Marino 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
10933ff40c12SJohn Marino 	pos += RSN_SELECTOR_LEN;
10943ff40c12SJohn Marino 	count_pos = pos;
10953ff40c12SJohn Marino 	pos += 2;
10963ff40c12SJohn Marino 
10973ff40c12SJohn Marino 	count = 0;
10983ff40c12SJohn Marino 
10993ff40c12SJohn Marino 	/*
11003ff40c12SJohn Marino 	 * AES-CCMP is the default Encryption preferred for TDLS, so
11013ff40c12SJohn Marino 	 * RSN IE is filled only with CCMP CIPHER
11023ff40c12SJohn Marino 	 * Note: TKIP is not used to encrypt TDLS link.
11033ff40c12SJohn Marino 	 *
11043ff40c12SJohn Marino 	 * Regardless of the cipher used on the AP connection, select CCMP
11053ff40c12SJohn Marino 	 * here.
11063ff40c12SJohn Marino 	 */
11073ff40c12SJohn Marino 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
11083ff40c12SJohn Marino 	pos += RSN_SELECTOR_LEN;
11093ff40c12SJohn Marino 	count++;
11103ff40c12SJohn Marino 
11113ff40c12SJohn Marino 	WPA_PUT_LE16(count_pos, count);
11123ff40c12SJohn Marino 
11133ff40c12SJohn Marino 	WPA_PUT_LE16(pos, 1);
11143ff40c12SJohn Marino 	pos += 2;
11153ff40c12SJohn Marino 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
11163ff40c12SJohn Marino 	pos += RSN_SELECTOR_LEN;
11173ff40c12SJohn Marino 
11183ff40c12SJohn Marino 	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
11193ff40c12SJohn Marino 	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
11203ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
11213ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
11223ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for "
11233ff40c12SJohn Marino 			   "testing");
11243ff40c12SJohn Marino 		rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
11253ff40c12SJohn Marino 	}
11263ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
11273ff40c12SJohn Marino 	WPA_PUT_LE16(pos, rsn_capab);
11283ff40c12SJohn Marino 	pos += 2;
11293ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
11303ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
11313ff40c12SJohn Marino 		/* Number of PMKIDs */
11323ff40c12SJohn Marino 		*pos++ = 0x00;
11333ff40c12SJohn Marino 		*pos++ = 0x00;
11343ff40c12SJohn Marino 	}
11353ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
11363ff40c12SJohn Marino 
11373ff40c12SJohn Marino 	hdr->len = (pos - peer->rsnie_i) - 2;
11383ff40c12SJohn Marino 	peer->rsnie_i_len = pos - peer->rsnie_i;
11393ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
11403ff40c12SJohn Marino 		    peer->rsnie_i, peer->rsnie_i_len);
11413ff40c12SJohn Marino 
11423ff40c12SJohn Marino skip_rsnie:
11433ff40c12SJohn Marino 	buf_len = 0;
11443ff40c12SJohn Marino 	if (wpa_tdls_get_privacy(sm))
11453ff40c12SJohn Marino 		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
11463ff40c12SJohn Marino 			sizeof(struct wpa_tdls_timeoutie);
11473ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
11483ff40c12SJohn Marino 	if (wpa_tdls_get_privacy(sm) &&
11493ff40c12SJohn Marino 	    (tdls_testing & TDLS_TESTING_LONG_FRAME))
11503ff40c12SJohn Marino 		buf_len += 170;
11513ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_DIFF_BSSID)
11523ff40c12SJohn Marino 		buf_len += sizeof(struct wpa_tdls_lnkid);
11533ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
11543ff40c12SJohn Marino 	rbuf = os_zalloc(buf_len + 1);
11553ff40c12SJohn Marino 	if (rbuf == NULL) {
11563ff40c12SJohn Marino 		wpa_tdls_peer_free(sm, peer);
11573ff40c12SJohn Marino 		return -1;
11583ff40c12SJohn Marino 	}
11593ff40c12SJohn Marino 	pos = rbuf;
11603ff40c12SJohn Marino 
11613ff40c12SJohn Marino 	if (!wpa_tdls_get_privacy(sm))
11623ff40c12SJohn Marino 		goto skip_ies;
11633ff40c12SJohn Marino 
11643ff40c12SJohn Marino 	/* Initiator RSN IE */
11653ff40c12SJohn Marino 	pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
11663ff40c12SJohn Marino 
11673ff40c12SJohn Marino 	ftie = (struct wpa_tdls_ftie *) pos;
11683ff40c12SJohn Marino 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
11693ff40c12SJohn Marino 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
11703ff40c12SJohn Marino 
11713ff40c12SJohn Marino 	if (os_get_random(peer->inonce, WPA_NONCE_LEN)) {
11723ff40c12SJohn Marino 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
11733ff40c12SJohn Marino 			"TDLS: Failed to get random data for initiator Nonce");
11743ff40c12SJohn Marino 		os_free(rbuf);
11753ff40c12SJohn Marino 		wpa_tdls_peer_free(sm, peer);
11763ff40c12SJohn Marino 		return -1;
11773ff40c12SJohn Marino 	}
1178*a1157835SDaniel Fojt 	peer->tk_set = 0; /* A new nonce results in a new TK */
11793ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake",
11803ff40c12SJohn Marino 		    peer->inonce, WPA_NONCE_LEN);
11813ff40c12SJohn Marino 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
11823ff40c12SJohn Marino 
11833ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1",
11843ff40c12SJohn Marino 		    (u8 *) ftie, sizeof(struct wpa_tdls_ftie));
11853ff40c12SJohn Marino 
11863ff40c12SJohn Marino 	pos = (u8 *) (ftie + 1);
11873ff40c12SJohn Marino 
11883ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
11893ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
11903ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
11913ff40c12SJohn Marino 			   "FTIE");
11923ff40c12SJohn Marino 		ftie->ie_len += 170;
11933ff40c12SJohn Marino 		*pos++ = 255; /* FTIE subelem */
11943ff40c12SJohn Marino 		*pos++ = 168; /* FTIE subelem length */
11953ff40c12SJohn Marino 		pos += 168;
11963ff40c12SJohn Marino 	}
11973ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
11983ff40c12SJohn Marino 
11993ff40c12SJohn Marino 	/* Lifetime */
12003ff40c12SJohn Marino 	peer->lifetime = TPK_LIFETIME;
12013ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
12023ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) {
12033ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK "
12043ff40c12SJohn Marino 			   "lifetime");
12053ff40c12SJohn Marino 		peer->lifetime = 301;
12063ff40c12SJohn Marino 	}
12073ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) {
12083ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK "
12093ff40c12SJohn Marino 			   "lifetime");
12103ff40c12SJohn Marino 		peer->lifetime = 0xffffffff;
12113ff40c12SJohn Marino 	}
12123ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
12133ff40c12SJohn Marino 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
12143ff40c12SJohn Marino 				     sizeof(timeoutie), peer->lifetime);
12153ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
12163ff40c12SJohn Marino 
12173ff40c12SJohn Marino skip_ies:
12183ff40c12SJohn Marino 
12193ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
12203ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_DIFF_BSSID) {
1221*a1157835SDaniel Fojt 		struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
1222*a1157835SDaniel Fojt 
12233ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in "
12243ff40c12SJohn Marino 			   "Link Identifier");
12253ff40c12SJohn Marino 		wpa_tdls_linkid(sm, peer, l);
12263ff40c12SJohn Marino 		l->bssid[5] ^= 0x01;
12273ff40c12SJohn Marino 		pos += sizeof(*l);
12283ff40c12SJohn Marino 	}
12293ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
12303ff40c12SJohn Marino 
12313ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK "
12323ff40c12SJohn Marino 		   "Handshake Message 1 (peer " MACSTR ")",
12333ff40c12SJohn Marino 		   MAC2STR(peer->addr));
12343ff40c12SJohn Marino 
12353ff40c12SJohn Marino 	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST,
1236*a1157835SDaniel Fojt 				   1, 0, 0, peer->initiator, rbuf, pos - rbuf);
12373ff40c12SJohn Marino 	os_free(rbuf);
12383ff40c12SJohn Marino 
12393ff40c12SJohn Marino 	return status;
12403ff40c12SJohn Marino }
12413ff40c12SJohn Marino 
12423ff40c12SJohn Marino 
wpa_tdls_send_tpk_m2(struct wpa_sm * sm,const unsigned char * src_addr,u8 dtoken,struct wpa_tdls_lnkid * lnkid,const struct wpa_tdls_peer * peer)12433ff40c12SJohn Marino static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm,
12443ff40c12SJohn Marino 				const unsigned char *src_addr, u8 dtoken,
12453ff40c12SJohn Marino 				struct wpa_tdls_lnkid *lnkid,
12463ff40c12SJohn Marino 				const struct wpa_tdls_peer *peer)
12473ff40c12SJohn Marino {
12483ff40c12SJohn Marino 	u8 *rbuf, *pos;
12493ff40c12SJohn Marino 	size_t buf_len;
12503ff40c12SJohn Marino 	u32 lifetime;
12513ff40c12SJohn Marino 	struct wpa_tdls_timeoutie timeoutie;
12523ff40c12SJohn Marino 	struct wpa_tdls_ftie *ftie;
12533ff40c12SJohn Marino 	int status;
12543ff40c12SJohn Marino 
12553ff40c12SJohn Marino 	buf_len = 0;
12563ff40c12SJohn Marino 	if (wpa_tdls_get_privacy(sm)) {
12573ff40c12SJohn Marino 		/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
12583ff40c12SJohn Marino 		 * Lifetime */
12593ff40c12SJohn Marino 		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
12603ff40c12SJohn Marino 			sizeof(struct wpa_tdls_timeoutie);
12613ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
12623ff40c12SJohn Marino 		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
12633ff40c12SJohn Marino 			buf_len += 170;
12643ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
12653ff40c12SJohn Marino 	}
12663ff40c12SJohn Marino 
12673ff40c12SJohn Marino 	rbuf = os_zalloc(buf_len + 1);
12683ff40c12SJohn Marino 	if (rbuf == NULL)
12693ff40c12SJohn Marino 		return -1;
12703ff40c12SJohn Marino 	pos = rbuf;
12713ff40c12SJohn Marino 
12723ff40c12SJohn Marino 	if (!wpa_tdls_get_privacy(sm))
12733ff40c12SJohn Marino 		goto skip_ies;
12743ff40c12SJohn Marino 
12753ff40c12SJohn Marino 	/* Peer RSN IE */
12763ff40c12SJohn Marino 	pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
12773ff40c12SJohn Marino 
12783ff40c12SJohn Marino 	ftie = (struct wpa_tdls_ftie *) pos;
12793ff40c12SJohn Marino 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
12803ff40c12SJohn Marino 	/* TODO: ftie->mic_control to set 2-RESPONSE */
12813ff40c12SJohn Marino 	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
12823ff40c12SJohn Marino 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
12833ff40c12SJohn Marino 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
12843ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2",
12853ff40c12SJohn Marino 		    (u8 *) ftie, sizeof(*ftie));
12863ff40c12SJohn Marino 
12873ff40c12SJohn Marino 	pos = (u8 *) (ftie + 1);
12883ff40c12SJohn Marino 
12893ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
12903ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
12913ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
12923ff40c12SJohn Marino 			   "FTIE");
12933ff40c12SJohn Marino 		ftie->ie_len += 170;
12943ff40c12SJohn Marino 		*pos++ = 255; /* FTIE subelem */
12953ff40c12SJohn Marino 		*pos++ = 168; /* FTIE subelem length */
12963ff40c12SJohn Marino 		pos += 168;
12973ff40c12SJohn Marino 	}
12983ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
12993ff40c12SJohn Marino 
13003ff40c12SJohn Marino 	/* Lifetime */
13013ff40c12SJohn Marino 	lifetime = peer->lifetime;
13023ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
13033ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) {
13043ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
13053ff40c12SJohn Marino 			   "lifetime in response");
13063ff40c12SJohn Marino 		lifetime++;
13073ff40c12SJohn Marino 	}
13083ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
13093ff40c12SJohn Marino 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
13103ff40c12SJohn Marino 				     sizeof(timeoutie), lifetime);
13113ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator",
13123ff40c12SJohn Marino 		   lifetime);
13133ff40c12SJohn Marino 
13143ff40c12SJohn Marino 	/* compute MIC before sending */
13153ff40c12SJohn Marino 	wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
13163ff40c12SJohn Marino 			  (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
1317*a1157835SDaniel Fojt #ifdef CONFIG_TDLS_TESTING
1318*a1157835SDaniel Fojt 	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
1319*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
1320*a1157835SDaniel Fojt 		ftie->mic[0] ^= 0x01;
1321*a1157835SDaniel Fojt 	}
1322*a1157835SDaniel Fojt #endif /* CONFIG_TDLS_TESTING */
13233ff40c12SJohn Marino 
13243ff40c12SJohn Marino skip_ies:
13253ff40c12SJohn Marino 	status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
1326*a1157835SDaniel Fojt 				   dtoken, 0, 0, peer->initiator, rbuf,
1327*a1157835SDaniel Fojt 				   pos - rbuf);
13283ff40c12SJohn Marino 	os_free(rbuf);
13293ff40c12SJohn Marino 
13303ff40c12SJohn Marino 	return status;
13313ff40c12SJohn Marino }
13323ff40c12SJohn Marino 
13333ff40c12SJohn Marino 
wpa_tdls_send_tpk_m3(struct wpa_sm * sm,const unsigned char * src_addr,u8 dtoken,struct wpa_tdls_lnkid * lnkid,const struct wpa_tdls_peer * peer)13343ff40c12SJohn Marino static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm,
13353ff40c12SJohn Marino 				const unsigned char *src_addr, u8 dtoken,
13363ff40c12SJohn Marino 				struct wpa_tdls_lnkid *lnkid,
13373ff40c12SJohn Marino 				const struct wpa_tdls_peer *peer)
13383ff40c12SJohn Marino {
13393ff40c12SJohn Marino 	u8 *rbuf, *pos;
13403ff40c12SJohn Marino 	size_t buf_len;
13413ff40c12SJohn Marino 	struct wpa_tdls_ftie *ftie;
13423ff40c12SJohn Marino 	struct wpa_tdls_timeoutie timeoutie;
13433ff40c12SJohn Marino 	u32 lifetime;
13443ff40c12SJohn Marino 	int status;
1345*a1157835SDaniel Fojt 	u32 peer_capab = 0;
13463ff40c12SJohn Marino 
13473ff40c12SJohn Marino 	buf_len = 0;
13483ff40c12SJohn Marino 	if (wpa_tdls_get_privacy(sm)) {
13493ff40c12SJohn Marino 		/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
13503ff40c12SJohn Marino 		 * Lifetime */
13513ff40c12SJohn Marino 		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
13523ff40c12SJohn Marino 			sizeof(struct wpa_tdls_timeoutie);
13533ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
13543ff40c12SJohn Marino 		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
13553ff40c12SJohn Marino 			buf_len += 170;
13563ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
13573ff40c12SJohn Marino 	}
13583ff40c12SJohn Marino 
13593ff40c12SJohn Marino 	rbuf = os_zalloc(buf_len + 1);
13603ff40c12SJohn Marino 	if (rbuf == NULL)
13613ff40c12SJohn Marino 		return -1;
13623ff40c12SJohn Marino 	pos = rbuf;
13633ff40c12SJohn Marino 
13643ff40c12SJohn Marino 	if (!wpa_tdls_get_privacy(sm))
13653ff40c12SJohn Marino 		goto skip_ies;
13663ff40c12SJohn Marino 
13673ff40c12SJohn Marino 	/* Peer RSN IE */
13683ff40c12SJohn Marino 	pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
13693ff40c12SJohn Marino 
13703ff40c12SJohn Marino 	ftie = (struct wpa_tdls_ftie *) pos;
13713ff40c12SJohn Marino 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
13723ff40c12SJohn Marino 	/*TODO: ftie->mic_control to set 3-CONFIRM */
13733ff40c12SJohn Marino 	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
13743ff40c12SJohn Marino 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
13753ff40c12SJohn Marino 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
13763ff40c12SJohn Marino 
13773ff40c12SJohn Marino 	pos = (u8 *) (ftie + 1);
13783ff40c12SJohn Marino 
13793ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
13803ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
13813ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
13823ff40c12SJohn Marino 			   "FTIE");
13833ff40c12SJohn Marino 		ftie->ie_len += 170;
13843ff40c12SJohn Marino 		*pos++ = 255; /* FTIE subelem */
13853ff40c12SJohn Marino 		*pos++ = 168; /* FTIE subelem length */
13863ff40c12SJohn Marino 		pos += 168;
13873ff40c12SJohn Marino 	}
13883ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
13893ff40c12SJohn Marino 
13903ff40c12SJohn Marino 	/* Lifetime */
13913ff40c12SJohn Marino 	lifetime = peer->lifetime;
13923ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
13933ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) {
13943ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
13953ff40c12SJohn Marino 			   "lifetime in confirm");
13963ff40c12SJohn Marino 		lifetime++;
13973ff40c12SJohn Marino 	}
13983ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
13993ff40c12SJohn Marino 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
14003ff40c12SJohn Marino 				     sizeof(timeoutie), lifetime);
14013ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds",
14023ff40c12SJohn Marino 		   lifetime);
14033ff40c12SJohn Marino 
14043ff40c12SJohn Marino 	/* compute MIC before sending */
14053ff40c12SJohn Marino 	wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
14063ff40c12SJohn Marino 			  (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
1407*a1157835SDaniel Fojt #ifdef CONFIG_TDLS_TESTING
1408*a1157835SDaniel Fojt 	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
1409*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
1410*a1157835SDaniel Fojt 		ftie->mic[0] ^= 0x01;
1411*a1157835SDaniel Fojt 	}
1412*a1157835SDaniel Fojt #endif /* CONFIG_TDLS_TESTING */
14133ff40c12SJohn Marino 
14143ff40c12SJohn Marino skip_ies:
1415*a1157835SDaniel Fojt 
1416*a1157835SDaniel Fojt 	if (peer->vht_capabilities)
1417*a1157835SDaniel Fojt 		peer_capab |= TDLS_PEER_VHT;
1418*a1157835SDaniel Fojt 	if (peer->ht_capabilities)
1419*a1157835SDaniel Fojt 		peer_capab |= TDLS_PEER_HT;
1420*a1157835SDaniel Fojt 	if (peer->wmm_capable)
1421*a1157835SDaniel Fojt 		peer_capab |= TDLS_PEER_WMM;
1422*a1157835SDaniel Fojt 
14233ff40c12SJohn Marino 	status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
1424*a1157835SDaniel Fojt 				   dtoken, 0, peer_capab, peer->initiator,
1425*a1157835SDaniel Fojt 				   rbuf, pos - rbuf);
14263ff40c12SJohn Marino 	os_free(rbuf);
14273ff40c12SJohn Marino 
14283ff40c12SJohn Marino 	return status;
14293ff40c12SJohn Marino }
14303ff40c12SJohn Marino 
14313ff40c12SJohn Marino 
wpa_tdls_send_discovery_response(struct wpa_sm * sm,struct wpa_tdls_peer * peer,u8 dialog_token)14323ff40c12SJohn Marino static int wpa_tdls_send_discovery_response(struct wpa_sm *sm,
14333ff40c12SJohn Marino 					    struct wpa_tdls_peer *peer,
14343ff40c12SJohn Marino 					    u8 dialog_token)
14353ff40c12SJohn Marino {
1436*a1157835SDaniel Fojt 	size_t buf_len = 0;
1437*a1157835SDaniel Fojt 	struct wpa_tdls_timeoutie timeoutie;
1438*a1157835SDaniel Fojt 	u16 rsn_capab;
1439*a1157835SDaniel Fojt 	u8 *rbuf, *pos, *count_pos;
1440*a1157835SDaniel Fojt 	u16 count;
1441*a1157835SDaniel Fojt 	struct rsn_ie_hdr *hdr;
1442*a1157835SDaniel Fojt 	int status;
1443*a1157835SDaniel Fojt 
14443ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response "
14453ff40c12SJohn Marino 		   "(peer " MACSTR ")", MAC2STR(peer->addr));
1446*a1157835SDaniel Fojt 	if (!wpa_tdls_get_privacy(sm))
1447*a1157835SDaniel Fojt 		goto skip_rsn_ies;
14483ff40c12SJohn Marino 
1449*a1157835SDaniel Fojt 	/* Filling RSN IE */
1450*a1157835SDaniel Fojt 	hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
1451*a1157835SDaniel Fojt 	hdr->elem_id = WLAN_EID_RSN;
1452*a1157835SDaniel Fojt 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
1453*a1157835SDaniel Fojt 	pos = (u8 *) (hdr + 1);
1454*a1157835SDaniel Fojt 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
1455*a1157835SDaniel Fojt 	pos += RSN_SELECTOR_LEN;
1456*a1157835SDaniel Fojt 	count_pos = pos;
1457*a1157835SDaniel Fojt 	pos += 2;
1458*a1157835SDaniel Fojt 	count = 0;
1459*a1157835SDaniel Fojt 
1460*a1157835SDaniel Fojt 	/*
1461*a1157835SDaniel Fojt 	* AES-CCMP is the default encryption preferred for TDLS, so
1462*a1157835SDaniel Fojt 	* RSN IE is filled only with CCMP cipher suite.
1463*a1157835SDaniel Fojt 	* Note: TKIP is not used to encrypt TDLS link.
1464*a1157835SDaniel Fojt 	*
1465*a1157835SDaniel Fojt 	* Regardless of the cipher used on the AP connection, select CCMP
1466*a1157835SDaniel Fojt 	* here.
1467*a1157835SDaniel Fojt 	*/
1468*a1157835SDaniel Fojt 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
1469*a1157835SDaniel Fojt 	pos += RSN_SELECTOR_LEN;
1470*a1157835SDaniel Fojt 	count++;
1471*a1157835SDaniel Fojt 	WPA_PUT_LE16(count_pos, count);
1472*a1157835SDaniel Fojt 	WPA_PUT_LE16(pos, 1);
1473*a1157835SDaniel Fojt 	pos += 2;
1474*a1157835SDaniel Fojt 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
1475*a1157835SDaniel Fojt 	pos += RSN_SELECTOR_LEN;
1476*a1157835SDaniel Fojt 
1477*a1157835SDaniel Fojt 	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
1478*a1157835SDaniel Fojt 	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
1479*a1157835SDaniel Fojt 	WPA_PUT_LE16(pos, rsn_capab);
1480*a1157835SDaniel Fojt 	pos += 2;
1481*a1157835SDaniel Fojt 	hdr->len = (pos - (u8 *) hdr) - 2;
1482*a1157835SDaniel Fojt 	peer->rsnie_i_len = pos - peer->rsnie_i;
1483*a1157835SDaniel Fojt 
1484*a1157835SDaniel Fojt 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response",
1485*a1157835SDaniel Fojt 		    (u8 *) hdr, hdr->len + 2);
1486*a1157835SDaniel Fojt skip_rsn_ies:
1487*a1157835SDaniel Fojt 	buf_len = 0;
1488*a1157835SDaniel Fojt 	if (wpa_tdls_get_privacy(sm)) {
1489*a1157835SDaniel Fojt 		/* Peer RSN IE, Lifetime */
1490*a1157835SDaniel Fojt 		buf_len += peer->rsnie_i_len +
1491*a1157835SDaniel Fojt 			sizeof(struct wpa_tdls_timeoutie);
1492*a1157835SDaniel Fojt 	}
1493*a1157835SDaniel Fojt 	rbuf = os_zalloc(buf_len + 1);
1494*a1157835SDaniel Fojt 	if (rbuf == NULL) {
1495*a1157835SDaniel Fojt 		wpa_tdls_peer_free(sm, peer);
1496*a1157835SDaniel Fojt 		return -1;
1497*a1157835SDaniel Fojt 	}
1498*a1157835SDaniel Fojt 	pos = rbuf;
1499*a1157835SDaniel Fojt 
1500*a1157835SDaniel Fojt 	if (!wpa_tdls_get_privacy(sm))
1501*a1157835SDaniel Fojt 		goto skip_ies;
1502*a1157835SDaniel Fojt 	/* Initiator RSN IE */
1503*a1157835SDaniel Fojt 	pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
1504*a1157835SDaniel Fojt 	/* Lifetime */
1505*a1157835SDaniel Fojt 	peer->lifetime = TPK_LIFETIME;
1506*a1157835SDaniel Fojt 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
1507*a1157835SDaniel Fojt 				     sizeof(timeoutie), peer->lifetime);
1508*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
1509*a1157835SDaniel Fojt skip_ies:
1510*a1157835SDaniel Fojt 	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
1511*a1157835SDaniel Fojt 				   dialog_token, 0, 0, 0, rbuf, pos - rbuf);
1512*a1157835SDaniel Fojt 	os_free(rbuf);
1513*a1157835SDaniel Fojt 
1514*a1157835SDaniel Fojt 	return status;
15153ff40c12SJohn Marino }
15163ff40c12SJohn Marino 
15173ff40c12SJohn Marino 
15183ff40c12SJohn Marino static int
wpa_tdls_process_discovery_request(struct wpa_sm * sm,const u8 * addr,const u8 * buf,size_t len)15193ff40c12SJohn Marino wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
15203ff40c12SJohn Marino 				   const u8 *buf, size_t len)
15213ff40c12SJohn Marino {
15223ff40c12SJohn Marino 	struct wpa_eapol_ie_parse kde;
15233ff40c12SJohn Marino 	const struct wpa_tdls_lnkid *lnkid;
15243ff40c12SJohn Marino 	struct wpa_tdls_peer *peer;
15253ff40c12SJohn Marino 	size_t min_req_len = sizeof(struct wpa_tdls_frame) +
15263ff40c12SJohn Marino 		1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid);
15273ff40c12SJohn Marino 	u8 dialog_token;
15283ff40c12SJohn Marino 
15293ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR,
15303ff40c12SJohn Marino 		   MAC2STR(addr));
15313ff40c12SJohn Marino 
15323ff40c12SJohn Marino 	if (len < min_req_len) {
15333ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: "
15343ff40c12SJohn Marino 			   "%d", (int) len);
15353ff40c12SJohn Marino 		return -1;
15363ff40c12SJohn Marino 	}
15373ff40c12SJohn Marino 
15383ff40c12SJohn Marino 	dialog_token = buf[sizeof(struct wpa_tdls_frame)];
15393ff40c12SJohn Marino 
1540*a1157835SDaniel Fojt 	/*
1541*a1157835SDaniel Fojt 	 * Some APs will tack on a weird IE to the end of a TDLS
1542*a1157835SDaniel Fojt 	 * discovery request packet. This needn't fail the response,
1543*a1157835SDaniel Fojt 	 * since the required IE are verified separately.
1544*a1157835SDaniel Fojt 	 */
15453ff40c12SJohn Marino 	if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
15463ff40c12SJohn Marino 				     len - (sizeof(struct wpa_tdls_frame) + 1),
1547*a1157835SDaniel Fojt 				     &kde) < 0) {
1548*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
1549*a1157835SDaniel Fojt 			   "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround");
1550*a1157835SDaniel Fojt 	}
15513ff40c12SJohn Marino 
15523ff40c12SJohn Marino 	if (!kde.lnkid) {
15533ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
15543ff40c12SJohn Marino 			   "Request");
15553ff40c12SJohn Marino 		return -1;
15563ff40c12SJohn Marino 	}
15573ff40c12SJohn Marino 
15583ff40c12SJohn Marino 	lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid;
15593ff40c12SJohn Marino 
15603ff40c12SJohn Marino 	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
15613ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different "
15623ff40c12SJohn Marino 			   " BSS " MACSTR, MAC2STR(lnkid->bssid));
15633ff40c12SJohn Marino 		return -1;
15643ff40c12SJohn Marino 	}
15653ff40c12SJohn Marino 
15663ff40c12SJohn Marino 	peer = wpa_tdls_add_peer(sm, addr, NULL);
15673ff40c12SJohn Marino 	if (peer == NULL)
15683ff40c12SJohn Marino 		return -1;
15693ff40c12SJohn Marino 
15703ff40c12SJohn Marino 	return wpa_tdls_send_discovery_response(sm, peer, dialog_token);
15713ff40c12SJohn Marino }
15723ff40c12SJohn Marino 
15733ff40c12SJohn Marino 
wpa_tdls_send_discovery_request(struct wpa_sm * sm,const u8 * addr)15743ff40c12SJohn Marino int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr)
15753ff40c12SJohn Marino {
15763ff40c12SJohn Marino 	if (sm->tdls_disabled || !sm->tdls_supported)
15773ff40c12SJohn Marino 		return -1;
15783ff40c12SJohn Marino 
15793ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer "
15803ff40c12SJohn Marino 		   MACSTR, MAC2STR(addr));
15813ff40c12SJohn Marino 	return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST,
1582*a1157835SDaniel Fojt 				 1, 0, 0, 1, NULL, 0);
15833ff40c12SJohn Marino }
15843ff40c12SJohn Marino 
15853ff40c12SJohn Marino 
copy_supp_rates(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)15863ff40c12SJohn Marino static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde,
15873ff40c12SJohn Marino 			   struct wpa_tdls_peer *peer)
15883ff40c12SJohn Marino {
15893ff40c12SJohn Marino 	if (!kde->supp_rates) {
15903ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
15913ff40c12SJohn Marino 		return -1;
15923ff40c12SJohn Marino 	}
15933ff40c12SJohn Marino 	peer->supp_rates_len = merge_byte_arrays(
15943ff40c12SJohn Marino 		peer->supp_rates, sizeof(peer->supp_rates),
15953ff40c12SJohn Marino 		kde->supp_rates + 2, kde->supp_rates_len - 2,
15963ff40c12SJohn Marino 		kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL,
1597*a1157835SDaniel Fojt 		kde->ext_supp_rates ? kde->ext_supp_rates_len - 2 : 0);
15983ff40c12SJohn Marino 	return 0;
15993ff40c12SJohn Marino }
16003ff40c12SJohn Marino 
16013ff40c12SJohn Marino 
copy_peer_ht_capab(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)16023ff40c12SJohn Marino static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde,
16033ff40c12SJohn Marino 			      struct wpa_tdls_peer *peer)
16043ff40c12SJohn Marino {
1605*a1157835SDaniel Fojt 	if (!kde->ht_capabilities) {
16063ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities "
16073ff40c12SJohn Marino 			   "received");
16083ff40c12SJohn Marino 		return 0;
16093ff40c12SJohn Marino 	}
16103ff40c12SJohn Marino 
16113ff40c12SJohn Marino 	if (!peer->ht_capabilities) {
16123ff40c12SJohn Marino 		peer->ht_capabilities =
16133ff40c12SJohn Marino                         os_zalloc(sizeof(struct ieee80211_ht_capabilities));
16143ff40c12SJohn Marino 		if (peer->ht_capabilities == NULL)
16153ff40c12SJohn Marino                         return -1;
16163ff40c12SJohn Marino 	}
16173ff40c12SJohn Marino 
16183ff40c12SJohn Marino 	os_memcpy(peer->ht_capabilities, kde->ht_capabilities,
16193ff40c12SJohn Marino                   sizeof(struct ieee80211_ht_capabilities));
16203ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities",
16213ff40c12SJohn Marino 		    (u8 *) peer->ht_capabilities,
16223ff40c12SJohn Marino 		    sizeof(struct ieee80211_ht_capabilities));
16233ff40c12SJohn Marino 
16243ff40c12SJohn Marino 	return 0;
16253ff40c12SJohn Marino }
16263ff40c12SJohn Marino 
16273ff40c12SJohn Marino 
copy_peer_vht_capab(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)16283ff40c12SJohn Marino static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde,
16293ff40c12SJohn Marino 			      struct wpa_tdls_peer *peer)
16303ff40c12SJohn Marino {
1631*a1157835SDaniel Fojt 	if (!kde->vht_capabilities) {
16323ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities "
16333ff40c12SJohn Marino 			   "received");
16343ff40c12SJohn Marino 		return 0;
16353ff40c12SJohn Marino 	}
16363ff40c12SJohn Marino 
16373ff40c12SJohn Marino 	if (!peer->vht_capabilities) {
16383ff40c12SJohn Marino 		peer->vht_capabilities =
16393ff40c12SJohn Marino                         os_zalloc(sizeof(struct ieee80211_vht_capabilities));
16403ff40c12SJohn Marino 		if (peer->vht_capabilities == NULL)
16413ff40c12SJohn Marino                         return -1;
16423ff40c12SJohn Marino 	}
16433ff40c12SJohn Marino 
16443ff40c12SJohn Marino 	os_memcpy(peer->vht_capabilities, kde->vht_capabilities,
16453ff40c12SJohn Marino                   sizeof(struct ieee80211_vht_capabilities));
16463ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities",
16473ff40c12SJohn Marino 		    (u8 *) peer->vht_capabilities,
16483ff40c12SJohn Marino 		    sizeof(struct ieee80211_vht_capabilities));
16493ff40c12SJohn Marino 
16503ff40c12SJohn Marino 	return 0;
16513ff40c12SJohn Marino }
16523ff40c12SJohn Marino 
16533ff40c12SJohn Marino 
copy_peer_ext_capab(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)16543ff40c12SJohn Marino static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde,
16553ff40c12SJohn Marino 			       struct wpa_tdls_peer *peer)
16563ff40c12SJohn Marino {
16573ff40c12SJohn Marino 	if (!kde->ext_capab) {
16583ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities "
16593ff40c12SJohn Marino 			   "received");
16603ff40c12SJohn Marino 		return 0;
16613ff40c12SJohn Marino 	}
16623ff40c12SJohn Marino 
16633ff40c12SJohn Marino 	if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) {
16643ff40c12SJohn Marino 		/* Need to allocate buffer to fit the new information */
16653ff40c12SJohn Marino 		os_free(peer->ext_capab);
16663ff40c12SJohn Marino 		peer->ext_capab = os_zalloc(kde->ext_capab_len - 2);
16673ff40c12SJohn Marino 		if (peer->ext_capab == NULL)
16683ff40c12SJohn Marino 			return -1;
16693ff40c12SJohn Marino 	}
16703ff40c12SJohn Marino 
16713ff40c12SJohn Marino 	peer->ext_capab_len = kde->ext_capab_len - 2;
16723ff40c12SJohn Marino 	os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len);
16733ff40c12SJohn Marino 
16743ff40c12SJohn Marino 	return 0;
16753ff40c12SJohn Marino }
16763ff40c12SJohn Marino 
16773ff40c12SJohn Marino 
copy_peer_wmm_capab(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)1678*a1157835SDaniel Fojt static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde,
1679*a1157835SDaniel Fojt 			       struct wpa_tdls_peer *peer)
1680*a1157835SDaniel Fojt {
1681*a1157835SDaniel Fojt 	struct wmm_information_element *wmm;
1682*a1157835SDaniel Fojt 
1683*a1157835SDaniel Fojt 	if (!kde->wmm) {
1684*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "TDLS: No supported WMM capabilities received");
1685*a1157835SDaniel Fojt 		return 0;
1686*a1157835SDaniel Fojt 	}
1687*a1157835SDaniel Fojt 
1688*a1157835SDaniel Fojt 	if (kde->wmm_len < sizeof(struct wmm_information_element)) {
1689*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "TDLS: Invalid supported WMM capabilities received");
1690*a1157835SDaniel Fojt 		return -1;
1691*a1157835SDaniel Fojt 	}
1692*a1157835SDaniel Fojt 
1693*a1157835SDaniel Fojt 	wmm = (struct wmm_information_element *) kde->wmm;
1694*a1157835SDaniel Fojt 	peer->qos_info = wmm->qos_info;
1695*a1157835SDaniel Fojt 
1696*a1157835SDaniel Fojt 	peer->wmm_capable = 1;
1697*a1157835SDaniel Fojt 
1698*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "TDLS: Peer WMM QOS Info 0x%x", peer->qos_info);
1699*a1157835SDaniel Fojt 	return 0;
1700*a1157835SDaniel Fojt }
1701*a1157835SDaniel Fojt 
1702*a1157835SDaniel Fojt 
copy_peer_supp_channels(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)17033ff40c12SJohn Marino static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde,
17043ff40c12SJohn Marino 				   struct wpa_tdls_peer *peer)
17053ff40c12SJohn Marino {
17063ff40c12SJohn Marino 	if (!kde->supp_channels) {
17073ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: No supported channels received");
17083ff40c12SJohn Marino 		return 0;
17093ff40c12SJohn Marino 	}
17103ff40c12SJohn Marino 
17113ff40c12SJohn Marino 	if (!peer->supp_channels ||
17123ff40c12SJohn Marino 	    peer->supp_channels_len < kde->supp_channels_len) {
17133ff40c12SJohn Marino 		os_free(peer->supp_channels);
17143ff40c12SJohn Marino 		peer->supp_channels = os_zalloc(kde->supp_channels_len);
17153ff40c12SJohn Marino 		if (peer->supp_channels == NULL)
17163ff40c12SJohn Marino 			return -1;
17173ff40c12SJohn Marino 	}
17183ff40c12SJohn Marino 
17193ff40c12SJohn Marino 	peer->supp_channels_len = kde->supp_channels_len;
17203ff40c12SJohn Marino 
17213ff40c12SJohn Marino 	os_memcpy(peer->supp_channels, kde->supp_channels,
17223ff40c12SJohn Marino 		  peer->supp_channels_len);
17233ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels",
17243ff40c12SJohn Marino 		    (u8 *) peer->supp_channels, peer->supp_channels_len);
17253ff40c12SJohn Marino 	return 0;
17263ff40c12SJohn Marino }
17273ff40c12SJohn Marino 
17283ff40c12SJohn Marino 
copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)17293ff40c12SJohn Marino static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde,
17303ff40c12SJohn Marino 				       struct wpa_tdls_peer *peer)
17313ff40c12SJohn Marino {
17323ff40c12SJohn Marino 	if (!kde->supp_oper_classes) {
17333ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received");
17343ff40c12SJohn Marino 		return 0;
17353ff40c12SJohn Marino 	}
17363ff40c12SJohn Marino 
17373ff40c12SJohn Marino 	if (!peer->supp_oper_classes ||
17383ff40c12SJohn Marino 	    peer->supp_oper_classes_len < kde->supp_oper_classes_len) {
17393ff40c12SJohn Marino 		os_free(peer->supp_oper_classes);
17403ff40c12SJohn Marino 		peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len);
17413ff40c12SJohn Marino 		if (peer->supp_oper_classes == NULL)
17423ff40c12SJohn Marino 			return -1;
17433ff40c12SJohn Marino 	}
17443ff40c12SJohn Marino 
17453ff40c12SJohn Marino 	peer->supp_oper_classes_len = kde->supp_oper_classes_len;
17463ff40c12SJohn Marino 	os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes,
17473ff40c12SJohn Marino 		  peer->supp_oper_classes_len);
17483ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes",
17493ff40c12SJohn Marino 		    (u8 *) peer->supp_oper_classes,
17503ff40c12SJohn Marino 		    peer->supp_oper_classes_len);
17513ff40c12SJohn Marino 	return 0;
17523ff40c12SJohn Marino }
17533ff40c12SJohn Marino 
17543ff40c12SJohn Marino 
wpa_tdls_addset_peer(struct wpa_sm * sm,struct wpa_tdls_peer * peer,int add)1755*a1157835SDaniel Fojt static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
1756*a1157835SDaniel Fojt 				int add)
1757*a1157835SDaniel Fojt {
1758*a1157835SDaniel Fojt 	return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid,
1759*a1157835SDaniel Fojt 				       peer->capability,
1760*a1157835SDaniel Fojt 				       peer->supp_rates, peer->supp_rates_len,
1761*a1157835SDaniel Fojt 				       peer->ht_capabilities,
1762*a1157835SDaniel Fojt 				       peer->vht_capabilities,
1763*a1157835SDaniel Fojt 				       peer->qos_info, peer->wmm_capable,
1764*a1157835SDaniel Fojt 				       peer->ext_capab, peer->ext_capab_len,
1765*a1157835SDaniel Fojt 				       peer->supp_channels,
1766*a1157835SDaniel Fojt 				       peer->supp_channels_len,
1767*a1157835SDaniel Fojt 				       peer->supp_oper_classes,
1768*a1157835SDaniel Fojt 				       peer->supp_oper_classes_len);
1769*a1157835SDaniel Fojt }
1770*a1157835SDaniel Fojt 
1771*a1157835SDaniel Fojt 
tdls_nonce_set(const u8 * nonce)1772*a1157835SDaniel Fojt static int tdls_nonce_set(const u8 *nonce)
1773*a1157835SDaniel Fojt {
1774*a1157835SDaniel Fojt 	int i;
1775*a1157835SDaniel Fojt 
1776*a1157835SDaniel Fojt 	for (i = 0; i < WPA_NONCE_LEN; i++) {
1777*a1157835SDaniel Fojt 		if (nonce[i])
1778*a1157835SDaniel Fojt 			return 1;
1779*a1157835SDaniel Fojt 	}
1780*a1157835SDaniel Fojt 
1781*a1157835SDaniel Fojt 	return 0;
1782*a1157835SDaniel Fojt }
1783*a1157835SDaniel Fojt 
1784*a1157835SDaniel Fojt 
wpa_tdls_process_tpk_m1(struct wpa_sm * sm,const u8 * src_addr,const u8 * buf,size_t len)17853ff40c12SJohn Marino static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
17863ff40c12SJohn Marino 				   const u8 *buf, size_t len)
17873ff40c12SJohn Marino {
17883ff40c12SJohn Marino 	struct wpa_tdls_peer *peer;
17893ff40c12SJohn Marino 	struct wpa_eapol_ie_parse kde;
17903ff40c12SJohn Marino 	struct wpa_ie_data ie;
17913ff40c12SJohn Marino 	int cipher;
17923ff40c12SJohn Marino 	const u8 *cpos;
17933ff40c12SJohn Marino 	struct wpa_tdls_ftie *ftie = NULL;
17943ff40c12SJohn Marino 	struct wpa_tdls_timeoutie *timeoutie;
17953ff40c12SJohn Marino 	struct wpa_tdls_lnkid *lnkid;
17963ff40c12SJohn Marino 	u32 lifetime = 0;
17973ff40c12SJohn Marino #if 0
17983ff40c12SJohn Marino 	struct rsn_ie_hdr *hdr;
17993ff40c12SJohn Marino 	u8 *pos;
18003ff40c12SJohn Marino 	u16 rsn_capab;
18013ff40c12SJohn Marino 	u16 rsn_ver;
18023ff40c12SJohn Marino #endif
18033ff40c12SJohn Marino 	u8 dtoken;
18043ff40c12SJohn Marino 	u16 ielen;
18053ff40c12SJohn Marino 	u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
18063ff40c12SJohn Marino 	int tdls_prohibited = sm->tdls_prohibited;
18073ff40c12SJohn Marino 	int existing_peer = 0;
18083ff40c12SJohn Marino 
18093ff40c12SJohn Marino 	if (len < 3 + 3)
18103ff40c12SJohn Marino 		return -1;
18113ff40c12SJohn Marino 
18123ff40c12SJohn Marino 	cpos = buf;
18133ff40c12SJohn Marino 	cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
18143ff40c12SJohn Marino 
18153ff40c12SJohn Marino 	/* driver had already verified the frame format */
18163ff40c12SJohn Marino 	dtoken = *cpos++; /* dialog token */
18173ff40c12SJohn Marino 
18183ff40c12SJohn Marino 	wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken);
18193ff40c12SJohn Marino 
18203ff40c12SJohn Marino 	peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer);
18213ff40c12SJohn Marino 	if (peer == NULL)
18223ff40c12SJohn Marino 		goto error;
18233ff40c12SJohn Marino 
18243ff40c12SJohn Marino 	/* If found, use existing entry instead of adding a new one;
18253ff40c12SJohn Marino 	 * how to handle the case where both ends initiate at the
18263ff40c12SJohn Marino 	 * same time? */
18273ff40c12SJohn Marino 	if (existing_peer) {
18283ff40c12SJohn Marino 		if (peer->tpk_success) {
18293ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
18303ff40c12SJohn Marino 				   "direct link is enabled - tear down the "
18313ff40c12SJohn Marino 				   "old link first");
1832*a1157835SDaniel Fojt 			wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
1833*a1157835SDaniel Fojt 			wpa_tdls_peer_clear(sm, peer);
1834*a1157835SDaniel Fojt 		} else if (peer->initiator) {
18353ff40c12SJohn Marino 			/*
1836*a1157835SDaniel Fojt 			 * An entry is already present, so check if we already
1837*a1157835SDaniel Fojt 			 * sent a TDLS Setup Request. If so, compare MAC
1838*a1157835SDaniel Fojt 			 * addresses and let the STA with the lower MAC address
1839*a1157835SDaniel Fojt 			 * continue as the initiator. The other negotiation is
1840*a1157835SDaniel Fojt 			 * terminated.
18413ff40c12SJohn Marino 			 */
18423ff40c12SJohn Marino 			if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
18433ff40c12SJohn Marino 				wpa_printf(MSG_DEBUG, "TDLS: Discard request "
18443ff40c12SJohn Marino 					   "from peer with higher address "
18453ff40c12SJohn Marino 					   MACSTR, MAC2STR(src_addr));
18463ff40c12SJohn Marino 				return -1;
18473ff40c12SJohn Marino 			} else {
18483ff40c12SJohn Marino 				wpa_printf(MSG_DEBUG, "TDLS: Accept request "
18493ff40c12SJohn Marino 					   "from peer with lower address "
18503ff40c12SJohn Marino 					   MACSTR " (terminate previously "
18513ff40c12SJohn Marino 					   "initiated negotiation",
18523ff40c12SJohn Marino 					   MAC2STR(src_addr));
1853*a1157835SDaniel Fojt 				wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
1854*a1157835SDaniel Fojt 						 peer->addr);
1855*a1157835SDaniel Fojt 				wpa_tdls_peer_clear(sm, peer);
18563ff40c12SJohn Marino 			}
18573ff40c12SJohn Marino 		}
18583ff40c12SJohn Marino 	}
18593ff40c12SJohn Marino 
18603ff40c12SJohn Marino 	/* capability information */
18613ff40c12SJohn Marino 	peer->capability = WPA_GET_LE16(cpos);
18623ff40c12SJohn Marino 	cpos += 2;
18633ff40c12SJohn Marino 
18643ff40c12SJohn Marino 	ielen = len - (cpos - buf); /* start of IE in buf */
1865*a1157835SDaniel Fojt 
1866*a1157835SDaniel Fojt 	/*
1867*a1157835SDaniel Fojt 	 * Don't reject the message if failing to parse IEs. The IEs we need are
1868*a1157835SDaniel Fojt 	 * explicitly checked below. Some APs may add arbitrary padding to the
1869*a1157835SDaniel Fojt 	 * end of short TDLS frames and that would look like invalid IEs.
1870*a1157835SDaniel Fojt 	 */
1871*a1157835SDaniel Fojt 	if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0)
1872*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
1873*a1157835SDaniel Fojt 			   "TDLS: Failed to parse IEs in TPK M1 - ignore as an interop workaround");
18743ff40c12SJohn Marino 
18753ff40c12SJohn Marino 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
18763ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
18773ff40c12SJohn Marino 			   "TPK M1");
18783ff40c12SJohn Marino 		goto error;
18793ff40c12SJohn Marino 	}
18803ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1",
18813ff40c12SJohn Marino 		    kde.lnkid, kde.lnkid_len);
18823ff40c12SJohn Marino 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
18833ff40c12SJohn Marino 	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
18843ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS");
1885*a1157835SDaniel Fojt 		status = WLAN_STATUS_REQUEST_DECLINED;
18863ff40c12SJohn Marino 		goto error;
18873ff40c12SJohn Marino 	}
18883ff40c12SJohn Marino 
18893ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
18903ff40c12SJohn Marino 		   MAC2STR(src_addr));
18913ff40c12SJohn Marino 
18923ff40c12SJohn Marino 	if (copy_supp_rates(&kde, peer) < 0)
18933ff40c12SJohn Marino 		goto error;
18943ff40c12SJohn Marino 
18953ff40c12SJohn Marino 	if (copy_peer_ht_capab(&kde, peer) < 0)
18963ff40c12SJohn Marino 		goto error;
18973ff40c12SJohn Marino 
18983ff40c12SJohn Marino 	if (copy_peer_vht_capab(&kde, peer) < 0)
18993ff40c12SJohn Marino 		goto error;
19003ff40c12SJohn Marino 
19013ff40c12SJohn Marino 	if (copy_peer_ext_capab(&kde, peer) < 0)
19023ff40c12SJohn Marino 		goto error;
19033ff40c12SJohn Marino 
19043ff40c12SJohn Marino 	if (copy_peer_supp_channels(&kde, peer) < 0)
19053ff40c12SJohn Marino 		goto error;
19063ff40c12SJohn Marino 
19073ff40c12SJohn Marino 	if (copy_peer_supp_oper_classes(&kde, peer) < 0)
19083ff40c12SJohn Marino 		goto error;
19093ff40c12SJohn Marino 
19103ff40c12SJohn Marino 	peer->qos_info = kde.qosinfo;
19113ff40c12SJohn Marino 
1912*a1157835SDaniel Fojt 	/* Overwrite with the qos_info obtained in WMM IE */
1913*a1157835SDaniel Fojt 	if (copy_peer_wmm_capab(&kde, peer) < 0)
1914*a1157835SDaniel Fojt 		goto error;
1915*a1157835SDaniel Fojt 
19163ff40c12SJohn Marino 	peer->aid = kde.aid;
19173ff40c12SJohn Marino 
19183ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
19193ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
19203ff40c12SJohn Marino 		peer = wpa_tdls_add_peer(sm, src_addr, NULL);
19213ff40c12SJohn Marino 		if (peer == NULL)
19223ff40c12SJohn Marino 			goto error;
19233ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of "
19243ff40c12SJohn Marino 			   "TDLS setup - send own request");
19253ff40c12SJohn Marino 		peer->initiator = 1;
1926*a1157835SDaniel Fojt 		wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
1927*a1157835SDaniel Fojt 					NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0);
19283ff40c12SJohn Marino 		wpa_tdls_send_tpk_m1(sm, peer);
19293ff40c12SJohn Marino 	}
19303ff40c12SJohn Marino 
19313ff40c12SJohn Marino 	if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
19323ff40c12SJohn Marino 	    tdls_prohibited) {
19333ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
19343ff40c12SJohn Marino 			   "on TDLS");
19353ff40c12SJohn Marino 		tdls_prohibited = 0;
19363ff40c12SJohn Marino 	}
19373ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
19383ff40c12SJohn Marino 
19393ff40c12SJohn Marino 	if (tdls_prohibited) {
19403ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS");
19413ff40c12SJohn Marino 		status = WLAN_STATUS_REQUEST_DECLINED;
19423ff40c12SJohn Marino 		goto error;
19433ff40c12SJohn Marino 	}
19443ff40c12SJohn Marino 
19453ff40c12SJohn Marino 	if (!wpa_tdls_get_privacy(sm)) {
19463ff40c12SJohn Marino 		if (kde.rsn_ie) {
19473ff40c12SJohn Marino 			wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while "
19483ff40c12SJohn Marino 				   "security is disabled");
19493ff40c12SJohn Marino 			status = WLAN_STATUS_SECURITY_DISABLED;
19503ff40c12SJohn Marino 			goto error;
19513ff40c12SJohn Marino 		}
19523ff40c12SJohn Marino 		goto skip_rsn;
19533ff40c12SJohn Marino 	}
19543ff40c12SJohn Marino 
19553ff40c12SJohn Marino 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
19563ff40c12SJohn Marino 	    kde.rsn_ie == NULL) {
19573ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1");
19583ff40c12SJohn Marino 		status = WLAN_STATUS_INVALID_PARAMETERS;
19593ff40c12SJohn Marino 		goto error;
19603ff40c12SJohn Marino 	}
19613ff40c12SJohn Marino 
19623ff40c12SJohn Marino 	if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
19633ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in "
19643ff40c12SJohn Marino 			   "TPK M1");
19653ff40c12SJohn Marino 		status = WLAN_STATUS_INVALID_RSNIE;
19663ff40c12SJohn Marino 		goto error;
19673ff40c12SJohn Marino 	}
19683ff40c12SJohn Marino 
19693ff40c12SJohn Marino 	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
19703ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1");
19713ff40c12SJohn Marino 		status = WLAN_STATUS_INVALID_RSNIE;
19723ff40c12SJohn Marino 		goto error;
19733ff40c12SJohn Marino 	}
19743ff40c12SJohn Marino 
19753ff40c12SJohn Marino 	cipher = ie.pairwise_cipher;
19763ff40c12SJohn Marino 	if (cipher & WPA_CIPHER_CCMP) {
19773ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
19783ff40c12SJohn Marino 		cipher = WPA_CIPHER_CCMP;
19793ff40c12SJohn Marino 	} else {
19803ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1");
19813ff40c12SJohn Marino 		status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
19823ff40c12SJohn Marino 		goto error;
19833ff40c12SJohn Marino 	}
19843ff40c12SJohn Marino 
19853ff40c12SJohn Marino 	if ((ie.capabilities &
19863ff40c12SJohn Marino 	     (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) !=
19873ff40c12SJohn Marino 	    WPA_CAPABILITY_PEERKEY_ENABLED) {
19883ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in "
19893ff40c12SJohn Marino 			   "TPK M1");
19903ff40c12SJohn Marino 		status = WLAN_STATUS_INVALID_RSN_IE_CAPAB;
19913ff40c12SJohn Marino 		goto error;
19923ff40c12SJohn Marino 	}
19933ff40c12SJohn Marino 
19943ff40c12SJohn Marino 	/* Lifetime */
19953ff40c12SJohn Marino 	if (kde.key_lifetime == NULL) {
19963ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1");
19973ff40c12SJohn Marino 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
19983ff40c12SJohn Marino 		goto error;
19993ff40c12SJohn Marino 	}
20003ff40c12SJohn Marino 	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
20013ff40c12SJohn Marino 	lifetime = WPA_GET_LE32(timeoutie->value);
20023ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime);
20033ff40c12SJohn Marino 	if (lifetime < 300) {
20043ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime");
20053ff40c12SJohn Marino 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
20063ff40c12SJohn Marino 		goto error;
20073ff40c12SJohn Marino 	}
20083ff40c12SJohn Marino 
20093ff40c12SJohn Marino skip_rsn:
20103ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
20113ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
20123ff40c12SJohn Marino 		if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) {
20133ff40c12SJohn Marino 			/*
20143ff40c12SJohn Marino 			 * The request frame from us is going to win, so do not
20153ff40c12SJohn Marino 			 * replace information based on this request frame from
20163ff40c12SJohn Marino 			 * the peer.
20173ff40c12SJohn Marino 			 */
20183ff40c12SJohn Marino 			goto skip_rsn_check;
20193ff40c12SJohn Marino 		}
20203ff40c12SJohn Marino 	}
20213ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
20223ff40c12SJohn Marino 
20233ff40c12SJohn Marino 	peer->initiator = 0; /* Need to check */
20243ff40c12SJohn Marino 	peer->dtoken = dtoken;
20253ff40c12SJohn Marino 
20263ff40c12SJohn Marino 	if (!wpa_tdls_get_privacy(sm)) {
20273ff40c12SJohn Marino 		peer->rsnie_i_len = 0;
20283ff40c12SJohn Marino 		peer->rsnie_p_len = 0;
20293ff40c12SJohn Marino 		peer->cipher = WPA_CIPHER_NONE;
20303ff40c12SJohn Marino 		goto skip_rsn_check;
20313ff40c12SJohn Marino 	}
20323ff40c12SJohn Marino 
20333ff40c12SJohn Marino 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
20343ff40c12SJohn Marino 	os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
20353ff40c12SJohn Marino 	peer->rsnie_i_len = kde.rsn_ie_len;
20363ff40c12SJohn Marino 	peer->cipher = cipher;
20373ff40c12SJohn Marino 
2038*a1157835SDaniel Fojt 	if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0 ||
2039*a1157835SDaniel Fojt 	    !tdls_nonce_set(peer->inonce)) {
20403ff40c12SJohn Marino 		/*
20413ff40c12SJohn Marino 		 * There is no point in updating the RNonce for every obtained
20423ff40c12SJohn Marino 		 * TPK M1 frame (e.g., retransmission due to timeout) with the
20433ff40c12SJohn Marino 		 * same INonce (SNonce in FTIE). However, if the TPK M1 is
20443ff40c12SJohn Marino 		 * retransmitted with a different INonce, update the RNonce
20453ff40c12SJohn Marino 		 * since this is for a new TDLS session.
20463ff40c12SJohn Marino 		 */
20473ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG,
20483ff40c12SJohn Marino 			   "TDLS: New TPK M1 INonce - generate new RNonce");
20493ff40c12SJohn Marino 		os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN);
20503ff40c12SJohn Marino 		if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
20513ff40c12SJohn Marino 			wpa_msg(sm->ctx->ctx, MSG_WARNING,
20523ff40c12SJohn Marino 				"TDLS: Failed to get random data for responder nonce");
20533ff40c12SJohn Marino 			goto error;
20543ff40c12SJohn Marino 		}
2055*a1157835SDaniel Fojt 		peer->tk_set = 0; /* A new nonce results in a new TK */
20563ff40c12SJohn Marino 	}
20573ff40c12SJohn Marino 
20583ff40c12SJohn Marino #if 0
20593ff40c12SJohn Marino 	/* get version info from RSNIE received from Peer */
20603ff40c12SJohn Marino 	hdr = (struct rsn_ie_hdr *) kde.rsn_ie;
20613ff40c12SJohn Marino 	rsn_ver = WPA_GET_LE16(hdr->version);
20623ff40c12SJohn Marino 
20633ff40c12SJohn Marino 	/* use min(peer's version, out version) */
20643ff40c12SJohn Marino 	if (rsn_ver > RSN_VERSION)
20653ff40c12SJohn Marino 		rsn_ver = RSN_VERSION;
20663ff40c12SJohn Marino 
20673ff40c12SJohn Marino 	hdr = (struct rsn_ie_hdr *) peer->rsnie_p;
20683ff40c12SJohn Marino 
20693ff40c12SJohn Marino 	hdr->elem_id = WLAN_EID_RSN;
20703ff40c12SJohn Marino 	WPA_PUT_LE16(hdr->version, rsn_ver);
20713ff40c12SJohn Marino 	pos = (u8 *) (hdr + 1);
20723ff40c12SJohn Marino 
20733ff40c12SJohn Marino 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
20743ff40c12SJohn Marino 	pos += RSN_SELECTOR_LEN;
20753ff40c12SJohn Marino 	/* Include only the selected cipher in pairwise cipher suite */
20763ff40c12SJohn Marino 	WPA_PUT_LE16(pos, 1);
20773ff40c12SJohn Marino 	pos += 2;
20783ff40c12SJohn Marino 	if (cipher == WPA_CIPHER_CCMP)
20793ff40c12SJohn Marino 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
20803ff40c12SJohn Marino 	pos += RSN_SELECTOR_LEN;
20813ff40c12SJohn Marino 
20823ff40c12SJohn Marino 	WPA_PUT_LE16(pos, 1);
20833ff40c12SJohn Marino 	pos += 2;
20843ff40c12SJohn Marino 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
20853ff40c12SJohn Marino 	pos += RSN_SELECTOR_LEN;
20863ff40c12SJohn Marino 
20873ff40c12SJohn Marino 	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
20883ff40c12SJohn Marino 	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
20893ff40c12SJohn Marino 	WPA_PUT_LE16(pos, rsn_capab);
20903ff40c12SJohn Marino 	pos += 2;
20913ff40c12SJohn Marino 
20923ff40c12SJohn Marino 	hdr->len = (pos - peer->rsnie_p) - 2;
20933ff40c12SJohn Marino 	peer->rsnie_p_len = pos - peer->rsnie_p;
20943ff40c12SJohn Marino #endif
20953ff40c12SJohn Marino 
20963ff40c12SJohn Marino 	/* temp fix: validation of RSNIE later */
20973ff40c12SJohn Marino 	os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len);
20983ff40c12SJohn Marino 	peer->rsnie_p_len = peer->rsnie_i_len;
20993ff40c12SJohn Marino 
21003ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
21013ff40c12SJohn Marino 		    peer->rsnie_p, peer->rsnie_p_len);
21023ff40c12SJohn Marino 
21033ff40c12SJohn Marino 	peer->lifetime = lifetime;
21043ff40c12SJohn Marino 
21053ff40c12SJohn Marino 	wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
21063ff40c12SJohn Marino 
21073ff40c12SJohn Marino skip_rsn_check:
2108*a1157835SDaniel Fojt #ifdef CONFIG_TDLS_TESTING
2109*a1157835SDaniel Fojt 	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT)
2110*a1157835SDaniel Fojt 		goto skip_add_peer;
2111*a1157835SDaniel Fojt #endif /* CONFIG_TDLS_TESTING */
2112*a1157835SDaniel Fojt 
2113*a1157835SDaniel Fojt 	/* add supported rates, capabilities, and qos_info to the TDLS peer */
2114*a1157835SDaniel Fojt 	if (wpa_tdls_addset_peer(sm, peer, 1) < 0)
2115*a1157835SDaniel Fojt 		goto error;
2116*a1157835SDaniel Fojt 
2117*a1157835SDaniel Fojt #ifdef CONFIG_TDLS_TESTING
2118*a1157835SDaniel Fojt skip_add_peer:
2119*a1157835SDaniel Fojt #endif /* CONFIG_TDLS_TESTING */
21203ff40c12SJohn Marino 	peer->tpk_in_progress = 1;
21213ff40c12SJohn Marino 
21223ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
21233ff40c12SJohn Marino 	if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
2124*a1157835SDaniel Fojt 		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
21253ff40c12SJohn Marino 		goto error;
21263ff40c12SJohn Marino 	}
21273ff40c12SJohn Marino 
2128*a1157835SDaniel Fojt #ifdef CONFIG_TDLS_TESTING
2129*a1157835SDaniel Fojt 	if (tdls_testing & TDLS_TESTING_DOUBLE_TPK_M2) {
2130*a1157835SDaniel Fojt 		wpa_printf(MSG_INFO, "TDLS: Testing - Send another TPK M2");
2131*a1157835SDaniel Fojt 		wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer);
2132*a1157835SDaniel Fojt 	}
2133*a1157835SDaniel Fojt #endif /* CONFIG_TDLS_TESTING */
2134*a1157835SDaniel Fojt 
21353ff40c12SJohn Marino 	return 0;
21363ff40c12SJohn Marino 
21373ff40c12SJohn Marino error:
2138*a1157835SDaniel Fojt 	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
21393ff40c12SJohn Marino 			    status);
2140*a1157835SDaniel Fojt 	if (peer)
2141*a1157835SDaniel Fojt 		wpa_tdls_peer_free(sm, peer);
21423ff40c12SJohn Marino 	return -1;
21433ff40c12SJohn Marino }
21443ff40c12SJohn Marino 
21453ff40c12SJohn Marino 
wpa_tdls_enable_link(struct wpa_sm * sm,struct wpa_tdls_peer * peer)21463ff40c12SJohn Marino static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
21473ff40c12SJohn Marino {
21483ff40c12SJohn Marino 	peer->tpk_success = 1;
21493ff40c12SJohn Marino 	peer->tpk_in_progress = 0;
21503ff40c12SJohn Marino 	eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
21513ff40c12SJohn Marino 	if (wpa_tdls_get_privacy(sm)) {
21523ff40c12SJohn Marino 		u32 lifetime = peer->lifetime;
21533ff40c12SJohn Marino 		/*
21543ff40c12SJohn Marino 		 * Start the initiator process a bit earlier to avoid race
21553ff40c12SJohn Marino 		 * condition with the responder sending teardown request.
21563ff40c12SJohn Marino 		 */
21573ff40c12SJohn Marino 		if (lifetime > 3 && peer->initiator)
21583ff40c12SJohn Marino 			lifetime -= 3;
21593ff40c12SJohn Marino 		eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout,
21603ff40c12SJohn Marino 				       sm, peer);
21613ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
21623ff40c12SJohn Marino 		if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) {
2163*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG,
2164*a1157835SDaniel Fojt 				   "TDLS: Testing - disable TPK expiration");
21653ff40c12SJohn Marino 			eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
21663ff40c12SJohn Marino 		}
21673ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
21683ff40c12SJohn Marino 	}
21693ff40c12SJohn Marino 
21703ff40c12SJohn Marino 	if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) {
21713ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Could not configure key to the "
21723ff40c12SJohn Marino 			   "driver");
21733ff40c12SJohn Marino 		return -1;
21743ff40c12SJohn Marino 	}
21753ff40c12SJohn Marino 	peer->reconfig_key = 0;
21763ff40c12SJohn Marino 
21773ff40c12SJohn Marino 	return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr);
21783ff40c12SJohn Marino }
21793ff40c12SJohn Marino 
21803ff40c12SJohn Marino 
wpa_tdls_process_tpk_m2(struct wpa_sm * sm,const u8 * src_addr,const u8 * buf,size_t len)21813ff40c12SJohn Marino static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
21823ff40c12SJohn Marino 				   const u8 *buf, size_t len)
21833ff40c12SJohn Marino {
21843ff40c12SJohn Marino 	struct wpa_tdls_peer *peer;
21853ff40c12SJohn Marino 	struct wpa_eapol_ie_parse kde;
21863ff40c12SJohn Marino 	struct wpa_ie_data ie;
21873ff40c12SJohn Marino 	int cipher;
21883ff40c12SJohn Marino 	struct wpa_tdls_ftie *ftie;
21893ff40c12SJohn Marino 	struct wpa_tdls_timeoutie *timeoutie;
21903ff40c12SJohn Marino 	struct wpa_tdls_lnkid *lnkid;
21913ff40c12SJohn Marino 	u32 lifetime;
21923ff40c12SJohn Marino 	u8 dtoken;
21933ff40c12SJohn Marino 	int ielen;
21943ff40c12SJohn Marino 	u16 status;
21953ff40c12SJohn Marino 	const u8 *pos;
21963ff40c12SJohn Marino 	int ret = 0;
21973ff40c12SJohn Marino 
21983ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
21993ff40c12SJohn Marino 		   "(Peer " MACSTR ")", MAC2STR(src_addr));
22003ff40c12SJohn Marino 	for (peer = sm->tdls; peer; peer = peer->next) {
22013ff40c12SJohn Marino 		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
22023ff40c12SJohn Marino 			break;
22033ff40c12SJohn Marino 	}
22043ff40c12SJohn Marino 	if (peer == NULL) {
22053ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
22063ff40c12SJohn Marino 			   "TPK M2: " MACSTR, MAC2STR(src_addr));
22073ff40c12SJohn Marino 		return -1;
22083ff40c12SJohn Marino 	}
22093ff40c12SJohn Marino 	if (!peer->initiator) {
22103ff40c12SJohn Marino 		/*
22113ff40c12SJohn Marino 		 * This may happen if both devices try to initiate TDLS at the
22123ff40c12SJohn Marino 		 * same time and we accept the TPK M1 from the peer in
22133ff40c12SJohn Marino 		 * wpa_tdls_process_tpk_m1() and clear our previous state.
22143ff40c12SJohn Marino 		 */
22153ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so "
22163ff40c12SJohn Marino 			   "ignore TPK M2 from " MACSTR, MAC2STR(src_addr));
22173ff40c12SJohn Marino 		return -1;
22183ff40c12SJohn Marino 	}
2219*a1157835SDaniel Fojt 
2220*a1157835SDaniel Fojt 	if (peer->tpk_success) {
2221*a1157835SDaniel Fojt 		wpa_printf(MSG_INFO, "TDLS: Ignore incoming TPK M2 retry, from "
2222*a1157835SDaniel Fojt 			   MACSTR " as TPK M3 was already sent",
2223*a1157835SDaniel Fojt 			   MAC2STR(src_addr));
2224*a1157835SDaniel Fojt 		return 0;
2225*a1157835SDaniel Fojt 	}
2226*a1157835SDaniel Fojt 
22273ff40c12SJohn Marino 	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
22283ff40c12SJohn Marino 
22293ff40c12SJohn Marino 	if (len < 3 + 2 + 1) {
22303ff40c12SJohn Marino 		wpa_tdls_disable_peer_link(sm, peer);
22313ff40c12SJohn Marino 		return -1;
22323ff40c12SJohn Marino 	}
22333ff40c12SJohn Marino 
22343ff40c12SJohn Marino 	pos = buf;
22353ff40c12SJohn Marino 	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
22363ff40c12SJohn Marino 	status = WPA_GET_LE16(pos);
22373ff40c12SJohn Marino 	pos += 2 /* status code */;
22383ff40c12SJohn Marino 
22393ff40c12SJohn Marino 	if (status != WLAN_STATUS_SUCCESS) {
22403ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u",
22413ff40c12SJohn Marino 			   status);
22423ff40c12SJohn Marino 		wpa_tdls_disable_peer_link(sm, peer);
22433ff40c12SJohn Marino 		return -1;
22443ff40c12SJohn Marino 	}
22453ff40c12SJohn Marino 
22463ff40c12SJohn Marino 	status = WLAN_STATUS_UNSPECIFIED_FAILURE;
22473ff40c12SJohn Marino 
22483ff40c12SJohn Marino 	/* TODO: need to verify dialog token matches here or in kernel */
22493ff40c12SJohn Marino 	dtoken = *pos++; /* dialog token */
22503ff40c12SJohn Marino 
22513ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken);
22523ff40c12SJohn Marino 
22533ff40c12SJohn Marino 	if (len < 3 + 2 + 1 + 2) {
22543ff40c12SJohn Marino 		wpa_tdls_disable_peer_link(sm, peer);
22553ff40c12SJohn Marino 		return -1;
22563ff40c12SJohn Marino 	}
22573ff40c12SJohn Marino 
22583ff40c12SJohn Marino 	/* capability information */
22593ff40c12SJohn Marino 	peer->capability = WPA_GET_LE16(pos);
22603ff40c12SJohn Marino 	pos += 2;
22613ff40c12SJohn Marino 
22623ff40c12SJohn Marino 	ielen = len - (pos - buf); /* start of IE in buf */
2263*a1157835SDaniel Fojt 
2264*a1157835SDaniel Fojt 	/*
2265*a1157835SDaniel Fojt 	 * Don't reject the message if failing to parse IEs. The IEs we need are
2266*a1157835SDaniel Fojt 	 * explicitly checked below. Some APs may add arbitrary padding to the
2267*a1157835SDaniel Fojt 	 * end of short TDLS frames and that would look like invalid IEs.
2268*a1157835SDaniel Fojt 	 */
2269*a1157835SDaniel Fojt 	if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0)
2270*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
2271*a1157835SDaniel Fojt 			   "TDLS: Failed to parse IEs in TPK M2 - ignore as an interop workaround");
22723ff40c12SJohn Marino 
22733ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
22743ff40c12SJohn Marino 	if (tdls_testing & TDLS_TESTING_DECLINE_RESP) {
22753ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response");
22763ff40c12SJohn Marino 		status = WLAN_STATUS_REQUEST_DECLINED;
22773ff40c12SJohn Marino 		goto error;
22783ff40c12SJohn Marino 	}
22793ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
22803ff40c12SJohn Marino 
22813ff40c12SJohn Marino 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
22823ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
22833ff40c12SJohn Marino 			   "TPK M2");
22843ff40c12SJohn Marino 		goto error;
22853ff40c12SJohn Marino 	}
22863ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2",
22873ff40c12SJohn Marino 		    kde.lnkid, kde.lnkid_len);
22883ff40c12SJohn Marino 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
22893ff40c12SJohn Marino 
22903ff40c12SJohn Marino 	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
22913ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
22923ff40c12SJohn Marino 		status = WLAN_STATUS_NOT_IN_SAME_BSS;
22933ff40c12SJohn Marino 		goto error;
22943ff40c12SJohn Marino 	}
22953ff40c12SJohn Marino 
22963ff40c12SJohn Marino 	if (copy_supp_rates(&kde, peer) < 0)
22973ff40c12SJohn Marino 		goto error;
22983ff40c12SJohn Marino 
22993ff40c12SJohn Marino 	if (copy_peer_ht_capab(&kde, peer) < 0)
23003ff40c12SJohn Marino 		goto error;
23013ff40c12SJohn Marino 
23023ff40c12SJohn Marino 	if (copy_peer_vht_capab(&kde, peer) < 0)
23033ff40c12SJohn Marino 		goto error;
23043ff40c12SJohn Marino 
23053ff40c12SJohn Marino 	if (copy_peer_ext_capab(&kde, peer) < 0)
23063ff40c12SJohn Marino 		goto error;
23073ff40c12SJohn Marino 
23083ff40c12SJohn Marino 	if (copy_peer_supp_channels(&kde, peer) < 0)
23093ff40c12SJohn Marino 		goto error;
23103ff40c12SJohn Marino 
23113ff40c12SJohn Marino 	if (copy_peer_supp_oper_classes(&kde, peer) < 0)
23123ff40c12SJohn Marino 		goto error;
23133ff40c12SJohn Marino 
23143ff40c12SJohn Marino 	peer->qos_info = kde.qosinfo;
23153ff40c12SJohn Marino 
2316*a1157835SDaniel Fojt 	/* Overwrite with the qos_info obtained in WMM IE */
2317*a1157835SDaniel Fojt 	if (copy_peer_wmm_capab(&kde, peer) < 0)
2318*a1157835SDaniel Fojt 		goto error;
2319*a1157835SDaniel Fojt 
23203ff40c12SJohn Marino 	peer->aid = kde.aid;
23213ff40c12SJohn Marino 
23223ff40c12SJohn Marino 	if (!wpa_tdls_get_privacy(sm)) {
23233ff40c12SJohn Marino 		peer->rsnie_p_len = 0;
23243ff40c12SJohn Marino 		peer->cipher = WPA_CIPHER_NONE;
23253ff40c12SJohn Marino 		goto skip_rsn;
23263ff40c12SJohn Marino 	}
23273ff40c12SJohn Marino 
23283ff40c12SJohn Marino 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
23293ff40c12SJohn Marino 	    kde.rsn_ie == NULL) {
23303ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2");
23313ff40c12SJohn Marino 		status = WLAN_STATUS_INVALID_PARAMETERS;
23323ff40c12SJohn Marino 		goto error;
23333ff40c12SJohn Marino 	}
23343ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
23353ff40c12SJohn Marino 		    kde.rsn_ie, kde.rsn_ie_len);
23363ff40c12SJohn Marino 
2337*a1157835SDaniel Fojt 	if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
2338*a1157835SDaniel Fojt 		wpa_printf(MSG_INFO,
2339*a1157835SDaniel Fojt 			   "TDLS: Too long Responder RSN IE in TPK M2");
2340*a1157835SDaniel Fojt 		status = WLAN_STATUS_INVALID_RSNIE;
2341*a1157835SDaniel Fojt 		goto error;
2342*a1157835SDaniel Fojt 	}
2343*a1157835SDaniel Fojt 
23443ff40c12SJohn Marino 	/*
23453ff40c12SJohn Marino 	 * FIX: bitwise comparison of RSN IE is not the correct way of
23463ff40c12SJohn Marino 	 * validation this. It can be different, but certain fields must
23473ff40c12SJohn Marino 	 * match. Since we list only a single pairwise cipher in TPK M1, the
23483ff40c12SJohn Marino 	 * memcmp is likely to work in most cases, though.
23493ff40c12SJohn Marino 	 */
23503ff40c12SJohn Marino 	if (kde.rsn_ie_len != peer->rsnie_i_len ||
23513ff40c12SJohn Marino 	    os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) {
23523ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does "
23533ff40c12SJohn Marino 			   "not match with RSN IE used in TPK M1");
23543ff40c12SJohn Marino 		wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1",
23553ff40c12SJohn Marino 			    peer->rsnie_i, peer->rsnie_i_len);
23563ff40c12SJohn Marino 		wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
23573ff40c12SJohn Marino 			    kde.rsn_ie, kde.rsn_ie_len);
23583ff40c12SJohn Marino 		status = WLAN_STATUS_INVALID_RSNIE;
23593ff40c12SJohn Marino 		goto error;
23603ff40c12SJohn Marino 	}
23613ff40c12SJohn Marino 
23623ff40c12SJohn Marino 	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
23633ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2");
23643ff40c12SJohn Marino 		status = WLAN_STATUS_INVALID_RSNIE;
23653ff40c12SJohn Marino 		goto error;
23663ff40c12SJohn Marino 	}
23673ff40c12SJohn Marino 
23683ff40c12SJohn Marino 	cipher = ie.pairwise_cipher;
23693ff40c12SJohn Marino 	if (cipher == WPA_CIPHER_CCMP) {
23703ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
23713ff40c12SJohn Marino 		cipher = WPA_CIPHER_CCMP;
23723ff40c12SJohn Marino 	} else {
23733ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2");
23743ff40c12SJohn Marino 		status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
23753ff40c12SJohn Marino 		goto error;
23763ff40c12SJohn Marino 	}
23773ff40c12SJohn Marino 
23783ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2",
23793ff40c12SJohn Marino 		    kde.ftie, sizeof(*ftie));
23803ff40c12SJohn Marino 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
23813ff40c12SJohn Marino 
2382*a1157835SDaniel Fojt 	if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) {
23833ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does "
23843ff40c12SJohn Marino 			   "not match with FTIE SNonce used in TPK M1");
23853ff40c12SJohn Marino 		/* Silently discard the frame */
23863ff40c12SJohn Marino 		return -1;
23873ff40c12SJohn Marino 	}
23883ff40c12SJohn Marino 
23893ff40c12SJohn Marino 	/* Responder Nonce and RSN IE */
23903ff40c12SJohn Marino 	os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN);
23913ff40c12SJohn Marino 	os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len);
23923ff40c12SJohn Marino 	peer->rsnie_p_len = kde.rsn_ie_len;
23933ff40c12SJohn Marino 	peer->cipher = cipher;
23943ff40c12SJohn Marino 
23953ff40c12SJohn Marino 	/* Lifetime */
23963ff40c12SJohn Marino 	if (kde.key_lifetime == NULL) {
23973ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2");
23983ff40c12SJohn Marino 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
23993ff40c12SJohn Marino 		goto error;
24003ff40c12SJohn Marino 	}
24013ff40c12SJohn Marino 	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
24023ff40c12SJohn Marino 	lifetime = WPA_GET_LE32(timeoutie->value);
24033ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2",
24043ff40c12SJohn Marino 		   lifetime);
24053ff40c12SJohn Marino 	if (lifetime != peer->lifetime) {
24063ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
24073ff40c12SJohn Marino 			   "TPK M2 (expected %u)", lifetime, peer->lifetime);
24083ff40c12SJohn Marino 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
24093ff40c12SJohn Marino 		goto error;
24103ff40c12SJohn Marino 	}
24113ff40c12SJohn Marino 
24123ff40c12SJohn Marino 	wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
24133ff40c12SJohn Marino 
24143ff40c12SJohn Marino 	/* Process MIC check to see if TPK M2 is right */
24153ff40c12SJohn Marino 	if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
24163ff40c12SJohn Marino 					   (u8 *) timeoutie, ftie) < 0) {
24173ff40c12SJohn Marino 		/* Discard the frame */
24183ff40c12SJohn Marino 		wpa_tdls_del_key(sm, peer);
24193ff40c12SJohn Marino 		wpa_tdls_disable_peer_link(sm, peer);
24203ff40c12SJohn Marino 		return -1;
24213ff40c12SJohn Marino 	}
24223ff40c12SJohn Marino 
24233ff40c12SJohn Marino 	if (wpa_tdls_set_key(sm, peer) < 0) {
24243ff40c12SJohn Marino 		/*
24253ff40c12SJohn Marino 		 * Some drivers may not be able to config the key prior to full
24263ff40c12SJohn Marino 		 * STA entry having been configured.
24273ff40c12SJohn Marino 		 */
24283ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
24293ff40c12SJohn Marino 			   "STA entry is complete");
24303ff40c12SJohn Marino 		peer->reconfig_key = 1;
24313ff40c12SJohn Marino 	}
24323ff40c12SJohn Marino 
24333ff40c12SJohn Marino skip_rsn:
24343ff40c12SJohn Marino 	peer->dtoken = dtoken;
24353ff40c12SJohn Marino 
2436*a1157835SDaniel Fojt 	/* add supported rates, capabilities, and qos_info to the TDLS peer */
2437*a1157835SDaniel Fojt 	if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
2438*a1157835SDaniel Fojt 		goto error;
2439*a1157835SDaniel Fojt 
24403ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
24413ff40c12SJohn Marino 		   "TPK Handshake Message 3");
2442*a1157835SDaniel Fojt 	if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
2443*a1157835SDaniel Fojt 		goto error_no_msg;
24443ff40c12SJohn Marino 
24453ff40c12SJohn Marino 	if (!peer->tpk_success) {
24463ff40c12SJohn Marino 		/*
24473ff40c12SJohn Marino 		 * Enable Link only when tpk_success is 0, signifying that this
24483ff40c12SJohn Marino 		 * processing of TPK M2 frame is not because of a retransmission
24493ff40c12SJohn Marino 		 * during TDLS setup handshake.
24503ff40c12SJohn Marino 		 */
24513ff40c12SJohn Marino 		ret = wpa_tdls_enable_link(sm, peer);
24523ff40c12SJohn Marino 		if (ret < 0) {
24533ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
24543ff40c12SJohn Marino 			wpa_tdls_do_teardown(
24553ff40c12SJohn Marino 				sm, peer,
24563ff40c12SJohn Marino 				WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
24573ff40c12SJohn Marino 		}
24583ff40c12SJohn Marino 	}
24593ff40c12SJohn Marino 	return ret;
24603ff40c12SJohn Marino 
24613ff40c12SJohn Marino error:
2462*a1157835SDaniel Fojt 	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1,
24633ff40c12SJohn Marino 			    status);
2464*a1157835SDaniel Fojt error_no_msg:
24653ff40c12SJohn Marino 	wpa_tdls_disable_peer_link(sm, peer);
24663ff40c12SJohn Marino 	return -1;
24673ff40c12SJohn Marino }
24683ff40c12SJohn Marino 
24693ff40c12SJohn Marino 
wpa_tdls_process_tpk_m3(struct wpa_sm * sm,const u8 * src_addr,const u8 * buf,size_t len)24703ff40c12SJohn Marino static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
24713ff40c12SJohn Marino 				   const u8 *buf, size_t len)
24723ff40c12SJohn Marino {
24733ff40c12SJohn Marino 	struct wpa_tdls_peer *peer;
24743ff40c12SJohn Marino 	struct wpa_eapol_ie_parse kde;
24753ff40c12SJohn Marino 	struct wpa_tdls_ftie *ftie;
24763ff40c12SJohn Marino 	struct wpa_tdls_timeoutie *timeoutie;
24773ff40c12SJohn Marino 	struct wpa_tdls_lnkid *lnkid;
24783ff40c12SJohn Marino 	int ielen;
24793ff40c12SJohn Marino 	u16 status;
24803ff40c12SJohn Marino 	const u8 *pos;
24813ff40c12SJohn Marino 	u32 lifetime;
24823ff40c12SJohn Marino 	int ret = 0;
24833ff40c12SJohn Marino 
24843ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
24853ff40c12SJohn Marino 		   "(Peer " MACSTR ")", MAC2STR(src_addr));
24863ff40c12SJohn Marino 	for (peer = sm->tdls; peer; peer = peer->next) {
24873ff40c12SJohn Marino 		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
24883ff40c12SJohn Marino 			break;
24893ff40c12SJohn Marino 	}
24903ff40c12SJohn Marino 	if (peer == NULL) {
24913ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
24923ff40c12SJohn Marino 			   "TPK M3: " MACSTR, MAC2STR(src_addr));
24933ff40c12SJohn Marino 		return -1;
24943ff40c12SJohn Marino 	}
24953ff40c12SJohn Marino 	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE);
24963ff40c12SJohn Marino 
24973ff40c12SJohn Marino 	if (len < 3 + 3)
24983ff40c12SJohn Marino 		goto error;
24993ff40c12SJohn Marino 	pos = buf;
25003ff40c12SJohn Marino 	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
25013ff40c12SJohn Marino 
25023ff40c12SJohn Marino 	status = WPA_GET_LE16(pos);
25033ff40c12SJohn Marino 
25043ff40c12SJohn Marino 	if (status != 0) {
25053ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u",
25063ff40c12SJohn Marino 			   status);
25073ff40c12SJohn Marino 		goto error;
25083ff40c12SJohn Marino 	}
25093ff40c12SJohn Marino 	pos += 2 /* status code */ + 1 /* dialog token */;
25103ff40c12SJohn Marino 
25113ff40c12SJohn Marino 	ielen = len - (pos - buf); /* start of IE in buf */
2512*a1157835SDaniel Fojt 
2513*a1157835SDaniel Fojt 	/*
2514*a1157835SDaniel Fojt 	 * Don't reject the message if failing to parse IEs. The IEs we need are
2515*a1157835SDaniel Fojt 	 * explicitly checked below. Some APs piggy-back broken IEs to the end
2516*a1157835SDaniel Fojt 	 * of a TDLS Confirm packet, which will fail the link if we don't ignore
2517*a1157835SDaniel Fojt 	 * this error.
2518*a1157835SDaniel Fojt 	 */
25193ff40c12SJohn Marino 	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
2520*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
2521*a1157835SDaniel Fojt 			   "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
25223ff40c12SJohn Marino 	}
25233ff40c12SJohn Marino 
25243ff40c12SJohn Marino 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
25253ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3");
25263ff40c12SJohn Marino 		goto error;
25273ff40c12SJohn Marino 	}
25283ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3",
25293ff40c12SJohn Marino 		    (u8 *) kde.lnkid, kde.lnkid_len);
25303ff40c12SJohn Marino 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
25313ff40c12SJohn Marino 
25323ff40c12SJohn Marino 	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
25333ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
25343ff40c12SJohn Marino 		goto error;
25353ff40c12SJohn Marino 	}
25363ff40c12SJohn Marino 
25373ff40c12SJohn Marino 	if (!wpa_tdls_get_privacy(sm))
25383ff40c12SJohn Marino 		goto skip_rsn;
25393ff40c12SJohn Marino 
25403ff40c12SJohn Marino 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
25413ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3");
25423ff40c12SJohn Marino 		goto error;
25433ff40c12SJohn Marino 	}
25443ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3",
25453ff40c12SJohn Marino 		    kde.ftie, sizeof(*ftie));
25463ff40c12SJohn Marino 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
25473ff40c12SJohn Marino 
25483ff40c12SJohn Marino 	if (kde.rsn_ie == NULL) {
25493ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3");
25503ff40c12SJohn Marino 		goto error;
25513ff40c12SJohn Marino 	}
25523ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3",
25533ff40c12SJohn Marino 		    kde.rsn_ie, kde.rsn_ie_len);
25543ff40c12SJohn Marino 	if (kde.rsn_ie_len != peer->rsnie_p_len ||
25553ff40c12SJohn Marino 	    os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) {
25563ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match "
25573ff40c12SJohn Marino 			   "with the one sent in TPK M2");
25583ff40c12SJohn Marino 		goto error;
25593ff40c12SJohn Marino 	}
25603ff40c12SJohn Marino 
2561*a1157835SDaniel Fojt 	if (os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) != 0) {
25623ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does "
25633ff40c12SJohn Marino 			   "not match with FTIE ANonce used in TPK M2");
25643ff40c12SJohn Marino 		goto error;
25653ff40c12SJohn Marino 	}
25663ff40c12SJohn Marino 
2567*a1157835SDaniel Fojt 	if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) {
25683ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not "
25693ff40c12SJohn Marino 			   "match with FTIE SNonce used in TPK M1");
25703ff40c12SJohn Marino 		goto error;
25713ff40c12SJohn Marino 	}
25723ff40c12SJohn Marino 
25733ff40c12SJohn Marino 	if (kde.key_lifetime == NULL) {
25743ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
25753ff40c12SJohn Marino 		goto error;
25763ff40c12SJohn Marino 	}
25773ff40c12SJohn Marino 	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
25783ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3",
25793ff40c12SJohn Marino 		    (u8 *) timeoutie, sizeof(*timeoutie));
25803ff40c12SJohn Marino 	lifetime = WPA_GET_LE32(timeoutie->value);
25813ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3",
25823ff40c12SJohn Marino 		   lifetime);
25833ff40c12SJohn Marino 	if (lifetime != peer->lifetime) {
25843ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
25853ff40c12SJohn Marino 			   "TPK M3 (expected %u)", lifetime, peer->lifetime);
25863ff40c12SJohn Marino 		goto error;
25873ff40c12SJohn Marino 	}
25883ff40c12SJohn Marino 
25893ff40c12SJohn Marino 	if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
25903ff40c12SJohn Marino 					   (u8 *) timeoutie, ftie) < 0) {
25913ff40c12SJohn Marino 		wpa_tdls_del_key(sm, peer);
25923ff40c12SJohn Marino 		goto error;
25933ff40c12SJohn Marino 	}
25943ff40c12SJohn Marino 
25953ff40c12SJohn Marino 	if (wpa_tdls_set_key(sm, peer) < 0) {
25963ff40c12SJohn Marino 		/*
25973ff40c12SJohn Marino 		 * Some drivers may not be able to config the key prior to full
25983ff40c12SJohn Marino 		 * STA entry having been configured.
25993ff40c12SJohn Marino 		 */
26003ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
26013ff40c12SJohn Marino 			   "STA entry is complete");
26023ff40c12SJohn Marino 		peer->reconfig_key = 1;
26033ff40c12SJohn Marino 	}
26043ff40c12SJohn Marino 
26053ff40c12SJohn Marino skip_rsn:
2606*a1157835SDaniel Fojt 	/* add supported rates, capabilities, and qos_info to the TDLS peer */
2607*a1157835SDaniel Fojt 	if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
2608*a1157835SDaniel Fojt 		goto error;
2609*a1157835SDaniel Fojt 
26103ff40c12SJohn Marino 	if (!peer->tpk_success) {
26113ff40c12SJohn Marino 		/*
26123ff40c12SJohn Marino 		 * Enable Link only when tpk_success is 0, signifying that this
26133ff40c12SJohn Marino 		 * processing of TPK M3 frame is not because of a retransmission
26143ff40c12SJohn Marino 		 * during TDLS setup handshake.
26153ff40c12SJohn Marino 		 */
26163ff40c12SJohn Marino 		ret = wpa_tdls_enable_link(sm, peer);
26173ff40c12SJohn Marino 		if (ret < 0) {
26183ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
2619*a1157835SDaniel Fojt 			goto error;
26203ff40c12SJohn Marino 		}
26213ff40c12SJohn Marino 	}
26223ff40c12SJohn Marino 	return ret;
26233ff40c12SJohn Marino error:
2624*a1157835SDaniel Fojt 	wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
26253ff40c12SJohn Marino 	return -1;
26263ff40c12SJohn Marino }
26273ff40c12SJohn Marino 
26283ff40c12SJohn Marino 
wpa_add_tdls_timeoutie(u8 * pos,u8 * ie,size_t ie_len,u32 tsecs)26293ff40c12SJohn Marino static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs)
26303ff40c12SJohn Marino {
26313ff40c12SJohn Marino 	struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie;
26323ff40c12SJohn Marino 
26333ff40c12SJohn Marino 	os_memset(lifetime, 0, ie_len);
26343ff40c12SJohn Marino 	lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL;
26353ff40c12SJohn Marino 	lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2;
26363ff40c12SJohn Marino 	lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME;
26373ff40c12SJohn Marino 	WPA_PUT_LE32(lifetime->value, tsecs);
26383ff40c12SJohn Marino 	os_memcpy(pos, ie, ie_len);
26393ff40c12SJohn Marino 	return pos + ie_len;
26403ff40c12SJohn Marino }
26413ff40c12SJohn Marino 
26423ff40c12SJohn Marino 
26433ff40c12SJohn Marino /**
26443ff40c12SJohn Marino  * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1)
26453ff40c12SJohn Marino  * @sm: Pointer to WPA state machine data from wpa_sm_init()
26463ff40c12SJohn Marino  * @peer: MAC address of the peer STA
26473ff40c12SJohn Marino  * Returns: 0 on success, or -1 on failure
26483ff40c12SJohn Marino  *
26493ff40c12SJohn Marino  * Send TPK Handshake Message 1 info to driver to start TDLS
26503ff40c12SJohn Marino  * handshake with the peer.
26513ff40c12SJohn Marino  */
wpa_tdls_start(struct wpa_sm * sm,const u8 * addr)26523ff40c12SJohn Marino int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
26533ff40c12SJohn Marino {
26543ff40c12SJohn Marino 	struct wpa_tdls_peer *peer;
26553ff40c12SJohn Marino 	int tdls_prohibited = sm->tdls_prohibited;
26563ff40c12SJohn Marino 
26573ff40c12SJohn Marino 	if (sm->tdls_disabled || !sm->tdls_supported)
26583ff40c12SJohn Marino 		return -1;
26593ff40c12SJohn Marino 
26603ff40c12SJohn Marino #ifdef CONFIG_TDLS_TESTING
26613ff40c12SJohn Marino 	if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
26623ff40c12SJohn Marino 	    tdls_prohibited) {
26633ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
26643ff40c12SJohn Marino 			   "on TDLS");
26653ff40c12SJohn Marino 		tdls_prohibited = 0;
26663ff40c12SJohn Marino 	}
26673ff40c12SJohn Marino #endif /* CONFIG_TDLS_TESTING */
26683ff40c12SJohn Marino 
26693ff40c12SJohn Marino 	if (tdls_prohibited) {
26703ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - "
26713ff40c12SJohn Marino 			   "reject request to start setup");
26723ff40c12SJohn Marino 		return -1;
26733ff40c12SJohn Marino 	}
26743ff40c12SJohn Marino 
26753ff40c12SJohn Marino 	peer = wpa_tdls_add_peer(sm, addr, NULL);
26763ff40c12SJohn Marino 	if (peer == NULL)
26773ff40c12SJohn Marino 		return -1;
26783ff40c12SJohn Marino 
26793ff40c12SJohn Marino 	if (peer->tpk_in_progress) {
26803ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer");
26813ff40c12SJohn Marino 		return 0;
26823ff40c12SJohn Marino 	}
26833ff40c12SJohn Marino 
26843ff40c12SJohn Marino 	peer->initiator = 1;
26853ff40c12SJohn Marino 
26863ff40c12SJohn Marino 	/* add the peer to the driver as a "setup in progress" peer */
2687*a1157835SDaniel Fojt 	if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
2688*a1157835SDaniel Fojt 				    NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0)) {
2689*a1157835SDaniel Fojt 		wpa_tdls_disable_peer_link(sm, peer);
2690*a1157835SDaniel Fojt 		return -1;
2691*a1157835SDaniel Fojt 	}
26923ff40c12SJohn Marino 
26933ff40c12SJohn Marino 	peer->tpk_in_progress = 1;
26943ff40c12SJohn Marino 
26953ff40c12SJohn Marino 	if (wpa_tdls_send_tpk_m1(sm, peer) < 0) {
26963ff40c12SJohn Marino 		wpa_tdls_disable_peer_link(sm, peer);
26973ff40c12SJohn Marino 		return -1;
26983ff40c12SJohn Marino 	}
26993ff40c12SJohn Marino 
27003ff40c12SJohn Marino 	return 0;
27013ff40c12SJohn Marino }
27023ff40c12SJohn Marino 
27033ff40c12SJohn Marino 
wpa_tdls_remove(struct wpa_sm * sm,const u8 * addr)27043ff40c12SJohn Marino void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr)
27053ff40c12SJohn Marino {
27063ff40c12SJohn Marino 	struct wpa_tdls_peer *peer;
27073ff40c12SJohn Marino 
27083ff40c12SJohn Marino 	if (sm->tdls_disabled || !sm->tdls_supported)
27093ff40c12SJohn Marino 		return;
27103ff40c12SJohn Marino 
27113ff40c12SJohn Marino 	for (peer = sm->tdls; peer; peer = peer->next) {
27123ff40c12SJohn Marino 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
27133ff40c12SJohn Marino 			break;
27143ff40c12SJohn Marino 	}
27153ff40c12SJohn Marino 
27163ff40c12SJohn Marino 	if (peer == NULL || !peer->tpk_success)
27173ff40c12SJohn Marino 		return;
27183ff40c12SJohn Marino 
27193ff40c12SJohn Marino 	if (sm->tdls_external_setup) {
27203ff40c12SJohn Marino 		/*
27213ff40c12SJohn Marino 		 * Disable previous link to allow renegotiation to be completed
27223ff40c12SJohn Marino 		 * on AP path.
27233ff40c12SJohn Marino 		 */
2724*a1157835SDaniel Fojt 		wpa_tdls_do_teardown(sm, peer,
2725*a1157835SDaniel Fojt 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
27263ff40c12SJohn Marino 	}
27273ff40c12SJohn Marino }
27283ff40c12SJohn Marino 
27293ff40c12SJohn Marino 
27303ff40c12SJohn Marino /**
27313ff40c12SJohn Marino  * wpa_supplicant_rx_tdls - Receive TDLS data frame
27323ff40c12SJohn Marino  *
27333ff40c12SJohn Marino  * This function is called to receive TDLS (ethertype = 0x890d) data frames.
27343ff40c12SJohn Marino  */
wpa_supplicant_rx_tdls(void * ctx,const u8 * src_addr,const u8 * buf,size_t len)27353ff40c12SJohn Marino static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr,
27363ff40c12SJohn Marino 				   const u8 *buf, size_t len)
27373ff40c12SJohn Marino {
27383ff40c12SJohn Marino 	struct wpa_sm *sm = ctx;
27393ff40c12SJohn Marino 	struct wpa_tdls_frame *tf;
27403ff40c12SJohn Marino 
27413ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation",
27423ff40c12SJohn Marino 		    buf, len);
27433ff40c12SJohn Marino 
27443ff40c12SJohn Marino 	if (sm->tdls_disabled || !sm->tdls_supported) {
27453ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled "
27463ff40c12SJohn Marino 			   "or unsupported by driver");
27473ff40c12SJohn Marino 		return;
27483ff40c12SJohn Marino 	}
27493ff40c12SJohn Marino 
27503ff40c12SJohn Marino 	if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) {
27513ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message");
27523ff40c12SJohn Marino 		return;
27533ff40c12SJohn Marino 	}
27543ff40c12SJohn Marino 
27553ff40c12SJohn Marino 	if (len < sizeof(*tf)) {
27563ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Drop too short frame");
27573ff40c12SJohn Marino 		return;
27583ff40c12SJohn Marino 	}
27593ff40c12SJohn Marino 
27603ff40c12SJohn Marino 	/* Check to make sure its a valid encapsulated TDLS frame */
27613ff40c12SJohn Marino 	tf = (struct wpa_tdls_frame *) buf;
27623ff40c12SJohn Marino 	if (tf->payloadtype != 2 /* TDLS_RFTYPE */ ||
27633ff40c12SJohn Marino 	    tf->category != WLAN_ACTION_TDLS) {
27643ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u "
27653ff40c12SJohn Marino 			   "category=%u action=%u",
27663ff40c12SJohn Marino 			   tf->payloadtype, tf->category, tf->action);
27673ff40c12SJohn Marino 		return;
27683ff40c12SJohn Marino 	}
27693ff40c12SJohn Marino 
27703ff40c12SJohn Marino 	switch (tf->action) {
27713ff40c12SJohn Marino 	case WLAN_TDLS_SETUP_REQUEST:
27723ff40c12SJohn Marino 		wpa_tdls_process_tpk_m1(sm, src_addr, buf, len);
27733ff40c12SJohn Marino 		break;
27743ff40c12SJohn Marino 	case WLAN_TDLS_SETUP_RESPONSE:
27753ff40c12SJohn Marino 		wpa_tdls_process_tpk_m2(sm, src_addr, buf, len);
27763ff40c12SJohn Marino 		break;
27773ff40c12SJohn Marino 	case WLAN_TDLS_SETUP_CONFIRM:
27783ff40c12SJohn Marino 		wpa_tdls_process_tpk_m3(sm, src_addr, buf, len);
27793ff40c12SJohn Marino 		break;
27803ff40c12SJohn Marino 	case WLAN_TDLS_TEARDOWN:
27813ff40c12SJohn Marino 		wpa_tdls_recv_teardown(sm, src_addr, buf, len);
27823ff40c12SJohn Marino 		break;
27833ff40c12SJohn Marino 	case WLAN_TDLS_DISCOVERY_REQUEST:
27843ff40c12SJohn Marino 		wpa_tdls_process_discovery_request(sm, src_addr, buf, len);
27853ff40c12SJohn Marino 		break;
27863ff40c12SJohn Marino 	default:
27873ff40c12SJohn Marino 		/* Kernel code will process remaining frames */
27883ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u",
27893ff40c12SJohn Marino 			   tf->action);
27903ff40c12SJohn Marino 		break;
27913ff40c12SJohn Marino 	}
27923ff40c12SJohn Marino }
27933ff40c12SJohn Marino 
27943ff40c12SJohn Marino 
27953ff40c12SJohn Marino /**
27963ff40c12SJohn Marino  * wpa_tdls_init - Initialize driver interface parameters for TDLS
27973ff40c12SJohn Marino  * @wpa_s: Pointer to wpa_supplicant data
27983ff40c12SJohn Marino  * Returns: 0 on success, -1 on failure
27993ff40c12SJohn Marino  *
28003ff40c12SJohn Marino  * This function is called to initialize driver interface parameters for TDLS.
28013ff40c12SJohn Marino  * wpa_drv_init() must have been called before this function to initialize the
28023ff40c12SJohn Marino  * driver interface.
28033ff40c12SJohn Marino  */
wpa_tdls_init(struct wpa_sm * sm)28043ff40c12SJohn Marino int wpa_tdls_init(struct wpa_sm *sm)
28053ff40c12SJohn Marino {
28063ff40c12SJohn Marino 	if (sm == NULL)
28073ff40c12SJohn Marino 		return -1;
28083ff40c12SJohn Marino 
28093ff40c12SJohn Marino 	sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
28103ff40c12SJohn Marino 				     sm->ifname,
28113ff40c12SJohn Marino 				     sm->own_addr,
28123ff40c12SJohn Marino 				     ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
28133ff40c12SJohn Marino 				     sm, 0);
28143ff40c12SJohn Marino 	if (sm->l2_tdls == NULL) {
28153ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet "
28163ff40c12SJohn Marino 			   "connection");
28173ff40c12SJohn Marino 		return -1;
28183ff40c12SJohn Marino 	}
28193ff40c12SJohn Marino 
28203ff40c12SJohn Marino 	/*
28213ff40c12SJohn Marino 	 * Drivers that support TDLS but don't implement the get_capa callback
28223ff40c12SJohn Marino 	 * are assumed to perform everything internally
28233ff40c12SJohn Marino 	 */
28243ff40c12SJohn Marino 	if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported,
2825*a1157835SDaniel Fojt 				 &sm->tdls_external_setup,
2826*a1157835SDaniel Fojt 				 &sm->tdls_chan_switch) < 0) {
28273ff40c12SJohn Marino 		sm->tdls_supported = 1;
28283ff40c12SJohn Marino 		sm->tdls_external_setup = 0;
28293ff40c12SJohn Marino 	}
28303ff40c12SJohn Marino 
28313ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by "
28323ff40c12SJohn Marino 		   "driver", sm->tdls_supported ? "" : " not");
28333ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup",
28343ff40c12SJohn Marino 		   sm->tdls_external_setup ? "external" : "internal");
2835*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching",
2836*a1157835SDaniel Fojt 		   sm->tdls_chan_switch ? "supports" : "does not support");
28373ff40c12SJohn Marino 
28383ff40c12SJohn Marino 	return 0;
28393ff40c12SJohn Marino }
28403ff40c12SJohn Marino 
28413ff40c12SJohn Marino 
wpa_tdls_teardown_peers(struct wpa_sm * sm)28423ff40c12SJohn Marino void wpa_tdls_teardown_peers(struct wpa_sm *sm)
28433ff40c12SJohn Marino {
2844*a1157835SDaniel Fojt 	struct wpa_tdls_peer *peer, *tmp;
28453ff40c12SJohn Marino 
2846*a1157835SDaniel Fojt 	if (!sm)
2847*a1157835SDaniel Fojt 		return;
28483ff40c12SJohn Marino 	peer = sm->tdls;
28493ff40c12SJohn Marino 
28503ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
28513ff40c12SJohn Marino 
28523ff40c12SJohn Marino 	while (peer) {
2853*a1157835SDaniel Fojt 		tmp = peer->next;
28543ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR,
28553ff40c12SJohn Marino 			   MAC2STR(peer->addr));
28563ff40c12SJohn Marino 		if (sm->tdls_external_setup)
2857*a1157835SDaniel Fojt 			wpa_tdls_do_teardown(sm, peer,
28583ff40c12SJohn Marino 					     WLAN_REASON_DEAUTH_LEAVING);
28593ff40c12SJohn Marino 		else
28603ff40c12SJohn Marino 			wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
28613ff40c12SJohn Marino 
2862*a1157835SDaniel Fojt 		peer = tmp;
28633ff40c12SJohn Marino 	}
28643ff40c12SJohn Marino }
28653ff40c12SJohn Marino 
28663ff40c12SJohn Marino 
wpa_tdls_remove_peers(struct wpa_sm * sm)28673ff40c12SJohn Marino static void wpa_tdls_remove_peers(struct wpa_sm *sm)
28683ff40c12SJohn Marino {
28693ff40c12SJohn Marino 	struct wpa_tdls_peer *peer, *tmp;
28703ff40c12SJohn Marino 
28713ff40c12SJohn Marino 	peer = sm->tdls;
28723ff40c12SJohn Marino 
28733ff40c12SJohn Marino 	while (peer) {
28743ff40c12SJohn Marino 		int res;
28753ff40c12SJohn Marino 		tmp = peer->next;
28763ff40c12SJohn Marino 		res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
28773ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
28783ff40c12SJohn Marino 			   MAC2STR(peer->addr), res);
28793ff40c12SJohn Marino 		wpa_tdls_peer_free(sm, peer);
28803ff40c12SJohn Marino 		peer = tmp;
28813ff40c12SJohn Marino 	}
28823ff40c12SJohn Marino }
28833ff40c12SJohn Marino 
28843ff40c12SJohn Marino 
28853ff40c12SJohn Marino /**
28863ff40c12SJohn Marino  * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS
28873ff40c12SJohn Marino  *
28883ff40c12SJohn Marino  * This function is called to recover driver interface parameters for TDLS
28893ff40c12SJohn Marino  * and frees resources allocated for it.
28903ff40c12SJohn Marino  */
wpa_tdls_deinit(struct wpa_sm * sm)28913ff40c12SJohn Marino void wpa_tdls_deinit(struct wpa_sm *sm)
28923ff40c12SJohn Marino {
28933ff40c12SJohn Marino 	if (sm == NULL)
28943ff40c12SJohn Marino 		return;
28953ff40c12SJohn Marino 
28963ff40c12SJohn Marino 	if (sm->l2_tdls)
28973ff40c12SJohn Marino 		l2_packet_deinit(sm->l2_tdls);
28983ff40c12SJohn Marino 	sm->l2_tdls = NULL;
28993ff40c12SJohn Marino 
29003ff40c12SJohn Marino 	wpa_tdls_remove_peers(sm);
29013ff40c12SJohn Marino }
29023ff40c12SJohn Marino 
29033ff40c12SJohn Marino 
wpa_tdls_assoc(struct wpa_sm * sm)29043ff40c12SJohn Marino void wpa_tdls_assoc(struct wpa_sm *sm)
29053ff40c12SJohn Marino {
29063ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association");
29073ff40c12SJohn Marino 	wpa_tdls_remove_peers(sm);
29083ff40c12SJohn Marino }
29093ff40c12SJohn Marino 
29103ff40c12SJohn Marino 
wpa_tdls_disassoc(struct wpa_sm * sm)29113ff40c12SJohn Marino void wpa_tdls_disassoc(struct wpa_sm *sm)
29123ff40c12SJohn Marino {
29133ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation");
29143ff40c12SJohn Marino 	wpa_tdls_remove_peers(sm);
29153ff40c12SJohn Marino }
29163ff40c12SJohn Marino 
29173ff40c12SJohn Marino 
wpa_tdls_prohibited(struct ieee802_11_elems * elems)2918*a1157835SDaniel Fojt static int wpa_tdls_prohibited(struct ieee802_11_elems *elems)
29193ff40c12SJohn Marino {
29203ff40c12SJohn Marino 	/* bit 38 - TDLS Prohibited */
2921*a1157835SDaniel Fojt 	return !!(elems->ext_capab[4] & 0x40);
2922*a1157835SDaniel Fojt }
2923*a1157835SDaniel Fojt 
2924*a1157835SDaniel Fojt 
wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems * elems)2925*a1157835SDaniel Fojt static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems)
2926*a1157835SDaniel Fojt {
2927*a1157835SDaniel Fojt 	/* bit 39 - TDLS Channel Switch Prohibited */
2928*a1157835SDaniel Fojt 	return !!(elems->ext_capab[4] & 0x80);
29293ff40c12SJohn Marino }
29303ff40c12SJohn Marino 
29313ff40c12SJohn Marino 
wpa_tdls_ap_ies(struct wpa_sm * sm,const u8 * ies,size_t len)29323ff40c12SJohn Marino void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
29333ff40c12SJohn Marino {
2934*a1157835SDaniel Fojt 	struct ieee802_11_elems elems;
2935*a1157835SDaniel Fojt 
2936*a1157835SDaniel Fojt 	sm->tdls_prohibited = 0;
2937*a1157835SDaniel Fojt 	sm->tdls_chan_switch_prohibited = 0;
2938*a1157835SDaniel Fojt 
2939*a1157835SDaniel Fojt 	if (ies == NULL ||
2940*a1157835SDaniel Fojt 	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
2941*a1157835SDaniel Fojt 	    elems.ext_capab == NULL || elems.ext_capab_len < 5)
2942*a1157835SDaniel Fojt 		return;
2943*a1157835SDaniel Fojt 
2944*a1157835SDaniel Fojt 	sm->tdls_prohibited = wpa_tdls_prohibited(&elems);
29453ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS",
29463ff40c12SJohn Marino 		   sm->tdls_prohibited ? "prohibited" : "allowed");
2947*a1157835SDaniel Fojt 	sm->tdls_chan_switch_prohibited =
2948*a1157835SDaniel Fojt 		wpa_tdls_chan_switch_prohibited(&elems);
2949*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS",
2950*a1157835SDaniel Fojt 		   sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed");
29513ff40c12SJohn Marino }
29523ff40c12SJohn Marino 
29533ff40c12SJohn Marino 
wpa_tdls_assoc_resp_ies(struct wpa_sm * sm,const u8 * ies,size_t len)29543ff40c12SJohn Marino void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
29553ff40c12SJohn Marino {
2956*a1157835SDaniel Fojt 	struct ieee802_11_elems elems;
2957*a1157835SDaniel Fojt 
2958*a1157835SDaniel Fojt 	if (ies == NULL ||
2959*a1157835SDaniel Fojt 	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
2960*a1157835SDaniel Fojt 	    elems.ext_capab == NULL || elems.ext_capab_len < 5)
2961*a1157835SDaniel Fojt 		return;
2962*a1157835SDaniel Fojt 
2963*a1157835SDaniel Fojt 	if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) {
29643ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on "
29653ff40c12SJohn Marino 			   "(Re)Association Response IEs");
29663ff40c12SJohn Marino 		sm->tdls_prohibited = 1;
29673ff40c12SJohn Marino 	}
2968*a1157835SDaniel Fojt 
2969*a1157835SDaniel Fojt 	if (!sm->tdls_chan_switch_prohibited &&
2970*a1157835SDaniel Fojt 	    wpa_tdls_chan_switch_prohibited(&elems)) {
2971*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
2972*a1157835SDaniel Fojt 			   "TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs");
2973*a1157835SDaniel Fojt 		sm->tdls_chan_switch_prohibited = 1;
2974*a1157835SDaniel Fojt 	}
29753ff40c12SJohn Marino }
29763ff40c12SJohn Marino 
29773ff40c12SJohn Marino 
wpa_tdls_enable(struct wpa_sm * sm,int enabled)29783ff40c12SJohn Marino void wpa_tdls_enable(struct wpa_sm *sm, int enabled)
29793ff40c12SJohn Marino {
29803ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled");
29813ff40c12SJohn Marino 	sm->tdls_disabled = !enabled;
29823ff40c12SJohn Marino }
29833ff40c12SJohn Marino 
29843ff40c12SJohn Marino 
wpa_tdls_is_external_setup(struct wpa_sm * sm)29853ff40c12SJohn Marino int wpa_tdls_is_external_setup(struct wpa_sm *sm)
29863ff40c12SJohn Marino {
29873ff40c12SJohn Marino 	return sm->tdls_external_setup;
29883ff40c12SJohn Marino }
2989*a1157835SDaniel Fojt 
2990*a1157835SDaniel Fojt 
wpa_tdls_enable_chan_switch(struct wpa_sm * sm,const u8 * addr,u8 oper_class,struct hostapd_freq_params * freq_params)2991*a1157835SDaniel Fojt int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
2992*a1157835SDaniel Fojt 				u8 oper_class,
2993*a1157835SDaniel Fojt 				struct hostapd_freq_params *freq_params)
2994*a1157835SDaniel Fojt {
2995*a1157835SDaniel Fojt 	struct wpa_tdls_peer *peer;
2996*a1157835SDaniel Fojt 	int ret;
2997*a1157835SDaniel Fojt 
2998*a1157835SDaniel Fojt 	if (sm->tdls_disabled || !sm->tdls_supported)
2999*a1157835SDaniel Fojt 		return -1;
3000*a1157835SDaniel Fojt 
3001*a1157835SDaniel Fojt 	if (!sm->tdls_chan_switch) {
3002*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
3003*a1157835SDaniel Fojt 			   "TDLS: Channel switching not supported by the driver");
3004*a1157835SDaniel Fojt 		return -1;
3005*a1157835SDaniel Fojt 	}
3006*a1157835SDaniel Fojt 
3007*a1157835SDaniel Fojt 	if (sm->tdls_chan_switch_prohibited) {
3008*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
3009*a1157835SDaniel Fojt 			   "TDLS: Channel switching is prohibited in this BSS - reject request to switch channel");
3010*a1157835SDaniel Fojt 		return -1;
3011*a1157835SDaniel Fojt 	}
3012*a1157835SDaniel Fojt 
3013*a1157835SDaniel Fojt 	for (peer = sm->tdls; peer; peer = peer->next) {
3014*a1157835SDaniel Fojt 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
3015*a1157835SDaniel Fojt 			break;
3016*a1157835SDaniel Fojt 	}
3017*a1157835SDaniel Fojt 
3018*a1157835SDaniel Fojt 	if (peer == NULL || !peer->tpk_success) {
3019*a1157835SDaniel Fojt 		wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR
3020*a1157835SDaniel Fojt 			   " not found for channel switching", MAC2STR(addr));
3021*a1157835SDaniel Fojt 		return -1;
3022*a1157835SDaniel Fojt 	}
3023*a1157835SDaniel Fojt 
3024*a1157835SDaniel Fojt 	if (peer->chan_switch_enabled) {
3025*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
3026*a1157835SDaniel Fojt 			   " already has channel switching enabled",
3027*a1157835SDaniel Fojt 			   MAC2STR(addr));
3028*a1157835SDaniel Fojt 		return 0;
3029*a1157835SDaniel Fojt 	}
3030*a1157835SDaniel Fojt 
3031*a1157835SDaniel Fojt 	ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr,
3032*a1157835SDaniel Fojt 						oper_class, freq_params);
3033*a1157835SDaniel Fojt 	if (!ret)
3034*a1157835SDaniel Fojt 		peer->chan_switch_enabled = 1;
3035*a1157835SDaniel Fojt 
3036*a1157835SDaniel Fojt 	return ret;
3037*a1157835SDaniel Fojt }
3038*a1157835SDaniel Fojt 
3039*a1157835SDaniel Fojt 
wpa_tdls_disable_chan_switch(struct wpa_sm * sm,const u8 * addr)3040*a1157835SDaniel Fojt int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr)
3041*a1157835SDaniel Fojt {
3042*a1157835SDaniel Fojt 	struct wpa_tdls_peer *peer;
3043*a1157835SDaniel Fojt 
3044*a1157835SDaniel Fojt 	if (sm->tdls_disabled || !sm->tdls_supported)
3045*a1157835SDaniel Fojt 		return -1;
3046*a1157835SDaniel Fojt 
3047*a1157835SDaniel Fojt 	for (peer = sm->tdls; peer; peer = peer->next) {
3048*a1157835SDaniel Fojt 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
3049*a1157835SDaniel Fojt 			break;
3050*a1157835SDaniel Fojt 	}
3051*a1157835SDaniel Fojt 
3052*a1157835SDaniel Fojt 	if (!peer || !peer->chan_switch_enabled) {
3053*a1157835SDaniel Fojt 		wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for "
3054*a1157835SDaniel Fojt 			   MACSTR, MAC2STR(addr));
3055*a1157835SDaniel Fojt 		return -1;
3056*a1157835SDaniel Fojt 	}
3057*a1157835SDaniel Fojt 
3058*a1157835SDaniel Fojt 	/* ignore the return value */
3059*a1157835SDaniel Fojt 	wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
3060*a1157835SDaniel Fojt 
3061*a1157835SDaniel Fojt 	peer->chan_switch_enabled = 0;
3062*a1157835SDaniel Fojt 	return 0;
3063*a1157835SDaniel Fojt }
3064