xref: /freebsd/contrib/wpa/src/rsn_supp/tdls.c (revision 325151a32e114f02699a301c1e74080e7c1f1a26)
1f05cddf9SRui Paulo /*
2f05cddf9SRui Paulo  * wpa_supplicant - TDLS
3f05cddf9SRui Paulo  * Copyright (c) 2010-2011, Atheros Communications
4f05cddf9SRui Paulo  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
7f05cddf9SRui Paulo  */
8f05cddf9SRui Paulo 
9f05cddf9SRui Paulo #include "utils/includes.h"
10f05cddf9SRui Paulo 
11f05cddf9SRui Paulo #include "utils/common.h"
12f05cddf9SRui Paulo #include "utils/eloop.h"
13f05cddf9SRui Paulo #include "utils/os.h"
14f05cddf9SRui Paulo #include "common/ieee802_11_defs.h"
15*325151a3SRui Paulo #include "common/ieee802_11_common.h"
16f05cddf9SRui Paulo #include "crypto/sha256.h"
17f05cddf9SRui Paulo #include "crypto/crypto.h"
18f05cddf9SRui Paulo #include "crypto/aes_wrap.h"
19f05cddf9SRui Paulo #include "rsn_supp/wpa.h"
20f05cddf9SRui Paulo #include "rsn_supp/wpa_ie.h"
21f05cddf9SRui Paulo #include "rsn_supp/wpa_i.h"
22f05cddf9SRui Paulo #include "drivers/driver.h"
23f05cddf9SRui Paulo #include "l2_packet/l2_packet.h"
24f05cddf9SRui Paulo 
25f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
26f05cddf9SRui Paulo #define TDLS_TESTING_LONG_FRAME BIT(0)
27f05cddf9SRui Paulo #define TDLS_TESTING_ALT_RSN_IE BIT(1)
28f05cddf9SRui Paulo #define TDLS_TESTING_DIFF_BSSID BIT(2)
29f05cddf9SRui Paulo #define TDLS_TESTING_SHORT_LIFETIME BIT(3)
30f05cddf9SRui Paulo #define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4)
31f05cddf9SRui Paulo #define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5)
32f05cddf9SRui Paulo #define TDLS_TESTING_LONG_LIFETIME BIT(6)
33f05cddf9SRui Paulo #define TDLS_TESTING_CONCURRENT_INIT BIT(7)
34f05cddf9SRui Paulo #define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8)
35f05cddf9SRui Paulo #define TDLS_TESTING_DECLINE_RESP BIT(9)
36f05cddf9SRui Paulo #define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
375b9c547cSRui Paulo #define TDLS_TESTING_WRONG_MIC BIT(11)
38f05cddf9SRui Paulo unsigned int tdls_testing = 0;
39f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
40f05cddf9SRui Paulo 
41f05cddf9SRui Paulo #define TPK_LIFETIME 43200 /* 12 hours */
425b9c547cSRui Paulo #define TPK_M1_RETRY_COUNT 3
435b9c547cSRui Paulo #define TPK_M1_TIMEOUT 5000 /* in milliseconds */
445b9c547cSRui Paulo #define TPK_M2_RETRY_COUNT 10
455b9c547cSRui Paulo #define TPK_M2_TIMEOUT 500 /* in milliseconds */
46f05cddf9SRui Paulo 
47f05cddf9SRui Paulo #define TDLS_MIC_LEN		16
48f05cddf9SRui Paulo 
49f05cddf9SRui Paulo #define TDLS_TIMEOUT_LEN	4
50f05cddf9SRui Paulo 
51f05cddf9SRui Paulo struct wpa_tdls_ftie {
52f05cddf9SRui Paulo 	u8 ie_type; /* FTIE */
53f05cddf9SRui Paulo 	u8 ie_len;
54f05cddf9SRui Paulo 	u8 mic_ctrl[2];
55f05cddf9SRui Paulo 	u8 mic[TDLS_MIC_LEN];
56f05cddf9SRui Paulo 	u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */
57f05cddf9SRui Paulo 	u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */
58f05cddf9SRui Paulo 	/* followed by optional elements */
59f05cddf9SRui Paulo } STRUCT_PACKED;
60f05cddf9SRui Paulo 
61f05cddf9SRui Paulo struct wpa_tdls_timeoutie {
62f05cddf9SRui Paulo 	u8 ie_type; /* Timeout IE */
63f05cddf9SRui Paulo 	u8 ie_len;
64f05cddf9SRui Paulo 	u8 interval_type;
65f05cddf9SRui Paulo 	u8 value[TDLS_TIMEOUT_LEN];
66f05cddf9SRui Paulo } STRUCT_PACKED;
67f05cddf9SRui Paulo 
68f05cddf9SRui Paulo struct wpa_tdls_lnkid {
69f05cddf9SRui Paulo 	u8 ie_type; /* Link Identifier IE */
70f05cddf9SRui Paulo 	u8 ie_len;
71f05cddf9SRui Paulo 	u8 bssid[ETH_ALEN];
72f05cddf9SRui Paulo 	u8 init_sta[ETH_ALEN];
73f05cddf9SRui Paulo 	u8 resp_sta[ETH_ALEN];
74f05cddf9SRui Paulo } STRUCT_PACKED;
75f05cddf9SRui Paulo 
76f05cddf9SRui Paulo /* TDLS frame headers as per IEEE Std 802.11z-2010 */
77f05cddf9SRui Paulo struct wpa_tdls_frame {
78f05cddf9SRui Paulo 	u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */
79f05cddf9SRui Paulo 	u8 category; /* Category */
80f05cddf9SRui Paulo 	u8 action; /* Action (enum tdls_frame_type) */
81f05cddf9SRui Paulo } STRUCT_PACKED;
82f05cddf9SRui Paulo 
83f05cddf9SRui Paulo static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs);
84f05cddf9SRui Paulo static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx);
85f05cddf9SRui Paulo static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer);
865b9c547cSRui Paulo static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
875b9c547cSRui Paulo 				       struct wpa_tdls_peer *peer);
885b9c547cSRui Paulo static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
895b9c547cSRui Paulo 				  u16 reason_code);
90f05cddf9SRui Paulo 
91f05cddf9SRui Paulo 
92f05cddf9SRui Paulo #define TDLS_MAX_IE_LEN 80
93f05cddf9SRui Paulo #define IEEE80211_MAX_SUPP_RATES 32
94f05cddf9SRui Paulo 
95f05cddf9SRui Paulo struct wpa_tdls_peer {
96f05cddf9SRui Paulo 	struct wpa_tdls_peer *next;
975b9c547cSRui Paulo 	unsigned int reconfig_key:1;
98f05cddf9SRui Paulo 	int initiator; /* whether this end was initiator for TDLS setup */
99f05cddf9SRui Paulo 	u8 addr[ETH_ALEN]; /* other end MAC address */
100f05cddf9SRui Paulo 	u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
101f05cddf9SRui Paulo 	u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */
102f05cddf9SRui Paulo 	u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */
103f05cddf9SRui Paulo 	size_t rsnie_i_len;
104f05cddf9SRui Paulo 	u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */
105f05cddf9SRui Paulo 	size_t rsnie_p_len;
106f05cddf9SRui Paulo 	u32 lifetime;
107f05cddf9SRui Paulo 	int cipher; /* Selected cipher (WPA_CIPHER_*) */
108f05cddf9SRui Paulo 	u8 dtoken;
109f05cddf9SRui Paulo 
110f05cddf9SRui Paulo 	struct tpk {
111f05cddf9SRui Paulo 		u8 kck[16]; /* TPK-KCK */
112f05cddf9SRui Paulo 		u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */
113f05cddf9SRui Paulo 	} tpk;
114f05cddf9SRui Paulo 	int tpk_set;
115f05cddf9SRui Paulo 	int tpk_success;
1165b9c547cSRui Paulo 	int tpk_in_progress;
117f05cddf9SRui Paulo 
118f05cddf9SRui Paulo 	struct tpk_timer {
119f05cddf9SRui Paulo 		u8 dest[ETH_ALEN];
120f05cddf9SRui Paulo 		int count;      /* Retry Count */
121f05cddf9SRui Paulo 		int timer;      /* Timeout in milliseconds */
122f05cddf9SRui Paulo 		u8 action_code; /* TDLS frame type */
123f05cddf9SRui Paulo 		u8 dialog_token;
124f05cddf9SRui Paulo 		u16 status_code;
1255b9c547cSRui Paulo 		u32 peer_capab;
126f05cddf9SRui Paulo 		int buf_len;    /* length of TPK message for retransmission */
127f05cddf9SRui Paulo 		u8 *buf;        /* buffer for TPK message */
128f05cddf9SRui Paulo 	} sm_tmr;
129f05cddf9SRui Paulo 
130f05cddf9SRui Paulo 	u16 capability;
131f05cddf9SRui Paulo 
132f05cddf9SRui Paulo 	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
133f05cddf9SRui Paulo 	size_t supp_rates_len;
1345b9c547cSRui Paulo 
1355b9c547cSRui Paulo 	struct ieee80211_ht_capabilities *ht_capabilities;
1365b9c547cSRui Paulo 	struct ieee80211_vht_capabilities *vht_capabilities;
1375b9c547cSRui Paulo 
1385b9c547cSRui Paulo 	u8 qos_info;
1395b9c547cSRui Paulo 
1405b9c547cSRui Paulo 	u16 aid;
1415b9c547cSRui Paulo 
1425b9c547cSRui Paulo 	u8 *ext_capab;
1435b9c547cSRui Paulo 	size_t ext_capab_len;
1445b9c547cSRui Paulo 
1455b9c547cSRui Paulo 	u8 *supp_channels;
1465b9c547cSRui Paulo 	size_t supp_channels_len;
1475b9c547cSRui Paulo 
1485b9c547cSRui Paulo 	u8 *supp_oper_classes;
1495b9c547cSRui Paulo 	size_t supp_oper_classes_len;
1505b9c547cSRui Paulo 
1515b9c547cSRui Paulo 	u8 wmm_capable;
1525b9c547cSRui Paulo 
1535b9c547cSRui Paulo 	/* channel switch currently enabled */
1545b9c547cSRui Paulo 	int chan_switch_enabled;
155f05cddf9SRui Paulo };
156f05cddf9SRui Paulo 
157f05cddf9SRui Paulo 
158f05cddf9SRui Paulo static int wpa_tdls_get_privacy(struct wpa_sm *sm)
159f05cddf9SRui Paulo {
160f05cddf9SRui Paulo 	/*
161f05cddf9SRui Paulo 	 * Get info needed from supplicant to check if the current BSS supports
162f05cddf9SRui Paulo 	 * security. Other than OPEN mode, rest are considered secured
163f05cddf9SRui Paulo 	 * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake.
164f05cddf9SRui Paulo 	 */
165f05cddf9SRui Paulo 	return sm->pairwise_cipher != WPA_CIPHER_NONE;
166f05cddf9SRui Paulo }
167f05cddf9SRui Paulo 
168f05cddf9SRui Paulo 
169f05cddf9SRui Paulo static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
170f05cddf9SRui Paulo {
171f05cddf9SRui Paulo 	os_memcpy(pos, ie, ie_len);
172f05cddf9SRui Paulo 	return pos + ie_len;
173f05cddf9SRui Paulo }
174f05cddf9SRui Paulo 
175f05cddf9SRui Paulo 
176f05cddf9SRui Paulo static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
177f05cddf9SRui Paulo {
178f05cddf9SRui Paulo 	if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
179f05cddf9SRui Paulo 			   0, 0, NULL, 0, NULL, 0) < 0) {
180f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
181f05cddf9SRui Paulo 			   "the driver");
182f05cddf9SRui Paulo 		return -1;
183f05cddf9SRui Paulo 	}
184f05cddf9SRui Paulo 
185f05cddf9SRui Paulo 	return 0;
186f05cddf9SRui Paulo }
187f05cddf9SRui Paulo 
188f05cddf9SRui Paulo 
189f05cddf9SRui Paulo static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
190f05cddf9SRui Paulo {
191f05cddf9SRui Paulo 	u8 key_len;
192f05cddf9SRui Paulo 	u8 rsc[6];
193f05cddf9SRui Paulo 	enum wpa_alg alg;
194f05cddf9SRui Paulo 
195f05cddf9SRui Paulo 	os_memset(rsc, 0, 6);
196f05cddf9SRui Paulo 
197f05cddf9SRui Paulo 	switch (peer->cipher) {
198f05cddf9SRui Paulo 	case WPA_CIPHER_CCMP:
199f05cddf9SRui Paulo 		alg = WPA_ALG_CCMP;
200f05cddf9SRui Paulo 		key_len = 16;
201f05cddf9SRui Paulo 		break;
202f05cddf9SRui Paulo 	case WPA_CIPHER_NONE:
203f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: "
204f05cddf9SRui Paulo 			   "NONE - do not use pairwise keys");
205f05cddf9SRui Paulo 		return -1;
206f05cddf9SRui Paulo 	default:
207f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d",
208f05cddf9SRui Paulo 			   sm->pairwise_cipher);
209f05cddf9SRui Paulo 		return -1;
210f05cddf9SRui Paulo 	}
211f05cddf9SRui Paulo 
212f05cddf9SRui Paulo 	if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1,
213f05cddf9SRui Paulo 			   rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) {
214f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
215f05cddf9SRui Paulo 			   "driver");
216f05cddf9SRui Paulo 		return -1;
217f05cddf9SRui Paulo 	}
218f05cddf9SRui Paulo 	return 0;
219f05cddf9SRui Paulo }
220f05cddf9SRui Paulo 
221f05cddf9SRui Paulo 
222f05cddf9SRui Paulo static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
223f05cddf9SRui Paulo 				 u8 action_code, u8 dialog_token,
2245b9c547cSRui Paulo 				 u16 status_code, u32 peer_capab,
2255b9c547cSRui Paulo 				 int initiator, const u8 *buf, size_t len)
226f05cddf9SRui Paulo {
227f05cddf9SRui Paulo 	return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
2285b9c547cSRui Paulo 				     status_code, peer_capab, initiator, buf,
2295b9c547cSRui Paulo 				     len);
230f05cddf9SRui Paulo }
231f05cddf9SRui Paulo 
232f05cddf9SRui Paulo 
233f05cddf9SRui Paulo static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
2345b9c547cSRui Paulo 			     u8 dialog_token, u16 status_code, u32 peer_capab,
2355b9c547cSRui Paulo 			     int initiator, const u8 *msg, size_t msg_len)
236f05cddf9SRui Paulo {
237f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
238f05cddf9SRui Paulo 
239f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u "
2405b9c547cSRui Paulo 		   "dialog_token=%u status_code=%u peer_capab=%u initiator=%d "
2415b9c547cSRui Paulo 		   "msg_len=%u",
242f05cddf9SRui Paulo 		   MAC2STR(dest), action_code, dialog_token, status_code,
2435b9c547cSRui Paulo 		   peer_capab, initiator, (unsigned int) msg_len);
244f05cddf9SRui Paulo 
245f05cddf9SRui Paulo 	if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
2465b9c547cSRui Paulo 				  status_code, peer_capab, initiator, msg,
2475b9c547cSRui Paulo 				  msg_len)) {
248f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Failed to send message "
249f05cddf9SRui Paulo 			   "(action_code=%u)", action_code);
250f05cddf9SRui Paulo 		return -1;
251f05cddf9SRui Paulo 	}
252f05cddf9SRui Paulo 
253f05cddf9SRui Paulo 	if (action_code == WLAN_TDLS_SETUP_CONFIRM ||
254f05cddf9SRui Paulo 	    action_code == WLAN_TDLS_TEARDOWN ||
255f05cddf9SRui Paulo 	    action_code == WLAN_TDLS_DISCOVERY_REQUEST ||
256f05cddf9SRui Paulo 	    action_code == WLAN_TDLS_DISCOVERY_RESPONSE)
257f05cddf9SRui Paulo 		return 0; /* No retries */
258f05cddf9SRui Paulo 
259f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
260f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0)
261f05cddf9SRui Paulo 			break;
262f05cddf9SRui Paulo 	}
263f05cddf9SRui Paulo 
264f05cddf9SRui Paulo 	if (peer == NULL) {
265f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
266f05cddf9SRui Paulo 			   "retry " MACSTR, MAC2STR(dest));
267f05cddf9SRui Paulo 		return 0;
268f05cddf9SRui Paulo 	}
269f05cddf9SRui Paulo 
270f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
271f05cddf9SRui Paulo 
2725b9c547cSRui Paulo 	if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
2735b9c547cSRui Paulo 		peer->sm_tmr.count = TPK_M2_RETRY_COUNT;
2745b9c547cSRui Paulo 		peer->sm_tmr.timer = TPK_M2_TIMEOUT;
2755b9c547cSRui Paulo 	} else {
2765b9c547cSRui Paulo 		peer->sm_tmr.count = TPK_M1_RETRY_COUNT;
2775b9c547cSRui Paulo 		peer->sm_tmr.timer = TPK_M1_TIMEOUT;
2785b9c547cSRui Paulo 	}
279f05cddf9SRui Paulo 
280f05cddf9SRui Paulo 	/* Copy message to resend on timeout */
281f05cddf9SRui Paulo 	os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN);
282f05cddf9SRui Paulo 	peer->sm_tmr.action_code = action_code;
283f05cddf9SRui Paulo 	peer->sm_tmr.dialog_token = dialog_token;
284f05cddf9SRui Paulo 	peer->sm_tmr.status_code = status_code;
2855b9c547cSRui Paulo 	peer->sm_tmr.peer_capab = peer_capab;
286f05cddf9SRui Paulo 	peer->sm_tmr.buf_len = msg_len;
287f05cddf9SRui Paulo 	os_free(peer->sm_tmr.buf);
288f05cddf9SRui Paulo 	peer->sm_tmr.buf = os_malloc(msg_len);
289f05cddf9SRui Paulo 	if (peer->sm_tmr.buf == NULL)
290f05cddf9SRui Paulo 		return -1;
291f05cddf9SRui Paulo 	os_memcpy(peer->sm_tmr.buf, msg, msg_len);
292f05cddf9SRui Paulo 
293f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
294f05cddf9SRui Paulo 		   "(action_code=%u)", action_code);
2955b9c547cSRui Paulo 	eloop_register_timeout(peer->sm_tmr.timer / 1000,
2965b9c547cSRui Paulo 			       (peer->sm_tmr.timer % 1000) * 1000,
297f05cddf9SRui Paulo 			       wpa_tdls_tpk_retry_timeout, sm, peer);
298f05cddf9SRui Paulo 	return 0;
299f05cddf9SRui Paulo }
300f05cddf9SRui Paulo 
301f05cddf9SRui Paulo 
302f05cddf9SRui Paulo static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
3035b9c547cSRui Paulo 				u16 reason_code)
304f05cddf9SRui Paulo {
305f05cddf9SRui Paulo 	int ret;
306f05cddf9SRui Paulo 
307f05cddf9SRui Paulo 	ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code);
308f05cddf9SRui Paulo 	/* disable the link after teardown was sent */
3095b9c547cSRui Paulo 	wpa_tdls_disable_peer_link(sm, peer);
310f05cddf9SRui Paulo 
311f05cddf9SRui Paulo 	return ret;
312f05cddf9SRui Paulo }
313f05cddf9SRui Paulo 
314f05cddf9SRui Paulo 
315f05cddf9SRui Paulo static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx)
316f05cddf9SRui Paulo {
317f05cddf9SRui Paulo 
318f05cddf9SRui Paulo 	struct wpa_sm *sm = eloop_ctx;
319f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer = timeout_ctx;
320f05cddf9SRui Paulo 
321f05cddf9SRui Paulo 	if (peer->sm_tmr.count) {
322f05cddf9SRui Paulo 		peer->sm_tmr.count--;
323f05cddf9SRui Paulo 
324f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Retrying sending of message "
325f05cddf9SRui Paulo 			   "(action_code=%u)",
326f05cddf9SRui Paulo 			   peer->sm_tmr.action_code);
327f05cddf9SRui Paulo 
328f05cddf9SRui Paulo 		if (peer->sm_tmr.buf == NULL) {
329f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: No retry buffer available "
330f05cddf9SRui Paulo 				   "for action_code=%u",
331f05cddf9SRui Paulo 				   peer->sm_tmr.action_code);
332f05cddf9SRui Paulo 			eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm,
333f05cddf9SRui Paulo 					     peer);
334f05cddf9SRui Paulo 			return;
335f05cddf9SRui Paulo 		}
336f05cddf9SRui Paulo 
337f05cddf9SRui Paulo 		/* resend TPK Handshake Message to Peer */
338f05cddf9SRui Paulo 		if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest,
339f05cddf9SRui Paulo 					  peer->sm_tmr.action_code,
340f05cddf9SRui Paulo 					  peer->sm_tmr.dialog_token,
341f05cddf9SRui Paulo 					  peer->sm_tmr.status_code,
3425b9c547cSRui Paulo 					  peer->sm_tmr.peer_capab,
3435b9c547cSRui Paulo 					  peer->initiator,
344f05cddf9SRui Paulo 					  peer->sm_tmr.buf,
345f05cddf9SRui Paulo 					  peer->sm_tmr.buf_len)) {
346f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: Failed to retry "
347f05cddf9SRui Paulo 				   "transmission");
348f05cddf9SRui Paulo 		}
349f05cddf9SRui Paulo 
350f05cddf9SRui Paulo 		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
3515b9c547cSRui Paulo 		eloop_register_timeout(peer->sm_tmr.timer / 1000,
3525b9c547cSRui Paulo 				       (peer->sm_tmr.timer % 1000) * 1000,
353f05cddf9SRui Paulo 				       wpa_tdls_tpk_retry_timeout, sm, peer);
354f05cddf9SRui Paulo 	} else {
355f05cddf9SRui Paulo 		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
356f05cddf9SRui Paulo 
357f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request");
358f05cddf9SRui Paulo 		wpa_tdls_do_teardown(sm, peer,
3595b9c547cSRui Paulo 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
360f05cddf9SRui Paulo 	}
361f05cddf9SRui Paulo }
362f05cddf9SRui Paulo 
363f05cddf9SRui Paulo 
364f05cddf9SRui Paulo static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm,
365f05cddf9SRui Paulo 					      struct wpa_tdls_peer *peer,
366f05cddf9SRui Paulo 					      u8 action_code)
367f05cddf9SRui Paulo {
368f05cddf9SRui Paulo 	if (action_code == peer->sm_tmr.action_code) {
369f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for "
370f05cddf9SRui Paulo 			   "action_code=%u", action_code);
371f05cddf9SRui Paulo 
372f05cddf9SRui Paulo 		/* Cancel Timeout registered */
373f05cddf9SRui Paulo 		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
374f05cddf9SRui Paulo 
375f05cddf9SRui Paulo 		/* free all resources meant for retry */
376f05cddf9SRui Paulo 		os_free(peer->sm_tmr.buf);
377f05cddf9SRui Paulo 		peer->sm_tmr.buf = NULL;
378f05cddf9SRui Paulo 
379f05cddf9SRui Paulo 		peer->sm_tmr.count = 0;
380f05cddf9SRui Paulo 		peer->sm_tmr.timer = 0;
381f05cddf9SRui Paulo 		peer->sm_tmr.buf_len = 0;
382f05cddf9SRui Paulo 		peer->sm_tmr.action_code = 0xff;
383f05cddf9SRui Paulo 	} else {
384f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout "
385f05cddf9SRui Paulo 			   "(Unknown action_code=%u)", action_code);
386f05cddf9SRui Paulo 	}
387f05cddf9SRui Paulo }
388f05cddf9SRui Paulo 
389f05cddf9SRui Paulo 
390f05cddf9SRui Paulo static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer,
391f05cddf9SRui Paulo 				  const u8 *own_addr, const u8 *bssid)
392f05cddf9SRui Paulo {
393f05cddf9SRui Paulo 	u8 key_input[SHA256_MAC_LEN];
394f05cddf9SRui Paulo 	const u8 *nonce[2];
395f05cddf9SRui Paulo 	size_t len[2];
396f05cddf9SRui Paulo 	u8 data[3 * ETH_ALEN];
397f05cddf9SRui Paulo 
398f05cddf9SRui Paulo 	/* IEEE Std 802.11z-2010 8.5.9.1:
399f05cddf9SRui Paulo 	 * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
400f05cddf9SRui Paulo 	 */
401f05cddf9SRui Paulo 	len[0] = WPA_NONCE_LEN;
402f05cddf9SRui Paulo 	len[1] = WPA_NONCE_LEN;
403f05cddf9SRui Paulo 	if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) {
404f05cddf9SRui Paulo 		nonce[0] = peer->inonce;
405f05cddf9SRui Paulo 		nonce[1] = peer->rnonce;
406f05cddf9SRui Paulo 	} else {
407f05cddf9SRui Paulo 		nonce[0] = peer->rnonce;
408f05cddf9SRui Paulo 		nonce[1] = peer->inonce;
409f05cddf9SRui Paulo 	}
410f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN);
411f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN);
412f05cddf9SRui Paulo 	sha256_vector(2, nonce, len, key_input);
413f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
414f05cddf9SRui Paulo 			key_input, SHA256_MAC_LEN);
415f05cddf9SRui Paulo 
416f05cddf9SRui Paulo 	/*
417f05cddf9SRui Paulo 	 * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
418f05cddf9SRui Paulo 	 *	min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
419f05cddf9SRui Paulo 	 * TODO: is N_KEY really included in KDF Context and if so, in which
420f05cddf9SRui Paulo 	 * presentation format (little endian 16-bit?) is it used? It gets
421f05cddf9SRui Paulo 	 * added by the KDF anyway..
422f05cddf9SRui Paulo 	 */
423f05cddf9SRui Paulo 
424f05cddf9SRui Paulo 	if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
425f05cddf9SRui Paulo 		os_memcpy(data, own_addr, ETH_ALEN);
426f05cddf9SRui Paulo 		os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN);
427f05cddf9SRui Paulo 	} else {
428f05cddf9SRui Paulo 		os_memcpy(data, peer->addr, ETH_ALEN);
429f05cddf9SRui Paulo 		os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN);
430f05cddf9SRui Paulo 	}
431f05cddf9SRui Paulo 	os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
432f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
433f05cddf9SRui Paulo 
434f05cddf9SRui Paulo 	sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
435f05cddf9SRui Paulo 		   (u8 *) &peer->tpk, sizeof(peer->tpk));
436f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
437f05cddf9SRui Paulo 			peer->tpk.kck, sizeof(peer->tpk.kck));
438f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
439f05cddf9SRui Paulo 			peer->tpk.tk, sizeof(peer->tpk.tk));
440f05cddf9SRui Paulo 	peer->tpk_set = 1;
441f05cddf9SRui Paulo }
442f05cddf9SRui Paulo 
443f05cddf9SRui Paulo 
444f05cddf9SRui Paulo /**
445f05cddf9SRui Paulo  * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
446f05cddf9SRui Paulo  * @kck: TPK-KCK
447f05cddf9SRui Paulo  * @lnkid: Pointer to the beginning of Link Identifier IE
448f05cddf9SRui Paulo  * @rsnie: Pointer to the beginning of RSN IE used for handshake
449f05cddf9SRui Paulo  * @timeoutie: Pointer to the beginning of Timeout IE used for handshake
450f05cddf9SRui Paulo  * @ftie: Pointer to the beginning of FT IE
451f05cddf9SRui Paulo  * @mic: Pointer for writing MIC
452f05cddf9SRui Paulo  *
453f05cddf9SRui Paulo  * Calculate MIC for TDLS frame.
454f05cddf9SRui Paulo  */
455f05cddf9SRui Paulo static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
456f05cddf9SRui Paulo 			     const u8 *rsnie, const u8 *timeoutie,
457f05cddf9SRui Paulo 			     const u8 *ftie, u8 *mic)
458f05cddf9SRui Paulo {
459f05cddf9SRui Paulo 	u8 *buf, *pos;
460f05cddf9SRui Paulo 	struct wpa_tdls_ftie *_ftie;
461f05cddf9SRui Paulo 	const struct wpa_tdls_lnkid *_lnkid;
462f05cddf9SRui Paulo 	int ret;
463f05cddf9SRui Paulo 	int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] +
464f05cddf9SRui Paulo 		2 + timeoutie[1] + 2 + ftie[1];
465f05cddf9SRui Paulo 	buf = os_zalloc(len);
466f05cddf9SRui Paulo 	if (!buf) {
467f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
468f05cddf9SRui Paulo 		return -1;
469f05cddf9SRui Paulo 	}
470f05cddf9SRui Paulo 
471f05cddf9SRui Paulo 	pos = buf;
472f05cddf9SRui Paulo 	_lnkid = (const struct wpa_tdls_lnkid *) lnkid;
473f05cddf9SRui Paulo 	/* 1) TDLS initiator STA MAC address */
474f05cddf9SRui Paulo 	os_memcpy(pos, _lnkid->init_sta, ETH_ALEN);
475f05cddf9SRui Paulo 	pos += ETH_ALEN;
476f05cddf9SRui Paulo 	/* 2) TDLS responder STA MAC address */
477f05cddf9SRui Paulo 	os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN);
478f05cddf9SRui Paulo 	pos += ETH_ALEN;
479f05cddf9SRui Paulo 	/* 3) Transaction Sequence number */
480f05cddf9SRui Paulo 	*pos++ = trans_seq;
481f05cddf9SRui Paulo 	/* 4) Link Identifier IE */
482f05cddf9SRui Paulo 	os_memcpy(pos, lnkid, 2 + lnkid[1]);
483f05cddf9SRui Paulo 	pos += 2 + lnkid[1];
484f05cddf9SRui Paulo 	/* 5) RSN IE */
485f05cddf9SRui Paulo 	os_memcpy(pos, rsnie, 2 + rsnie[1]);
486f05cddf9SRui Paulo 	pos += 2 + rsnie[1];
487f05cddf9SRui Paulo 	/* 6) Timeout Interval IE */
488f05cddf9SRui Paulo 	os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
489f05cddf9SRui Paulo 	pos += 2 + timeoutie[1];
490f05cddf9SRui Paulo 	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
491f05cddf9SRui Paulo 	os_memcpy(pos, ftie, 2 + ftie[1]);
492f05cddf9SRui Paulo 	_ftie = (struct wpa_tdls_ftie *) pos;
493f05cddf9SRui Paulo 	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
494f05cddf9SRui Paulo 	pos += 2 + ftie[1];
495f05cddf9SRui Paulo 
496f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
497f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
498f05cddf9SRui Paulo 	ret = omac1_aes_128(kck, buf, pos - buf, mic);
499f05cddf9SRui Paulo 	os_free(buf);
500f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
501f05cddf9SRui Paulo 	return ret;
502f05cddf9SRui Paulo }
503f05cddf9SRui Paulo 
504f05cddf9SRui Paulo 
505f05cddf9SRui Paulo /**
506f05cddf9SRui Paulo  * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame
507f05cddf9SRui Paulo  * @kck: TPK-KCK
508f05cddf9SRui Paulo  * @trans_seq: Transaction Sequence Number (4 - Teardown)
509f05cddf9SRui Paulo  * @rcode: Reason code for Teardown
510f05cddf9SRui Paulo  * @dtoken: Dialog Token used for that particular link
511f05cddf9SRui Paulo  * @lnkid: Pointer to the beginning of Link Identifier IE
512f05cddf9SRui Paulo  * @ftie: Pointer to the beginning of FT IE
513f05cddf9SRui Paulo  * @mic: Pointer for writing MIC
514f05cddf9SRui Paulo  *
515f05cddf9SRui Paulo  * Calculate MIC for TDLS frame.
516f05cddf9SRui Paulo  */
517f05cddf9SRui Paulo static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
518f05cddf9SRui Paulo 				     u8 dtoken, const u8 *lnkid,
519f05cddf9SRui Paulo 				     const u8 *ftie, u8 *mic)
520f05cddf9SRui Paulo {
521f05cddf9SRui Paulo 	u8 *buf, *pos;
522f05cddf9SRui Paulo 	struct wpa_tdls_ftie *_ftie;
523f05cddf9SRui Paulo 	int ret;
524f05cddf9SRui Paulo 	int len;
525f05cddf9SRui Paulo 
526f05cddf9SRui Paulo 	if (lnkid == NULL)
527f05cddf9SRui Paulo 		return -1;
528f05cddf9SRui Paulo 
529f05cddf9SRui Paulo 	len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
530f05cddf9SRui Paulo 		sizeof(trans_seq) + 2 + ftie[1];
531f05cddf9SRui Paulo 
532f05cddf9SRui Paulo 	buf = os_zalloc(len);
533f05cddf9SRui Paulo 	if (!buf) {
534f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
535f05cddf9SRui Paulo 		return -1;
536f05cddf9SRui Paulo 	}
537f05cddf9SRui Paulo 
538f05cddf9SRui Paulo 	pos = buf;
539f05cddf9SRui Paulo 	/* 1) Link Identifier IE */
540f05cddf9SRui Paulo 	os_memcpy(pos, lnkid, 2 + lnkid[1]);
541f05cddf9SRui Paulo 	pos += 2 + lnkid[1];
542f05cddf9SRui Paulo 	/* 2) Reason Code */
543f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, rcode);
544f05cddf9SRui Paulo 	pos += sizeof(rcode);
545f05cddf9SRui Paulo 	/* 3) Dialog token */
546f05cddf9SRui Paulo 	*pos++ = dtoken;
547f05cddf9SRui Paulo 	/* 4) Transaction Sequence number */
548f05cddf9SRui Paulo 	*pos++ = trans_seq;
549f05cddf9SRui Paulo 	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
550f05cddf9SRui Paulo 	os_memcpy(pos, ftie, 2 + ftie[1]);
551f05cddf9SRui Paulo 	_ftie = (struct wpa_tdls_ftie *) pos;
552f05cddf9SRui Paulo 	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
553f05cddf9SRui Paulo 	pos += 2 + ftie[1];
554f05cddf9SRui Paulo 
555f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
556f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
557f05cddf9SRui Paulo 	ret = omac1_aes_128(kck, buf, pos - buf, mic);
558f05cddf9SRui Paulo 	os_free(buf);
559f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
560f05cddf9SRui Paulo 	return ret;
561f05cddf9SRui Paulo }
562f05cddf9SRui Paulo 
563f05cddf9SRui Paulo 
564f05cddf9SRui Paulo static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
565f05cddf9SRui Paulo 					  struct wpa_tdls_peer *peer,
566f05cddf9SRui Paulo 					  const u8 *lnkid, const u8 *timeoutie,
567f05cddf9SRui Paulo 					  const struct wpa_tdls_ftie *ftie)
568f05cddf9SRui Paulo {
569f05cddf9SRui Paulo 	u8 mic[16];
570f05cddf9SRui Paulo 
571f05cddf9SRui Paulo 	if (peer->tpk_set) {
572f05cddf9SRui Paulo 		wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
573f05cddf9SRui Paulo 				  peer->rsnie_p, timeoutie, (u8 *) ftie,
574f05cddf9SRui Paulo 				  mic);
5755b9c547cSRui Paulo 		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
576f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
577f05cddf9SRui Paulo 				   "dropping packet");
578f05cddf9SRui Paulo 			wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC",
579f05cddf9SRui Paulo 				    ftie->mic, 16);
580f05cddf9SRui Paulo 			wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC",
581f05cddf9SRui Paulo 				    mic, 16);
582f05cddf9SRui Paulo 			return -1;
583f05cddf9SRui Paulo 		}
584f05cddf9SRui Paulo 	} else {
585f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, "
586f05cddf9SRui Paulo 			   "TPK not set - dropping packet");
587f05cddf9SRui Paulo 		return -1;
588f05cddf9SRui Paulo 	}
589f05cddf9SRui Paulo 	return 0;
590f05cddf9SRui Paulo }
591f05cddf9SRui Paulo 
592f05cddf9SRui Paulo 
593f05cddf9SRui Paulo static int wpa_supplicant_verify_tdls_mic_teardown(
594f05cddf9SRui Paulo 	u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
595f05cddf9SRui Paulo 	const u8 *lnkid, const struct wpa_tdls_ftie *ftie)
596f05cddf9SRui Paulo {
597f05cddf9SRui Paulo 	u8 mic[16];
598f05cddf9SRui Paulo 
599f05cddf9SRui Paulo 	if (peer->tpk_set) {
600f05cddf9SRui Paulo 		wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
601f05cddf9SRui Paulo 					  dtoken, lnkid, (u8 *) ftie, mic);
6025b9c547cSRui Paulo 		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
603f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
604f05cddf9SRui Paulo 				   "dropping packet");
605f05cddf9SRui Paulo 			return -1;
606f05cddf9SRui Paulo 		}
607f05cddf9SRui Paulo 	} else {
608f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown "
609f05cddf9SRui Paulo 			   "MIC, TPK not set - dropping packet");
610f05cddf9SRui Paulo 		return -1;
611f05cddf9SRui Paulo 	}
612f05cddf9SRui Paulo 	return 0;
613f05cddf9SRui Paulo }
614f05cddf9SRui Paulo 
615f05cddf9SRui Paulo 
616f05cddf9SRui Paulo static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx)
617f05cddf9SRui Paulo {
618f05cddf9SRui Paulo 	struct wpa_sm *sm = eloop_ctx;
619f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer = timeout_ctx;
620f05cddf9SRui Paulo 
621f05cddf9SRui Paulo 	/*
622f05cddf9SRui Paulo 	 * On TPK lifetime expiration, we have an option of either tearing down
623f05cddf9SRui Paulo 	 * the direct link or trying to re-initiate it. The selection of what
624f05cddf9SRui Paulo 	 * to do is not strictly speaking controlled by our role in the expired
625f05cddf9SRui Paulo 	 * link, but for now, use that to select whether to renew or tear down
626f05cddf9SRui Paulo 	 * the link.
627f05cddf9SRui Paulo 	 */
628f05cddf9SRui Paulo 
629f05cddf9SRui Paulo 	if (peer->initiator) {
630f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
631f05cddf9SRui Paulo 			   " - try to renew", MAC2STR(peer->addr));
632f05cddf9SRui Paulo 		wpa_tdls_start(sm, peer->addr);
633f05cddf9SRui Paulo 	} else {
634f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
635f05cddf9SRui Paulo 			   " - tear down", MAC2STR(peer->addr));
636f05cddf9SRui Paulo 		wpa_tdls_do_teardown(sm, peer,
6375b9c547cSRui Paulo 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
638f05cddf9SRui Paulo 	}
639f05cddf9SRui Paulo }
640f05cddf9SRui Paulo 
641f05cddf9SRui Paulo 
6425b9c547cSRui Paulo static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm,
6435b9c547cSRui Paulo 					   struct wpa_tdls_peer *peer)
6445b9c547cSRui Paulo {
6455b9c547cSRui Paulo 	struct wpa_tdls_peer *cur, *prev;
6465b9c547cSRui Paulo 
6475b9c547cSRui Paulo 	cur = sm->tdls;
6485b9c547cSRui Paulo 	prev = NULL;
6495b9c547cSRui Paulo 	while (cur && cur != peer) {
6505b9c547cSRui Paulo 		prev = cur;
6515b9c547cSRui Paulo 		cur = cur->next;
6525b9c547cSRui Paulo 	}
6535b9c547cSRui Paulo 
6545b9c547cSRui Paulo 	if (cur != peer) {
6555b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR
6565b9c547cSRui Paulo 			   " to remove it from the list",
6575b9c547cSRui Paulo 			   MAC2STR(peer->addr));
6585b9c547cSRui Paulo 		return;
6595b9c547cSRui Paulo 	}
6605b9c547cSRui Paulo 
6615b9c547cSRui Paulo 	if (prev)
6625b9c547cSRui Paulo 		prev->next = peer->next;
6635b9c547cSRui Paulo 	else
6645b9c547cSRui Paulo 		sm->tdls = peer->next;
6655b9c547cSRui Paulo }
6665b9c547cSRui Paulo 
6675b9c547cSRui Paulo 
6685b9c547cSRui Paulo static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
669f05cddf9SRui Paulo {
670f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
671f05cddf9SRui Paulo 		   MAC2STR(peer->addr));
672f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
673f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
6745b9c547cSRui Paulo 	peer->reconfig_key = 0;
675f05cddf9SRui Paulo 	peer->initiator = 0;
6765b9c547cSRui Paulo 	peer->tpk_in_progress = 0;
677f05cddf9SRui Paulo 	os_free(peer->sm_tmr.buf);
678f05cddf9SRui Paulo 	peer->sm_tmr.buf = NULL;
6795b9c547cSRui Paulo 	os_free(peer->ht_capabilities);
6805b9c547cSRui Paulo 	peer->ht_capabilities = NULL;
6815b9c547cSRui Paulo 	os_free(peer->vht_capabilities);
6825b9c547cSRui Paulo 	peer->vht_capabilities = NULL;
6835b9c547cSRui Paulo 	os_free(peer->ext_capab);
6845b9c547cSRui Paulo 	peer->ext_capab = NULL;
6855b9c547cSRui Paulo 	os_free(peer->supp_channels);
6865b9c547cSRui Paulo 	peer->supp_channels = NULL;
6875b9c547cSRui Paulo 	os_free(peer->supp_oper_classes);
6885b9c547cSRui Paulo 	peer->supp_oper_classes = NULL;
689f05cddf9SRui Paulo 	peer->rsnie_i_len = peer->rsnie_p_len = 0;
690f05cddf9SRui Paulo 	peer->cipher = 0;
6915b9c547cSRui Paulo 	peer->qos_info = 0;
6925b9c547cSRui Paulo 	peer->wmm_capable = 0;
693f05cddf9SRui Paulo 	peer->tpk_set = peer->tpk_success = 0;
6945b9c547cSRui Paulo 	peer->chan_switch_enabled = 0;
695f05cddf9SRui Paulo 	os_memset(&peer->tpk, 0, sizeof(peer->tpk));
696f05cddf9SRui Paulo 	os_memset(peer->inonce, 0, WPA_NONCE_LEN);
697f05cddf9SRui Paulo 	os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
698f05cddf9SRui Paulo }
699f05cddf9SRui Paulo 
700f05cddf9SRui Paulo 
7015b9c547cSRui Paulo static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
7025b9c547cSRui Paulo {
7035b9c547cSRui Paulo 	wpa_tdls_peer_clear(sm, peer);
7045b9c547cSRui Paulo 	wpa_tdls_peer_remove_from_list(sm, peer);
7055b9c547cSRui Paulo 	os_free(peer);
7065b9c547cSRui Paulo }
7075b9c547cSRui Paulo 
7085b9c547cSRui Paulo 
709f05cddf9SRui Paulo static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
710f05cddf9SRui Paulo 			    struct wpa_tdls_lnkid *lnkid)
711f05cddf9SRui Paulo {
712f05cddf9SRui Paulo 	lnkid->ie_type = WLAN_EID_LINK_ID;
713f05cddf9SRui Paulo 	lnkid->ie_len = 3 * ETH_ALEN;
714f05cddf9SRui Paulo 	os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN);
715f05cddf9SRui Paulo 	if (peer->initiator) {
716f05cddf9SRui Paulo 		os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN);
717f05cddf9SRui Paulo 		os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN);
718f05cddf9SRui Paulo 	} else {
719f05cddf9SRui Paulo 		os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN);
720f05cddf9SRui Paulo 		os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN);
721f05cddf9SRui Paulo 	}
722f05cddf9SRui Paulo }
723f05cddf9SRui Paulo 
724f05cddf9SRui Paulo 
7255b9c547cSRui Paulo static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
7265b9c547cSRui Paulo 				  u16 reason_code)
727f05cddf9SRui Paulo {
728f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
729f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
730f05cddf9SRui Paulo 	struct wpa_tdls_lnkid lnkid;
731f05cddf9SRui Paulo 	u8 dialog_token;
732f05cddf9SRui Paulo 	u8 *rbuf, *pos;
733f05cddf9SRui Paulo 	int ielen;
734f05cddf9SRui Paulo 
735f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
736f05cddf9SRui Paulo 		return -1;
737f05cddf9SRui Paulo 
738f05cddf9SRui Paulo 	/* Find the node and free from the list */
739f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
740f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
741f05cddf9SRui Paulo 			break;
742f05cddf9SRui Paulo 	}
743f05cddf9SRui Paulo 
744f05cddf9SRui Paulo 	if (peer == NULL) {
745f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
746f05cddf9SRui Paulo 			   "Teardown " MACSTR, MAC2STR(addr));
747f05cddf9SRui Paulo 		return 0;
748f05cddf9SRui Paulo 	}
749f05cddf9SRui Paulo 
7505b9c547cSRui Paulo 	/* Cancel active channel switch before teardown */
7515b9c547cSRui Paulo 	if (peer->chan_switch_enabled) {
7525b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR
7535b9c547cSRui Paulo 			   " to base channel", MAC2STR(addr));
7545b9c547cSRui Paulo 		wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
7555b9c547cSRui Paulo 	}
7565b9c547cSRui Paulo 
757f05cddf9SRui Paulo 	dialog_token = peer->dtoken;
758f05cddf9SRui Paulo 
759f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR,
760f05cddf9SRui Paulo 		   MAC2STR(addr));
761f05cddf9SRui Paulo 
762f05cddf9SRui Paulo 	ielen = 0;
763f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) {
764f05cddf9SRui Paulo 		/* To add FTIE for Teardown request and compute MIC */
765f05cddf9SRui Paulo 		ielen += sizeof(*ftie);
766f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
767f05cddf9SRui Paulo 		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
768f05cddf9SRui Paulo 			ielen += 170;
769f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
770f05cddf9SRui Paulo 	}
771f05cddf9SRui Paulo 
772f05cddf9SRui Paulo 	rbuf = os_zalloc(ielen + 1);
773f05cddf9SRui Paulo 	if (rbuf == NULL)
774f05cddf9SRui Paulo 		return -1;
775f05cddf9SRui Paulo 	pos = rbuf;
776f05cddf9SRui Paulo 
777f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
778f05cddf9SRui Paulo 		goto skip_ies;
779f05cddf9SRui Paulo 
780f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) pos;
781f05cddf9SRui Paulo 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
782f05cddf9SRui Paulo 	/* Using the recent nonce which should be for CONFIRM frame */
783f05cddf9SRui Paulo 	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
784f05cddf9SRui Paulo 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
785f05cddf9SRui Paulo 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
786f05cddf9SRui Paulo 	pos = (u8 *) (ftie + 1);
787f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
788f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
789f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
790f05cddf9SRui Paulo 			   "FTIE");
791f05cddf9SRui Paulo 		ftie->ie_len += 170;
792f05cddf9SRui Paulo 		*pos++ = 255; /* FTIE subelem */
793f05cddf9SRui Paulo 		*pos++ = 168; /* FTIE subelem length */
794f05cddf9SRui Paulo 		pos += 168;
795f05cddf9SRui Paulo 	}
796f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
797f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake",
798f05cddf9SRui Paulo 		    (u8 *) ftie, pos - (u8 *) ftie);
799f05cddf9SRui Paulo 
800f05cddf9SRui Paulo 	/* compute MIC before sending */
801f05cddf9SRui Paulo 	wpa_tdls_linkid(sm, peer, &lnkid);
802f05cddf9SRui Paulo 	wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
803f05cddf9SRui Paulo 				  dialog_token, (u8 *) &lnkid, (u8 *) ftie,
804f05cddf9SRui Paulo 				  ftie->mic);
805f05cddf9SRui Paulo 
806f05cddf9SRui Paulo skip_ies:
807f05cddf9SRui Paulo 	/* TODO: register for a Timeout handler, if Teardown is not received at
808f05cddf9SRui Paulo 	 * the other end, then try again another time */
809f05cddf9SRui Paulo 
810f05cddf9SRui Paulo 	/* request driver to send Teardown using this FTIE */
811f05cddf9SRui Paulo 	wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
8125b9c547cSRui Paulo 			  reason_code, 0, peer->initiator, rbuf, pos - rbuf);
813f05cddf9SRui Paulo 	os_free(rbuf);
814f05cddf9SRui Paulo 
815f05cddf9SRui Paulo 	return 0;
816f05cddf9SRui Paulo }
817f05cddf9SRui Paulo 
818f05cddf9SRui Paulo 
819f05cddf9SRui Paulo int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
820f05cddf9SRui Paulo {
821f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
822f05cddf9SRui Paulo 
823f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
824f05cddf9SRui Paulo 		return -1;
825f05cddf9SRui Paulo 
826f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
827f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
828f05cddf9SRui Paulo 			break;
829f05cddf9SRui Paulo 	}
830f05cddf9SRui Paulo 
831f05cddf9SRui Paulo 	if (peer == NULL) {
832f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR
833f05cddf9SRui Paulo 		   " for link Teardown", MAC2STR(addr));
834f05cddf9SRui Paulo 		return -1;
835f05cddf9SRui Paulo 	}
836f05cddf9SRui Paulo 
837f05cddf9SRui Paulo 	if (!peer->tpk_success) {
838f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
839f05cddf9SRui Paulo 		   " not connected - cannot Teardown link", MAC2STR(addr));
840f05cddf9SRui Paulo 		return -1;
841f05cddf9SRui Paulo 	}
842f05cddf9SRui Paulo 
8435b9c547cSRui Paulo 	return wpa_tdls_do_teardown(sm, peer, reason_code);
844f05cddf9SRui Paulo }
845f05cddf9SRui Paulo 
846f05cddf9SRui Paulo 
8475b9c547cSRui Paulo static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
8485b9c547cSRui Paulo 				       struct wpa_tdls_peer *peer)
8495b9c547cSRui Paulo {
8505b9c547cSRui Paulo 	wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
8515b9c547cSRui Paulo 	wpa_tdls_peer_free(sm, peer);
8525b9c547cSRui Paulo }
8535b9c547cSRui Paulo 
8545b9c547cSRui Paulo 
8555b9c547cSRui Paulo void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr)
856f05cddf9SRui Paulo {
857f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
858f05cddf9SRui Paulo 
859f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
860f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
861f05cddf9SRui Paulo 			break;
862f05cddf9SRui Paulo 	}
863f05cddf9SRui Paulo 
8645b9c547cSRui Paulo 	if (!peer || !peer->tpk_success) {
8655b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
8665b9c547cSRui Paulo 			   " not connected - cannot teardown unreachable link",
8675b9c547cSRui Paulo 			   MAC2STR(addr));
8685b9c547cSRui Paulo 		return;
869f05cddf9SRui Paulo 	}
8705b9c547cSRui Paulo 
8715b9c547cSRui Paulo 	if (wpa_tdls_is_external_setup(sm)) {
8725b9c547cSRui Paulo 		/*
8735b9c547cSRui Paulo 		 * Get us on the base channel, disable the link, send a
8745b9c547cSRui Paulo 		 * teardown packet through the AP, and then reset link data.
8755b9c547cSRui Paulo 		 */
8765b9c547cSRui Paulo 		if (peer->chan_switch_enabled)
8775b9c547cSRui Paulo 			wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
8785b9c547cSRui Paulo 		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
8795b9c547cSRui Paulo 		wpa_tdls_send_teardown(sm, addr,
8805b9c547cSRui Paulo 				       WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE);
8815b9c547cSRui Paulo 		wpa_tdls_peer_free(sm, peer);
8825b9c547cSRui Paulo 	} else {
8835b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
8845b9c547cSRui Paulo 	}
8855b9c547cSRui Paulo }
8865b9c547cSRui Paulo 
8875b9c547cSRui Paulo 
8885b9c547cSRui Paulo const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr)
8895b9c547cSRui Paulo {
8905b9c547cSRui Paulo 	struct wpa_tdls_peer *peer;
8915b9c547cSRui Paulo 
8925b9c547cSRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
8935b9c547cSRui Paulo 		return "disabled";
8945b9c547cSRui Paulo 
8955b9c547cSRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
8965b9c547cSRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
8975b9c547cSRui Paulo 			break;
8985b9c547cSRui Paulo 	}
8995b9c547cSRui Paulo 
9005b9c547cSRui Paulo 	if (peer == NULL)
9015b9c547cSRui Paulo 		return "peer does not exist";
9025b9c547cSRui Paulo 
9035b9c547cSRui Paulo 	if (!peer->tpk_success)
9045b9c547cSRui Paulo 		return "peer not connected";
9055b9c547cSRui Paulo 
9065b9c547cSRui Paulo 	return "connected";
907f05cddf9SRui Paulo }
908f05cddf9SRui Paulo 
909f05cddf9SRui Paulo 
910f05cddf9SRui Paulo static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr,
911f05cddf9SRui Paulo 				  const u8 *buf, size_t len)
912f05cddf9SRui Paulo {
913f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer = NULL;
914f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
915f05cddf9SRui Paulo 	struct wpa_tdls_lnkid *lnkid;
916f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
917f05cddf9SRui Paulo 	u16 reason_code;
918f05cddf9SRui Paulo 	const u8 *pos;
919f05cddf9SRui Paulo 	int ielen;
920f05cddf9SRui Paulo 
921f05cddf9SRui Paulo 	/* Find the node and free from the list */
922f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
923f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
924f05cddf9SRui Paulo 			break;
925f05cddf9SRui Paulo 	}
926f05cddf9SRui Paulo 
927f05cddf9SRui Paulo 	if (peer == NULL) {
928f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
929f05cddf9SRui Paulo 			   "Teardown " MACSTR, MAC2STR(src_addr));
930f05cddf9SRui Paulo 		return 0;
931f05cddf9SRui Paulo 	}
932f05cddf9SRui Paulo 
933f05cddf9SRui Paulo 	pos = buf;
934f05cddf9SRui Paulo 	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
935f05cddf9SRui Paulo 
936f05cddf9SRui Paulo 	reason_code = WPA_GET_LE16(pos);
937f05cddf9SRui Paulo 	pos += 2;
938f05cddf9SRui Paulo 
939f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR
940f05cddf9SRui Paulo 		   " (reason code %u)", MAC2STR(src_addr), reason_code);
941f05cddf9SRui Paulo 
942f05cddf9SRui Paulo 	ielen = len - (pos - buf); /* start of IE in buf */
9435b9c547cSRui Paulo 
9445b9c547cSRui Paulo 	/*
9455b9c547cSRui Paulo 	 * Don't reject the message if failing to parse IEs. The IEs we need are
9465b9c547cSRui Paulo 	 * explicitly checked below. Some APs may add arbitrary padding to the
9475b9c547cSRui Paulo 	 * end of short TDLS frames and that would look like invalid IEs.
9485b9c547cSRui Paulo 	 */
9495b9c547cSRui Paulo 	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0)
9505b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
9515b9c547cSRui Paulo 			   "TDLS: Failed to parse IEs in Teardown - ignore as an interop workaround");
952f05cddf9SRui Paulo 
953f05cddf9SRui Paulo 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
954f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS "
955f05cddf9SRui Paulo 			   "Teardown");
956f05cddf9SRui Paulo 		return -1;
957f05cddf9SRui Paulo 	}
958f05cddf9SRui Paulo 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
959f05cddf9SRui Paulo 
960f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
961f05cddf9SRui Paulo 		goto skip_ftie;
962f05cddf9SRui Paulo 
963f05cddf9SRui Paulo 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
964f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown");
965f05cddf9SRui Paulo 		return -1;
966f05cddf9SRui Paulo 	}
967f05cddf9SRui Paulo 
968f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
969f05cddf9SRui Paulo 
970f05cddf9SRui Paulo 	/* Process MIC check to see if TDLS Teardown is right */
971f05cddf9SRui Paulo 	if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
972f05cddf9SRui Paulo 						    peer->dtoken, peer,
973f05cddf9SRui Paulo 						    (u8 *) lnkid, ftie) < 0) {
974f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
975f05cddf9SRui Paulo 			   "Teardown Request from " MACSTR, MAC2STR(src_addr));
976f05cddf9SRui Paulo 		return -1;
977f05cddf9SRui Paulo 	}
978f05cddf9SRui Paulo 
979f05cddf9SRui Paulo skip_ftie:
980f05cddf9SRui Paulo 	/*
981f05cddf9SRui Paulo 	 * Request the driver to disable the direct link and clear associated
982f05cddf9SRui Paulo 	 * keys.
983f05cddf9SRui Paulo 	 */
9845b9c547cSRui Paulo 	wpa_tdls_disable_peer_link(sm, peer);
985f05cddf9SRui Paulo 	return 0;
986f05cddf9SRui Paulo }
987f05cddf9SRui Paulo 
988f05cddf9SRui Paulo 
989f05cddf9SRui Paulo /**
990f05cddf9SRui Paulo  * wpa_tdls_send_error - To send suitable TDLS status response with
991f05cddf9SRui Paulo  *	appropriate status code mentioning reason for error/failure.
992f05cddf9SRui Paulo  * @dst 	- MAC addr of Peer station
993f05cddf9SRui Paulo  * @tdls_action - TDLS frame type for which error code is sent
9945b9c547cSRui Paulo  * @initiator   - was this end the initiator of the connection
995f05cddf9SRui Paulo  * @status 	- status code mentioning reason
996f05cddf9SRui Paulo  */
997f05cddf9SRui Paulo 
998f05cddf9SRui Paulo static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
9995b9c547cSRui Paulo 			       u8 tdls_action, u8 dialog_token, int initiator,
10005b9c547cSRui Paulo 			       u16 status)
1001f05cddf9SRui Paulo {
1002f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR
1003f05cddf9SRui Paulo 		   " (action=%u status=%u)",
1004f05cddf9SRui Paulo 		   MAC2STR(dst), tdls_action, status);
1005f05cddf9SRui Paulo 	return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
10065b9c547cSRui Paulo 				 0, initiator, NULL, 0);
1007f05cddf9SRui Paulo }
1008f05cddf9SRui Paulo 
1009f05cddf9SRui Paulo 
1010f05cddf9SRui Paulo static struct wpa_tdls_peer *
10115b9c547cSRui Paulo wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing)
1012f05cddf9SRui Paulo {
1013f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
1014f05cddf9SRui Paulo 
10155b9c547cSRui Paulo 	if (existing)
10165b9c547cSRui Paulo 		*existing = 0;
10175b9c547cSRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
10185b9c547cSRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) {
10195b9c547cSRui Paulo 			if (existing)
10205b9c547cSRui Paulo 				*existing = 1;
10215b9c547cSRui Paulo 			return peer; /* re-use existing entry */
10225b9c547cSRui Paulo 		}
10235b9c547cSRui Paulo 	}
10245b9c547cSRui Paulo 
1025f05cddf9SRui Paulo 	wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR,
1026f05cddf9SRui Paulo 		   MAC2STR(addr));
1027f05cddf9SRui Paulo 
1028f05cddf9SRui Paulo 	peer = os_zalloc(sizeof(*peer));
1029f05cddf9SRui Paulo 	if (peer == NULL)
1030f05cddf9SRui Paulo 		return NULL;
1031f05cddf9SRui Paulo 
1032f05cddf9SRui Paulo 	os_memcpy(peer->addr, addr, ETH_ALEN);
1033f05cddf9SRui Paulo 	peer->next = sm->tdls;
1034f05cddf9SRui Paulo 	sm->tdls = peer;
1035f05cddf9SRui Paulo 
1036f05cddf9SRui Paulo 	return peer;
1037f05cddf9SRui Paulo }
1038f05cddf9SRui Paulo 
1039f05cddf9SRui Paulo 
1040f05cddf9SRui Paulo static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm,
1041f05cddf9SRui Paulo 				struct wpa_tdls_peer *peer)
1042f05cddf9SRui Paulo {
1043f05cddf9SRui Paulo 	size_t buf_len;
1044f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie timeoutie;
1045f05cddf9SRui Paulo 	u16 rsn_capab;
1046f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
1047f05cddf9SRui Paulo 	u8 *rbuf, *pos, *count_pos;
1048f05cddf9SRui Paulo 	u16 count;
1049f05cddf9SRui Paulo 	struct rsn_ie_hdr *hdr;
10505b9c547cSRui Paulo 	int status;
1051f05cddf9SRui Paulo 
1052f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm)) {
1053f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No security used on the link");
1054f05cddf9SRui Paulo 		peer->rsnie_i_len = 0;
1055f05cddf9SRui Paulo 		goto skip_rsnie;
1056f05cddf9SRui Paulo 	}
1057f05cddf9SRui Paulo 
1058f05cddf9SRui Paulo 	/*
1059f05cddf9SRui Paulo 	 * TPK Handshake Message 1:
1060f05cddf9SRui Paulo 	 * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I,
1061f05cddf9SRui Paulo 	 * Timeout Interval IE))
1062f05cddf9SRui Paulo 	 */
1063f05cddf9SRui Paulo 
1064f05cddf9SRui Paulo 	/* Filling RSN IE */
1065f05cddf9SRui Paulo 	hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
1066f05cddf9SRui Paulo 	hdr->elem_id = WLAN_EID_RSN;
1067f05cddf9SRui Paulo 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
1068f05cddf9SRui Paulo 
1069f05cddf9SRui Paulo 	pos = (u8 *) (hdr + 1);
1070f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
1071f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
1072f05cddf9SRui Paulo 	count_pos = pos;
1073f05cddf9SRui Paulo 	pos += 2;
1074f05cddf9SRui Paulo 
1075f05cddf9SRui Paulo 	count = 0;
1076f05cddf9SRui Paulo 
1077f05cddf9SRui Paulo 	/*
1078f05cddf9SRui Paulo 	 * AES-CCMP is the default Encryption preferred for TDLS, so
1079f05cddf9SRui Paulo 	 * RSN IE is filled only with CCMP CIPHER
1080f05cddf9SRui Paulo 	 * Note: TKIP is not used to encrypt TDLS link.
1081f05cddf9SRui Paulo 	 *
1082f05cddf9SRui Paulo 	 * Regardless of the cipher used on the AP connection, select CCMP
1083f05cddf9SRui Paulo 	 * here.
1084f05cddf9SRui Paulo 	 */
1085f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
1086f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
1087f05cddf9SRui Paulo 	count++;
1088f05cddf9SRui Paulo 
1089f05cddf9SRui Paulo 	WPA_PUT_LE16(count_pos, count);
1090f05cddf9SRui Paulo 
1091f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, 1);
1092f05cddf9SRui Paulo 	pos += 2;
1093f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
1094f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
1095f05cddf9SRui Paulo 
1096f05cddf9SRui Paulo 	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
1097f05cddf9SRui Paulo 	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
1098f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1099f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
1100f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for "
1101f05cddf9SRui Paulo 			   "testing");
1102f05cddf9SRui Paulo 		rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
1103f05cddf9SRui Paulo 	}
1104f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1105f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, rsn_capab);
1106f05cddf9SRui Paulo 	pos += 2;
1107f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1108f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
1109f05cddf9SRui Paulo 		/* Number of PMKIDs */
1110f05cddf9SRui Paulo 		*pos++ = 0x00;
1111f05cddf9SRui Paulo 		*pos++ = 0x00;
1112f05cddf9SRui Paulo 	}
1113f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1114f05cddf9SRui Paulo 
1115f05cddf9SRui Paulo 	hdr->len = (pos - peer->rsnie_i) - 2;
1116f05cddf9SRui Paulo 	peer->rsnie_i_len = pos - peer->rsnie_i;
1117f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
1118f05cddf9SRui Paulo 		    peer->rsnie_i, peer->rsnie_i_len);
1119f05cddf9SRui Paulo 
1120f05cddf9SRui Paulo skip_rsnie:
1121f05cddf9SRui Paulo 	buf_len = 0;
1122f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm))
1123f05cddf9SRui Paulo 		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
1124f05cddf9SRui Paulo 			sizeof(struct wpa_tdls_timeoutie);
1125f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1126f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm) &&
1127f05cddf9SRui Paulo 	    (tdls_testing & TDLS_TESTING_LONG_FRAME))
1128f05cddf9SRui Paulo 		buf_len += 170;
1129f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_DIFF_BSSID)
1130f05cddf9SRui Paulo 		buf_len += sizeof(struct wpa_tdls_lnkid);
1131f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1132f05cddf9SRui Paulo 	rbuf = os_zalloc(buf_len + 1);
1133f05cddf9SRui Paulo 	if (rbuf == NULL) {
1134f05cddf9SRui Paulo 		wpa_tdls_peer_free(sm, peer);
1135f05cddf9SRui Paulo 		return -1;
1136f05cddf9SRui Paulo 	}
1137f05cddf9SRui Paulo 	pos = rbuf;
1138f05cddf9SRui Paulo 
1139f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm))
1140f05cddf9SRui Paulo 		goto skip_ies;
1141f05cddf9SRui Paulo 
1142f05cddf9SRui Paulo 	/* Initiator RSN IE */
1143f05cddf9SRui Paulo 	pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
1144f05cddf9SRui Paulo 
1145f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) pos;
1146f05cddf9SRui Paulo 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
1147f05cddf9SRui Paulo 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
1148f05cddf9SRui Paulo 
1149f05cddf9SRui Paulo 	if (os_get_random(peer->inonce, WPA_NONCE_LEN)) {
1150f05cddf9SRui Paulo 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
1151f05cddf9SRui Paulo 			"TDLS: Failed to get random data for initiator Nonce");
1152f05cddf9SRui Paulo 		os_free(rbuf);
1153f05cddf9SRui Paulo 		wpa_tdls_peer_free(sm, peer);
1154f05cddf9SRui Paulo 		return -1;
1155f05cddf9SRui Paulo 	}
1156f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake",
1157f05cddf9SRui Paulo 		    peer->inonce, WPA_NONCE_LEN);
1158f05cddf9SRui Paulo 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
1159f05cddf9SRui Paulo 
1160f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1",
1161f05cddf9SRui Paulo 		    (u8 *) ftie, sizeof(struct wpa_tdls_ftie));
1162f05cddf9SRui Paulo 
1163f05cddf9SRui Paulo 	pos = (u8 *) (ftie + 1);
1164f05cddf9SRui Paulo 
1165f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1166f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
1167f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
1168f05cddf9SRui Paulo 			   "FTIE");
1169f05cddf9SRui Paulo 		ftie->ie_len += 170;
1170f05cddf9SRui Paulo 		*pos++ = 255; /* FTIE subelem */
1171f05cddf9SRui Paulo 		*pos++ = 168; /* FTIE subelem length */
1172f05cddf9SRui Paulo 		pos += 168;
1173f05cddf9SRui Paulo 	}
1174f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1175f05cddf9SRui Paulo 
1176f05cddf9SRui Paulo 	/* Lifetime */
1177f05cddf9SRui Paulo 	peer->lifetime = TPK_LIFETIME;
1178f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1179f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) {
1180f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK "
1181f05cddf9SRui Paulo 			   "lifetime");
1182f05cddf9SRui Paulo 		peer->lifetime = 301;
1183f05cddf9SRui Paulo 	}
1184f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) {
1185f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK "
1186f05cddf9SRui Paulo 			   "lifetime");
1187f05cddf9SRui Paulo 		peer->lifetime = 0xffffffff;
1188f05cddf9SRui Paulo 	}
1189f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1190f05cddf9SRui Paulo 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
1191f05cddf9SRui Paulo 				     sizeof(timeoutie), peer->lifetime);
1192f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
1193f05cddf9SRui Paulo 
1194f05cddf9SRui Paulo skip_ies:
1195f05cddf9SRui Paulo 
1196f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1197f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_DIFF_BSSID) {
1198f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in "
1199f05cddf9SRui Paulo 			   "Link Identifier");
1200f05cddf9SRui Paulo 		struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
1201f05cddf9SRui Paulo 		wpa_tdls_linkid(sm, peer, l);
1202f05cddf9SRui Paulo 		l->bssid[5] ^= 0x01;
1203f05cddf9SRui Paulo 		pos += sizeof(*l);
1204f05cddf9SRui Paulo 	}
1205f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1206f05cddf9SRui Paulo 
1207f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK "
1208f05cddf9SRui Paulo 		   "Handshake Message 1 (peer " MACSTR ")",
1209f05cddf9SRui Paulo 		   MAC2STR(peer->addr));
1210f05cddf9SRui Paulo 
12115b9c547cSRui Paulo 	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST,
12125b9c547cSRui Paulo 				   1, 0, 0, peer->initiator, rbuf, pos - rbuf);
1213f05cddf9SRui Paulo 	os_free(rbuf);
1214f05cddf9SRui Paulo 
12155b9c547cSRui Paulo 	return status;
1216f05cddf9SRui Paulo }
1217f05cddf9SRui Paulo 
1218f05cddf9SRui Paulo 
1219f05cddf9SRui Paulo static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm,
1220f05cddf9SRui Paulo 				const unsigned char *src_addr, u8 dtoken,
1221f05cddf9SRui Paulo 				struct wpa_tdls_lnkid *lnkid,
1222f05cddf9SRui Paulo 				const struct wpa_tdls_peer *peer)
1223f05cddf9SRui Paulo {
1224f05cddf9SRui Paulo 	u8 *rbuf, *pos;
1225f05cddf9SRui Paulo 	size_t buf_len;
1226f05cddf9SRui Paulo 	u32 lifetime;
1227f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie timeoutie;
1228f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
12295b9c547cSRui Paulo 	int status;
1230f05cddf9SRui Paulo 
1231f05cddf9SRui Paulo 	buf_len = 0;
1232f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm)) {
1233f05cddf9SRui Paulo 		/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
1234f05cddf9SRui Paulo 		 * Lifetime */
1235f05cddf9SRui Paulo 		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
1236f05cddf9SRui Paulo 			sizeof(struct wpa_tdls_timeoutie);
1237f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1238f05cddf9SRui Paulo 		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
1239f05cddf9SRui Paulo 			buf_len += 170;
1240f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1241f05cddf9SRui Paulo 	}
1242f05cddf9SRui Paulo 
1243f05cddf9SRui Paulo 	rbuf = os_zalloc(buf_len + 1);
1244f05cddf9SRui Paulo 	if (rbuf == NULL)
1245f05cddf9SRui Paulo 		return -1;
1246f05cddf9SRui Paulo 	pos = rbuf;
1247f05cddf9SRui Paulo 
1248f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm))
1249f05cddf9SRui Paulo 		goto skip_ies;
1250f05cddf9SRui Paulo 
1251f05cddf9SRui Paulo 	/* Peer RSN IE */
1252f05cddf9SRui Paulo 	pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
1253f05cddf9SRui Paulo 
1254f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) pos;
1255f05cddf9SRui Paulo 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
1256f05cddf9SRui Paulo 	/* TODO: ftie->mic_control to set 2-RESPONSE */
1257f05cddf9SRui Paulo 	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
1258f05cddf9SRui Paulo 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
1259f05cddf9SRui Paulo 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
1260f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2",
1261f05cddf9SRui Paulo 		    (u8 *) ftie, sizeof(*ftie));
1262f05cddf9SRui Paulo 
1263f05cddf9SRui Paulo 	pos = (u8 *) (ftie + 1);
1264f05cddf9SRui Paulo 
1265f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1266f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
1267f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
1268f05cddf9SRui Paulo 			   "FTIE");
1269f05cddf9SRui Paulo 		ftie->ie_len += 170;
1270f05cddf9SRui Paulo 		*pos++ = 255; /* FTIE subelem */
1271f05cddf9SRui Paulo 		*pos++ = 168; /* FTIE subelem length */
1272f05cddf9SRui Paulo 		pos += 168;
1273f05cddf9SRui Paulo 	}
1274f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1275f05cddf9SRui Paulo 
1276f05cddf9SRui Paulo 	/* Lifetime */
1277f05cddf9SRui Paulo 	lifetime = peer->lifetime;
1278f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1279f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) {
1280f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
1281f05cddf9SRui Paulo 			   "lifetime in response");
1282f05cddf9SRui Paulo 		lifetime++;
1283f05cddf9SRui Paulo 	}
1284f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1285f05cddf9SRui Paulo 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
1286f05cddf9SRui Paulo 				     sizeof(timeoutie), lifetime);
1287f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator",
1288f05cddf9SRui Paulo 		   lifetime);
1289f05cddf9SRui Paulo 
1290f05cddf9SRui Paulo 	/* compute MIC before sending */
1291f05cddf9SRui Paulo 	wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
1292f05cddf9SRui Paulo 			  (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
12935b9c547cSRui Paulo #ifdef CONFIG_TDLS_TESTING
12945b9c547cSRui Paulo 	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
12955b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
12965b9c547cSRui Paulo 		ftie->mic[0] ^= 0x01;
12975b9c547cSRui Paulo 	}
12985b9c547cSRui Paulo #endif /* CONFIG_TDLS_TESTING */
1299f05cddf9SRui Paulo 
1300f05cddf9SRui Paulo skip_ies:
13015b9c547cSRui Paulo 	status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
13025b9c547cSRui Paulo 				   dtoken, 0, 0, peer->initiator, rbuf,
13035b9c547cSRui Paulo 				   pos - rbuf);
1304f05cddf9SRui Paulo 	os_free(rbuf);
1305f05cddf9SRui Paulo 
13065b9c547cSRui Paulo 	return status;
1307f05cddf9SRui Paulo }
1308f05cddf9SRui Paulo 
1309f05cddf9SRui Paulo 
1310f05cddf9SRui Paulo static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm,
1311f05cddf9SRui Paulo 				const unsigned char *src_addr, u8 dtoken,
1312f05cddf9SRui Paulo 				struct wpa_tdls_lnkid *lnkid,
1313f05cddf9SRui Paulo 				const struct wpa_tdls_peer *peer)
1314f05cddf9SRui Paulo {
1315f05cddf9SRui Paulo 	u8 *rbuf, *pos;
1316f05cddf9SRui Paulo 	size_t buf_len;
1317f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
1318f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie timeoutie;
1319f05cddf9SRui Paulo 	u32 lifetime;
13205b9c547cSRui Paulo 	int status;
13215b9c547cSRui Paulo 	u32 peer_capab = 0;
1322f05cddf9SRui Paulo 
1323f05cddf9SRui Paulo 	buf_len = 0;
1324f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm)) {
1325f05cddf9SRui Paulo 		/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
1326f05cddf9SRui Paulo 		 * Lifetime */
1327f05cddf9SRui Paulo 		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
1328f05cddf9SRui Paulo 			sizeof(struct wpa_tdls_timeoutie);
1329f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1330f05cddf9SRui Paulo 		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
1331f05cddf9SRui Paulo 			buf_len += 170;
1332f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1333f05cddf9SRui Paulo 	}
1334f05cddf9SRui Paulo 
1335f05cddf9SRui Paulo 	rbuf = os_zalloc(buf_len + 1);
1336f05cddf9SRui Paulo 	if (rbuf == NULL)
1337f05cddf9SRui Paulo 		return -1;
1338f05cddf9SRui Paulo 	pos = rbuf;
1339f05cddf9SRui Paulo 
1340f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm))
1341f05cddf9SRui Paulo 		goto skip_ies;
1342f05cddf9SRui Paulo 
1343f05cddf9SRui Paulo 	/* Peer RSN IE */
1344f05cddf9SRui Paulo 	pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
1345f05cddf9SRui Paulo 
1346f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) pos;
1347f05cddf9SRui Paulo 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
1348f05cddf9SRui Paulo 	/*TODO: ftie->mic_control to set 3-CONFIRM */
1349f05cddf9SRui Paulo 	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
1350f05cddf9SRui Paulo 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
1351f05cddf9SRui Paulo 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
1352f05cddf9SRui Paulo 
1353f05cddf9SRui Paulo 	pos = (u8 *) (ftie + 1);
1354f05cddf9SRui Paulo 
1355f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1356f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
1357f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
1358f05cddf9SRui Paulo 			   "FTIE");
1359f05cddf9SRui Paulo 		ftie->ie_len += 170;
1360f05cddf9SRui Paulo 		*pos++ = 255; /* FTIE subelem */
1361f05cddf9SRui Paulo 		*pos++ = 168; /* FTIE subelem length */
1362f05cddf9SRui Paulo 		pos += 168;
1363f05cddf9SRui Paulo 	}
1364f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1365f05cddf9SRui Paulo 
1366f05cddf9SRui Paulo 	/* Lifetime */
1367f05cddf9SRui Paulo 	lifetime = peer->lifetime;
1368f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1369f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) {
1370f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
1371f05cddf9SRui Paulo 			   "lifetime in confirm");
1372f05cddf9SRui Paulo 		lifetime++;
1373f05cddf9SRui Paulo 	}
1374f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1375f05cddf9SRui Paulo 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
1376f05cddf9SRui Paulo 				     sizeof(timeoutie), lifetime);
1377f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds",
1378f05cddf9SRui Paulo 		   lifetime);
1379f05cddf9SRui Paulo 
1380f05cddf9SRui Paulo 	/* compute MIC before sending */
1381f05cddf9SRui Paulo 	wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
1382f05cddf9SRui Paulo 			  (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
13835b9c547cSRui Paulo #ifdef CONFIG_TDLS_TESTING
13845b9c547cSRui Paulo 	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
13855b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
13865b9c547cSRui Paulo 		ftie->mic[0] ^= 0x01;
13875b9c547cSRui Paulo 	}
13885b9c547cSRui Paulo #endif /* CONFIG_TDLS_TESTING */
1389f05cddf9SRui Paulo 
1390f05cddf9SRui Paulo skip_ies:
13915b9c547cSRui Paulo 
13925b9c547cSRui Paulo 	if (peer->vht_capabilities)
13935b9c547cSRui Paulo 		peer_capab |= TDLS_PEER_VHT;
13945b9c547cSRui Paulo 	if (peer->ht_capabilities)
13955b9c547cSRui Paulo 		peer_capab |= TDLS_PEER_HT;
13965b9c547cSRui Paulo 	if (peer->wmm_capable)
13975b9c547cSRui Paulo 		peer_capab |= TDLS_PEER_WMM;
13985b9c547cSRui Paulo 
13995b9c547cSRui Paulo 	status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
14005b9c547cSRui Paulo 				   dtoken, 0, peer_capab, peer->initiator,
1401f05cddf9SRui Paulo 				   rbuf, pos - rbuf);
1402f05cddf9SRui Paulo 	os_free(rbuf);
1403f05cddf9SRui Paulo 
14045b9c547cSRui Paulo 	return status;
1405f05cddf9SRui Paulo }
1406f05cddf9SRui Paulo 
1407f05cddf9SRui Paulo 
1408f05cddf9SRui Paulo static int wpa_tdls_send_discovery_response(struct wpa_sm *sm,
1409f05cddf9SRui Paulo 					    struct wpa_tdls_peer *peer,
1410f05cddf9SRui Paulo 					    u8 dialog_token)
1411f05cddf9SRui Paulo {
14125b9c547cSRui Paulo 	size_t buf_len = 0;
14135b9c547cSRui Paulo 	struct wpa_tdls_timeoutie timeoutie;
14145b9c547cSRui Paulo 	u16 rsn_capab;
14155b9c547cSRui Paulo 	u8 *rbuf, *pos, *count_pos;
14165b9c547cSRui Paulo 	u16 count;
14175b9c547cSRui Paulo 	struct rsn_ie_hdr *hdr;
14185b9c547cSRui Paulo 	int status;
14195b9c547cSRui Paulo 
1420f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response "
1421f05cddf9SRui Paulo 		   "(peer " MACSTR ")", MAC2STR(peer->addr));
14225b9c547cSRui Paulo 	if (!wpa_tdls_get_privacy(sm))
14235b9c547cSRui Paulo 		goto skip_rsn_ies;
1424f05cddf9SRui Paulo 
14255b9c547cSRui Paulo 	/* Filling RSN IE */
14265b9c547cSRui Paulo 	hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
14275b9c547cSRui Paulo 	hdr->elem_id = WLAN_EID_RSN;
14285b9c547cSRui Paulo 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
14295b9c547cSRui Paulo 	pos = (u8 *) (hdr + 1);
14305b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
14315b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
14325b9c547cSRui Paulo 	count_pos = pos;
14335b9c547cSRui Paulo 	pos += 2;
14345b9c547cSRui Paulo 	count = 0;
14355b9c547cSRui Paulo 
14365b9c547cSRui Paulo 	/*
14375b9c547cSRui Paulo 	* AES-CCMP is the default encryption preferred for TDLS, so
14385b9c547cSRui Paulo 	* RSN IE is filled only with CCMP cipher suite.
14395b9c547cSRui Paulo 	* Note: TKIP is not used to encrypt TDLS link.
14405b9c547cSRui Paulo 	*
14415b9c547cSRui Paulo 	* Regardless of the cipher used on the AP connection, select CCMP
14425b9c547cSRui Paulo 	* here.
14435b9c547cSRui Paulo 	*/
14445b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
14455b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
14465b9c547cSRui Paulo 	count++;
14475b9c547cSRui Paulo 	WPA_PUT_LE16(count_pos, count);
14485b9c547cSRui Paulo 	WPA_PUT_LE16(pos, 1);
14495b9c547cSRui Paulo 	pos += 2;
14505b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
14515b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
14525b9c547cSRui Paulo 
14535b9c547cSRui Paulo 	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
14545b9c547cSRui Paulo 	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
14555b9c547cSRui Paulo 	WPA_PUT_LE16(pos, rsn_capab);
14565b9c547cSRui Paulo 	pos += 2;
14575b9c547cSRui Paulo 	hdr->len = (pos - (u8 *) hdr) - 2;
14585b9c547cSRui Paulo 	peer->rsnie_i_len = pos - peer->rsnie_i;
14595b9c547cSRui Paulo 
14605b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response",
14615b9c547cSRui Paulo 		    (u8 *) hdr, hdr->len + 2);
14625b9c547cSRui Paulo skip_rsn_ies:
14635b9c547cSRui Paulo 	buf_len = 0;
14645b9c547cSRui Paulo 	if (wpa_tdls_get_privacy(sm)) {
14655b9c547cSRui Paulo 		/* Peer RSN IE, Lifetime */
14665b9c547cSRui Paulo 		buf_len += peer->rsnie_i_len +
14675b9c547cSRui Paulo 			sizeof(struct wpa_tdls_timeoutie);
14685b9c547cSRui Paulo 	}
14695b9c547cSRui Paulo 	rbuf = os_zalloc(buf_len + 1);
14705b9c547cSRui Paulo 	if (rbuf == NULL) {
14715b9c547cSRui Paulo 		wpa_tdls_peer_free(sm, peer);
14725b9c547cSRui Paulo 		return -1;
14735b9c547cSRui Paulo 	}
14745b9c547cSRui Paulo 	pos = rbuf;
14755b9c547cSRui Paulo 
14765b9c547cSRui Paulo 	if (!wpa_tdls_get_privacy(sm))
14775b9c547cSRui Paulo 		goto skip_ies;
14785b9c547cSRui Paulo 	/* Initiator RSN IE */
14795b9c547cSRui Paulo 	pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
14805b9c547cSRui Paulo 	/* Lifetime */
14815b9c547cSRui Paulo 	peer->lifetime = TPK_LIFETIME;
14825b9c547cSRui Paulo 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
14835b9c547cSRui Paulo 				     sizeof(timeoutie), peer->lifetime);
14845b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
14855b9c547cSRui Paulo skip_ies:
14865b9c547cSRui Paulo 	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
14875b9c547cSRui Paulo 				   dialog_token, 0, 0, 0, rbuf, pos - rbuf);
14885b9c547cSRui Paulo 	os_free(rbuf);
14895b9c547cSRui Paulo 
14905b9c547cSRui Paulo 	return status;
1491f05cddf9SRui Paulo }
1492f05cddf9SRui Paulo 
1493f05cddf9SRui Paulo 
1494f05cddf9SRui Paulo static int
1495f05cddf9SRui Paulo wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
1496f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
1497f05cddf9SRui Paulo {
1498f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
1499f05cddf9SRui Paulo 	const struct wpa_tdls_lnkid *lnkid;
1500f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
1501f05cddf9SRui Paulo 	size_t min_req_len = sizeof(struct wpa_tdls_frame) +
1502f05cddf9SRui Paulo 		1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid);
1503f05cddf9SRui Paulo 	u8 dialog_token;
1504f05cddf9SRui Paulo 
1505f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR,
1506f05cddf9SRui Paulo 		   MAC2STR(addr));
1507f05cddf9SRui Paulo 
1508f05cddf9SRui Paulo 	if (len < min_req_len) {
1509f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: "
1510f05cddf9SRui Paulo 			   "%d", (int) len);
1511f05cddf9SRui Paulo 		return -1;
1512f05cddf9SRui Paulo 	}
1513f05cddf9SRui Paulo 
1514f05cddf9SRui Paulo 	dialog_token = buf[sizeof(struct wpa_tdls_frame)];
1515f05cddf9SRui Paulo 
15165b9c547cSRui Paulo 	/*
15175b9c547cSRui Paulo 	 * Some APs will tack on a weird IE to the end of a TDLS
15185b9c547cSRui Paulo 	 * discovery request packet. This needn't fail the response,
15195b9c547cSRui Paulo 	 * since the required IE are verified separately.
15205b9c547cSRui Paulo 	 */
1521f05cddf9SRui Paulo 	if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
1522f05cddf9SRui Paulo 				     len - (sizeof(struct wpa_tdls_frame) + 1),
15235b9c547cSRui Paulo 				     &kde) < 0) {
15245b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
15255b9c547cSRui Paulo 			   "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround");
15265b9c547cSRui Paulo 	}
1527f05cddf9SRui Paulo 
1528f05cddf9SRui Paulo 	if (!kde.lnkid) {
1529f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
1530f05cddf9SRui Paulo 			   "Request");
1531f05cddf9SRui Paulo 		return -1;
1532f05cddf9SRui Paulo 	}
1533f05cddf9SRui Paulo 
1534f05cddf9SRui Paulo 	lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid;
1535f05cddf9SRui Paulo 
1536f05cddf9SRui Paulo 	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
1537f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different "
1538f05cddf9SRui Paulo 			   " BSS " MACSTR, MAC2STR(lnkid->bssid));
1539f05cddf9SRui Paulo 		return -1;
1540f05cddf9SRui Paulo 	}
1541f05cddf9SRui Paulo 
15425b9c547cSRui Paulo 	peer = wpa_tdls_add_peer(sm, addr, NULL);
1543f05cddf9SRui Paulo 	if (peer == NULL)
1544f05cddf9SRui Paulo 		return -1;
1545f05cddf9SRui Paulo 
1546f05cddf9SRui Paulo 	return wpa_tdls_send_discovery_response(sm, peer, dialog_token);
1547f05cddf9SRui Paulo }
1548f05cddf9SRui Paulo 
1549f05cddf9SRui Paulo 
1550f05cddf9SRui Paulo int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr)
1551f05cddf9SRui Paulo {
1552f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
1553f05cddf9SRui Paulo 		return -1;
1554f05cddf9SRui Paulo 
1555f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer "
1556f05cddf9SRui Paulo 		   MACSTR, MAC2STR(addr));
1557f05cddf9SRui Paulo 	return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST,
15585b9c547cSRui Paulo 				 1, 0, 0, 1, NULL, 0);
1559f05cddf9SRui Paulo }
1560f05cddf9SRui Paulo 
1561f05cddf9SRui Paulo 
1562f05cddf9SRui Paulo static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde,
1563f05cddf9SRui Paulo 			   struct wpa_tdls_peer *peer)
1564f05cddf9SRui Paulo {
1565f05cddf9SRui Paulo 	if (!kde->supp_rates) {
1566f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
1567f05cddf9SRui Paulo 		return -1;
1568f05cddf9SRui Paulo 	}
15695b9c547cSRui Paulo 	peer->supp_rates_len = merge_byte_arrays(
15705b9c547cSRui Paulo 		peer->supp_rates, sizeof(peer->supp_rates),
15715b9c547cSRui Paulo 		kde->supp_rates + 2, kde->supp_rates_len - 2,
15725b9c547cSRui Paulo 		kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL,
15735b9c547cSRui Paulo 		kde->ext_supp_rates_len - 2);
15745b9c547cSRui Paulo 	return 0;
1575f05cddf9SRui Paulo }
1576f05cddf9SRui Paulo 
15775b9c547cSRui Paulo 
15785b9c547cSRui Paulo static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde,
15795b9c547cSRui Paulo 			      struct wpa_tdls_peer *peer)
15805b9c547cSRui Paulo {
1581*325151a3SRui Paulo 	if (!kde->ht_capabilities) {
15825b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities "
15835b9c547cSRui Paulo 			   "received");
1584f05cddf9SRui Paulo 		return 0;
1585f05cddf9SRui Paulo 	}
1586f05cddf9SRui Paulo 
15875b9c547cSRui Paulo 	if (!peer->ht_capabilities) {
15885b9c547cSRui Paulo 		peer->ht_capabilities =
15895b9c547cSRui Paulo                         os_zalloc(sizeof(struct ieee80211_ht_capabilities));
15905b9c547cSRui Paulo 		if (peer->ht_capabilities == NULL)
15915b9c547cSRui Paulo                         return -1;
15925b9c547cSRui Paulo 	}
15935b9c547cSRui Paulo 
15945b9c547cSRui Paulo 	os_memcpy(peer->ht_capabilities, kde->ht_capabilities,
15955b9c547cSRui Paulo                   sizeof(struct ieee80211_ht_capabilities));
15965b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities",
15975b9c547cSRui Paulo 		    (u8 *) peer->ht_capabilities,
15985b9c547cSRui Paulo 		    sizeof(struct ieee80211_ht_capabilities));
15995b9c547cSRui Paulo 
16005b9c547cSRui Paulo 	return 0;
16015b9c547cSRui Paulo }
16025b9c547cSRui Paulo 
16035b9c547cSRui Paulo 
16045b9c547cSRui Paulo static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde,
16055b9c547cSRui Paulo 			      struct wpa_tdls_peer *peer)
16065b9c547cSRui Paulo {
1607*325151a3SRui Paulo 	if (!kde->vht_capabilities) {
16085b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities "
16095b9c547cSRui Paulo 			   "received");
16105b9c547cSRui Paulo 		return 0;
16115b9c547cSRui Paulo 	}
16125b9c547cSRui Paulo 
16135b9c547cSRui Paulo 	if (!peer->vht_capabilities) {
16145b9c547cSRui Paulo 		peer->vht_capabilities =
16155b9c547cSRui Paulo                         os_zalloc(sizeof(struct ieee80211_vht_capabilities));
16165b9c547cSRui Paulo 		if (peer->vht_capabilities == NULL)
16175b9c547cSRui Paulo                         return -1;
16185b9c547cSRui Paulo 	}
16195b9c547cSRui Paulo 
16205b9c547cSRui Paulo 	os_memcpy(peer->vht_capabilities, kde->vht_capabilities,
16215b9c547cSRui Paulo                   sizeof(struct ieee80211_vht_capabilities));
16225b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities",
16235b9c547cSRui Paulo 		    (u8 *) peer->vht_capabilities,
16245b9c547cSRui Paulo 		    sizeof(struct ieee80211_vht_capabilities));
16255b9c547cSRui Paulo 
16265b9c547cSRui Paulo 	return 0;
16275b9c547cSRui Paulo }
16285b9c547cSRui Paulo 
16295b9c547cSRui Paulo 
16305b9c547cSRui Paulo static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde,
16315b9c547cSRui Paulo 			       struct wpa_tdls_peer *peer)
16325b9c547cSRui Paulo {
16335b9c547cSRui Paulo 	if (!kde->ext_capab) {
16345b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities "
16355b9c547cSRui Paulo 			   "received");
16365b9c547cSRui Paulo 		return 0;
16375b9c547cSRui Paulo 	}
16385b9c547cSRui Paulo 
16395b9c547cSRui Paulo 	if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) {
16405b9c547cSRui Paulo 		/* Need to allocate buffer to fit the new information */
16415b9c547cSRui Paulo 		os_free(peer->ext_capab);
16425b9c547cSRui Paulo 		peer->ext_capab = os_zalloc(kde->ext_capab_len - 2);
16435b9c547cSRui Paulo 		if (peer->ext_capab == NULL)
16445b9c547cSRui Paulo 			return -1;
16455b9c547cSRui Paulo 	}
16465b9c547cSRui Paulo 
16475b9c547cSRui Paulo 	peer->ext_capab_len = kde->ext_capab_len - 2;
16485b9c547cSRui Paulo 	os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len);
16495b9c547cSRui Paulo 
16505b9c547cSRui Paulo 	return 0;
16515b9c547cSRui Paulo }
16525b9c547cSRui Paulo 
16535b9c547cSRui Paulo 
16545b9c547cSRui Paulo static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde,
16555b9c547cSRui Paulo 			       struct wpa_tdls_peer *peer)
16565b9c547cSRui Paulo {
16575b9c547cSRui Paulo 	struct wmm_information_element *wmm;
16585b9c547cSRui Paulo 
16595b9c547cSRui Paulo 	if (!kde->wmm) {
16605b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported WMM capabilities received");
16615b9c547cSRui Paulo 		return 0;
16625b9c547cSRui Paulo 	}
16635b9c547cSRui Paulo 
16645b9c547cSRui Paulo 	if (kde->wmm_len < sizeof(struct wmm_information_element)) {
16655b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Invalid supported WMM capabilities received");
16665b9c547cSRui Paulo 		return -1;
16675b9c547cSRui Paulo 	}
16685b9c547cSRui Paulo 
16695b9c547cSRui Paulo 	wmm = (struct wmm_information_element *) kde->wmm;
16705b9c547cSRui Paulo 	peer->qos_info = wmm->qos_info;
16715b9c547cSRui Paulo 
16725b9c547cSRui Paulo 	peer->wmm_capable = 1;
16735b9c547cSRui Paulo 
16745b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Peer WMM QOS Info 0x%x", peer->qos_info);
16755b9c547cSRui Paulo 	return 0;
16765b9c547cSRui Paulo }
16775b9c547cSRui Paulo 
16785b9c547cSRui Paulo 
16795b9c547cSRui Paulo static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde,
16805b9c547cSRui Paulo 				   struct wpa_tdls_peer *peer)
16815b9c547cSRui Paulo {
16825b9c547cSRui Paulo 	if (!kde->supp_channels) {
16835b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported channels received");
16845b9c547cSRui Paulo 		return 0;
16855b9c547cSRui Paulo 	}
16865b9c547cSRui Paulo 
16875b9c547cSRui Paulo 	if (!peer->supp_channels ||
16885b9c547cSRui Paulo 	    peer->supp_channels_len < kde->supp_channels_len) {
16895b9c547cSRui Paulo 		os_free(peer->supp_channels);
16905b9c547cSRui Paulo 		peer->supp_channels = os_zalloc(kde->supp_channels_len);
16915b9c547cSRui Paulo 		if (peer->supp_channels == NULL)
16925b9c547cSRui Paulo 			return -1;
16935b9c547cSRui Paulo 	}
16945b9c547cSRui Paulo 
16955b9c547cSRui Paulo 	peer->supp_channels_len = kde->supp_channels_len;
16965b9c547cSRui Paulo 
16975b9c547cSRui Paulo 	os_memcpy(peer->supp_channels, kde->supp_channels,
16985b9c547cSRui Paulo 		  peer->supp_channels_len);
16995b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels",
17005b9c547cSRui Paulo 		    (u8 *) peer->supp_channels, peer->supp_channels_len);
17015b9c547cSRui Paulo 	return 0;
17025b9c547cSRui Paulo }
17035b9c547cSRui Paulo 
17045b9c547cSRui Paulo 
17055b9c547cSRui Paulo static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde,
17065b9c547cSRui Paulo 				       struct wpa_tdls_peer *peer)
17075b9c547cSRui Paulo {
17085b9c547cSRui Paulo 	if (!kde->supp_oper_classes) {
17095b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received");
17105b9c547cSRui Paulo 		return 0;
17115b9c547cSRui Paulo 	}
17125b9c547cSRui Paulo 
17135b9c547cSRui Paulo 	if (!peer->supp_oper_classes ||
17145b9c547cSRui Paulo 	    peer->supp_oper_classes_len < kde->supp_oper_classes_len) {
17155b9c547cSRui Paulo 		os_free(peer->supp_oper_classes);
17165b9c547cSRui Paulo 		peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len);
17175b9c547cSRui Paulo 		if (peer->supp_oper_classes == NULL)
17185b9c547cSRui Paulo 			return -1;
17195b9c547cSRui Paulo 	}
17205b9c547cSRui Paulo 
17215b9c547cSRui Paulo 	peer->supp_oper_classes_len = kde->supp_oper_classes_len;
17225b9c547cSRui Paulo 	os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes,
17235b9c547cSRui Paulo 		  peer->supp_oper_classes_len);
17245b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes",
17255b9c547cSRui Paulo 		    (u8 *) peer->supp_oper_classes,
17265b9c547cSRui Paulo 		    peer->supp_oper_classes_len);
17275b9c547cSRui Paulo 	return 0;
17285b9c547cSRui Paulo }
17295b9c547cSRui Paulo 
17305b9c547cSRui Paulo 
17315b9c547cSRui Paulo static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
17325b9c547cSRui Paulo 				int add)
17335b9c547cSRui Paulo {
17345b9c547cSRui Paulo 	return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid,
17355b9c547cSRui Paulo 				       peer->capability,
17365b9c547cSRui Paulo 				       peer->supp_rates, peer->supp_rates_len,
17375b9c547cSRui Paulo 				       peer->ht_capabilities,
17385b9c547cSRui Paulo 				       peer->vht_capabilities,
17395b9c547cSRui Paulo 				       peer->qos_info, peer->wmm_capable,
17405b9c547cSRui Paulo 				       peer->ext_capab, peer->ext_capab_len,
17415b9c547cSRui Paulo 				       peer->supp_channels,
17425b9c547cSRui Paulo 				       peer->supp_channels_len,
17435b9c547cSRui Paulo 				       peer->supp_oper_classes,
17445b9c547cSRui Paulo 				       peer->supp_oper_classes_len);
17455b9c547cSRui Paulo }
17465b9c547cSRui Paulo 
1747f05cddf9SRui Paulo 
1748f05cddf9SRui Paulo static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
1749f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
1750f05cddf9SRui Paulo {
1751f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
1752f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
1753f05cddf9SRui Paulo 	struct wpa_ie_data ie;
1754f05cddf9SRui Paulo 	int cipher;
1755f05cddf9SRui Paulo 	const u8 *cpos;
1756f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie = NULL;
1757f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie *timeoutie;
1758f05cddf9SRui Paulo 	struct wpa_tdls_lnkid *lnkid;
1759f05cddf9SRui Paulo 	u32 lifetime = 0;
1760f05cddf9SRui Paulo #if 0
1761f05cddf9SRui Paulo 	struct rsn_ie_hdr *hdr;
1762f05cddf9SRui Paulo 	u8 *pos;
1763f05cddf9SRui Paulo 	u16 rsn_capab;
1764f05cddf9SRui Paulo 	u16 rsn_ver;
1765f05cddf9SRui Paulo #endif
1766f05cddf9SRui Paulo 	u8 dtoken;
1767f05cddf9SRui Paulo 	u16 ielen;
1768f05cddf9SRui Paulo 	u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
1769f05cddf9SRui Paulo 	int tdls_prohibited = sm->tdls_prohibited;
1770f05cddf9SRui Paulo 	int existing_peer = 0;
1771f05cddf9SRui Paulo 
1772f05cddf9SRui Paulo 	if (len < 3 + 3)
1773f05cddf9SRui Paulo 		return -1;
1774f05cddf9SRui Paulo 
1775f05cddf9SRui Paulo 	cpos = buf;
1776f05cddf9SRui Paulo 	cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
1777f05cddf9SRui Paulo 
1778f05cddf9SRui Paulo 	/* driver had already verified the frame format */
1779f05cddf9SRui Paulo 	dtoken = *cpos++; /* dialog token */
1780f05cddf9SRui Paulo 
1781f05cddf9SRui Paulo 	wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken);
1782f05cddf9SRui Paulo 
17835b9c547cSRui Paulo 	peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer);
1784f05cddf9SRui Paulo 	if (peer == NULL)
1785f05cddf9SRui Paulo 		goto error;
17865b9c547cSRui Paulo 
17875b9c547cSRui Paulo 	/* If found, use existing entry instead of adding a new one;
17885b9c547cSRui Paulo 	 * how to handle the case where both ends initiate at the
17895b9c547cSRui Paulo 	 * same time? */
17905b9c547cSRui Paulo 	if (existing_peer) {
17915b9c547cSRui Paulo 		if (peer->tpk_success) {
17925b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
17935b9c547cSRui Paulo 				   "direct link is enabled - tear down the "
17945b9c547cSRui Paulo 				   "old link first");
17955b9c547cSRui Paulo 			wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
17965b9c547cSRui Paulo 			wpa_tdls_peer_clear(sm, peer);
17975b9c547cSRui Paulo 		} else if (peer->initiator) {
17985b9c547cSRui Paulo 			/*
17995b9c547cSRui Paulo 			 * An entry is already present, so check if we already
18005b9c547cSRui Paulo 			 * sent a TDLS Setup Request. If so, compare MAC
18015b9c547cSRui Paulo 			 * addresses and let the STA with the lower MAC address
18025b9c547cSRui Paulo 			 * continue as the initiator. The other negotiation is
18035b9c547cSRui Paulo 			 * terminated.
18045b9c547cSRui Paulo 			 */
18055b9c547cSRui Paulo 			if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
18065b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "TDLS: Discard request "
18075b9c547cSRui Paulo 					   "from peer with higher address "
18085b9c547cSRui Paulo 					   MACSTR, MAC2STR(src_addr));
18095b9c547cSRui Paulo 				return -1;
18105b9c547cSRui Paulo 			} else {
18115b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "TDLS: Accept request "
18125b9c547cSRui Paulo 					   "from peer with lower address "
18135b9c547cSRui Paulo 					   MACSTR " (terminate previously "
18145b9c547cSRui Paulo 					   "initiated negotiation",
18155b9c547cSRui Paulo 					   MAC2STR(src_addr));
18165b9c547cSRui Paulo 				wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
18175b9c547cSRui Paulo 						 peer->addr);
18185b9c547cSRui Paulo 				wpa_tdls_peer_clear(sm, peer);
18195b9c547cSRui Paulo 			}
18205b9c547cSRui Paulo 		}
1821f05cddf9SRui Paulo 	}
1822f05cddf9SRui Paulo 
1823f05cddf9SRui Paulo 	/* capability information */
1824f05cddf9SRui Paulo 	peer->capability = WPA_GET_LE16(cpos);
1825f05cddf9SRui Paulo 	cpos += 2;
1826f05cddf9SRui Paulo 
1827f05cddf9SRui Paulo 	ielen = len - (cpos - buf); /* start of IE in buf */
18285b9c547cSRui Paulo 
18295b9c547cSRui Paulo 	/*
18305b9c547cSRui Paulo 	 * Don't reject the message if failing to parse IEs. The IEs we need are
18315b9c547cSRui Paulo 	 * explicitly checked below. Some APs may add arbitrary padding to the
18325b9c547cSRui Paulo 	 * end of short TDLS frames and that would look like invalid IEs.
18335b9c547cSRui Paulo 	 */
18345b9c547cSRui Paulo 	if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0)
18355b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
18365b9c547cSRui Paulo 			   "TDLS: Failed to parse IEs in TPK M1 - ignore as an interop workaround");
1837f05cddf9SRui Paulo 
1838f05cddf9SRui Paulo 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
1839f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
1840f05cddf9SRui Paulo 			   "TPK M1");
1841f05cddf9SRui Paulo 		goto error;
1842f05cddf9SRui Paulo 	}
1843f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1",
1844f05cddf9SRui Paulo 		    kde.lnkid, kde.lnkid_len);
1845f05cddf9SRui Paulo 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
1846f05cddf9SRui Paulo 	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
1847f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS");
18485b9c547cSRui Paulo 		status = WLAN_STATUS_REQUEST_DECLINED;
1849f05cddf9SRui Paulo 		goto error;
1850f05cddf9SRui Paulo 	}
1851f05cddf9SRui Paulo 
1852f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
1853f05cddf9SRui Paulo 		   MAC2STR(src_addr));
1854f05cddf9SRui Paulo 
1855f05cddf9SRui Paulo 	if (copy_supp_rates(&kde, peer) < 0)
1856f05cddf9SRui Paulo 		goto error;
1857f05cddf9SRui Paulo 
18585b9c547cSRui Paulo 	if (copy_peer_ht_capab(&kde, peer) < 0)
18595b9c547cSRui Paulo 		goto error;
18605b9c547cSRui Paulo 
18615b9c547cSRui Paulo 	if (copy_peer_vht_capab(&kde, peer) < 0)
18625b9c547cSRui Paulo 		goto error;
18635b9c547cSRui Paulo 
18645b9c547cSRui Paulo 	if (copy_peer_ext_capab(&kde, peer) < 0)
18655b9c547cSRui Paulo 		goto error;
18665b9c547cSRui Paulo 
18675b9c547cSRui Paulo 	if (copy_peer_supp_channels(&kde, peer) < 0)
18685b9c547cSRui Paulo 		goto error;
18695b9c547cSRui Paulo 
18705b9c547cSRui Paulo 	if (copy_peer_supp_oper_classes(&kde, peer) < 0)
18715b9c547cSRui Paulo 		goto error;
18725b9c547cSRui Paulo 
18735b9c547cSRui Paulo 	peer->qos_info = kde.qosinfo;
18745b9c547cSRui Paulo 
18755b9c547cSRui Paulo 	/* Overwrite with the qos_info obtained in WMM IE */
18765b9c547cSRui Paulo 	if (copy_peer_wmm_capab(&kde, peer) < 0)
18775b9c547cSRui Paulo 		goto error;
18785b9c547cSRui Paulo 
18795b9c547cSRui Paulo 	peer->aid = kde.aid;
18805b9c547cSRui Paulo 
1881f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1882f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
18835b9c547cSRui Paulo 		peer = wpa_tdls_add_peer(sm, src_addr, NULL);
1884f05cddf9SRui Paulo 		if (peer == NULL)
1885f05cddf9SRui Paulo 			goto error;
1886f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of "
1887f05cddf9SRui Paulo 			   "TDLS setup - send own request");
1888f05cddf9SRui Paulo 		peer->initiator = 1;
18895b9c547cSRui Paulo 		wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
18905b9c547cSRui Paulo 					NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0);
1891f05cddf9SRui Paulo 		wpa_tdls_send_tpk_m1(sm, peer);
1892f05cddf9SRui Paulo 	}
1893f05cddf9SRui Paulo 
1894f05cddf9SRui Paulo 	if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
1895f05cddf9SRui Paulo 	    tdls_prohibited) {
1896f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
1897f05cddf9SRui Paulo 			   "on TDLS");
1898f05cddf9SRui Paulo 		tdls_prohibited = 0;
1899f05cddf9SRui Paulo 	}
1900f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1901f05cddf9SRui Paulo 
1902f05cddf9SRui Paulo 	if (tdls_prohibited) {
1903f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS");
1904f05cddf9SRui Paulo 		status = WLAN_STATUS_REQUEST_DECLINED;
1905f05cddf9SRui Paulo 		goto error;
1906f05cddf9SRui Paulo 	}
1907f05cddf9SRui Paulo 
1908f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm)) {
1909f05cddf9SRui Paulo 		if (kde.rsn_ie) {
1910f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while "
1911f05cddf9SRui Paulo 				   "security is disabled");
1912f05cddf9SRui Paulo 			status = WLAN_STATUS_SECURITY_DISABLED;
1913f05cddf9SRui Paulo 			goto error;
1914f05cddf9SRui Paulo 		}
1915f05cddf9SRui Paulo 		goto skip_rsn;
1916f05cddf9SRui Paulo 	}
1917f05cddf9SRui Paulo 
1918f05cddf9SRui Paulo 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
1919f05cddf9SRui Paulo 	    kde.rsn_ie == NULL) {
1920f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1");
1921f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_PARAMETERS;
1922f05cddf9SRui Paulo 		goto error;
1923f05cddf9SRui Paulo 	}
1924f05cddf9SRui Paulo 
1925f05cddf9SRui Paulo 	if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
1926f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in "
1927f05cddf9SRui Paulo 			   "TPK M1");
1928f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
1929f05cddf9SRui Paulo 		goto error;
1930f05cddf9SRui Paulo 	}
1931f05cddf9SRui Paulo 
1932f05cddf9SRui Paulo 	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
1933f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1");
1934f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
1935f05cddf9SRui Paulo 		goto error;
1936f05cddf9SRui Paulo 	}
1937f05cddf9SRui Paulo 
1938f05cddf9SRui Paulo 	cipher = ie.pairwise_cipher;
1939f05cddf9SRui Paulo 	if (cipher & WPA_CIPHER_CCMP) {
1940f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
1941f05cddf9SRui Paulo 		cipher = WPA_CIPHER_CCMP;
1942f05cddf9SRui Paulo 	} else {
1943f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1");
1944f05cddf9SRui Paulo 		status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
1945f05cddf9SRui Paulo 		goto error;
1946f05cddf9SRui Paulo 	}
1947f05cddf9SRui Paulo 
1948f05cddf9SRui Paulo 	if ((ie.capabilities &
1949f05cddf9SRui Paulo 	     (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) !=
1950f05cddf9SRui Paulo 	    WPA_CAPABILITY_PEERKEY_ENABLED) {
1951f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in "
1952f05cddf9SRui Paulo 			   "TPK M1");
1953f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSN_IE_CAPAB;
1954f05cddf9SRui Paulo 		goto error;
1955f05cddf9SRui Paulo 	}
1956f05cddf9SRui Paulo 
1957f05cddf9SRui Paulo 	/* Lifetime */
1958f05cddf9SRui Paulo 	if (kde.key_lifetime == NULL) {
1959f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1");
1960f05cddf9SRui Paulo 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
1961f05cddf9SRui Paulo 		goto error;
1962f05cddf9SRui Paulo 	}
1963f05cddf9SRui Paulo 	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
1964f05cddf9SRui Paulo 	lifetime = WPA_GET_LE32(timeoutie->value);
1965f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime);
1966f05cddf9SRui Paulo 	if (lifetime < 300) {
1967f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime");
1968f05cddf9SRui Paulo 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
1969f05cddf9SRui Paulo 		goto error;
1970f05cddf9SRui Paulo 	}
1971f05cddf9SRui Paulo 
1972f05cddf9SRui Paulo skip_rsn:
1973f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1974f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
1975f05cddf9SRui Paulo 		if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) {
1976f05cddf9SRui Paulo 			/*
1977f05cddf9SRui Paulo 			 * The request frame from us is going to win, so do not
1978f05cddf9SRui Paulo 			 * replace information based on this request frame from
1979f05cddf9SRui Paulo 			 * the peer.
1980f05cddf9SRui Paulo 			 */
1981f05cddf9SRui Paulo 			goto skip_rsn_check;
1982f05cddf9SRui Paulo 		}
1983f05cddf9SRui Paulo 	}
1984f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1985f05cddf9SRui Paulo 
1986f05cddf9SRui Paulo 	peer->initiator = 0; /* Need to check */
1987f05cddf9SRui Paulo 	peer->dtoken = dtoken;
1988f05cddf9SRui Paulo 
1989f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm)) {
1990f05cddf9SRui Paulo 		peer->rsnie_i_len = 0;
1991f05cddf9SRui Paulo 		peer->rsnie_p_len = 0;
1992f05cddf9SRui Paulo 		peer->cipher = WPA_CIPHER_NONE;
1993f05cddf9SRui Paulo 		goto skip_rsn_check;
1994f05cddf9SRui Paulo 	}
1995f05cddf9SRui Paulo 
1996f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
1997f05cddf9SRui Paulo 	os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
1998f05cddf9SRui Paulo 	peer->rsnie_i_len = kde.rsn_ie_len;
1999f05cddf9SRui Paulo 	peer->cipher = cipher;
2000f05cddf9SRui Paulo 
20015b9c547cSRui Paulo 	if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) {
20025b9c547cSRui Paulo 		/*
20035b9c547cSRui Paulo 		 * There is no point in updating the RNonce for every obtained
20045b9c547cSRui Paulo 		 * TPK M1 frame (e.g., retransmission due to timeout) with the
20055b9c547cSRui Paulo 		 * same INonce (SNonce in FTIE). However, if the TPK M1 is
20065b9c547cSRui Paulo 		 * retransmitted with a different INonce, update the RNonce
20075b9c547cSRui Paulo 		 * since this is for a new TDLS session.
20085b9c547cSRui Paulo 		 */
20095b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
20105b9c547cSRui Paulo 			   "TDLS: New TPK M1 INonce - generate new RNonce");
20115b9c547cSRui Paulo 		os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN);
2012f05cddf9SRui Paulo 		if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
2013f05cddf9SRui Paulo 			wpa_msg(sm->ctx->ctx, MSG_WARNING,
2014f05cddf9SRui Paulo 				"TDLS: Failed to get random data for responder nonce");
2015f05cddf9SRui Paulo 			goto error;
2016f05cddf9SRui Paulo 		}
20175b9c547cSRui Paulo 	}
2018f05cddf9SRui Paulo 
2019f05cddf9SRui Paulo #if 0
2020f05cddf9SRui Paulo 	/* get version info from RSNIE received from Peer */
2021f05cddf9SRui Paulo 	hdr = (struct rsn_ie_hdr *) kde.rsn_ie;
2022f05cddf9SRui Paulo 	rsn_ver = WPA_GET_LE16(hdr->version);
2023f05cddf9SRui Paulo 
2024f05cddf9SRui Paulo 	/* use min(peer's version, out version) */
2025f05cddf9SRui Paulo 	if (rsn_ver > RSN_VERSION)
2026f05cddf9SRui Paulo 		rsn_ver = RSN_VERSION;
2027f05cddf9SRui Paulo 
2028f05cddf9SRui Paulo 	hdr = (struct rsn_ie_hdr *) peer->rsnie_p;
2029f05cddf9SRui Paulo 
2030f05cddf9SRui Paulo 	hdr->elem_id = WLAN_EID_RSN;
2031f05cddf9SRui Paulo 	WPA_PUT_LE16(hdr->version, rsn_ver);
2032f05cddf9SRui Paulo 	pos = (u8 *) (hdr + 1);
2033f05cddf9SRui Paulo 
2034f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
2035f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
2036f05cddf9SRui Paulo 	/* Include only the selected cipher in pairwise cipher suite */
2037f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, 1);
2038f05cddf9SRui Paulo 	pos += 2;
2039f05cddf9SRui Paulo 	if (cipher == WPA_CIPHER_CCMP)
2040f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
2041f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
2042f05cddf9SRui Paulo 
2043f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, 1);
2044f05cddf9SRui Paulo 	pos += 2;
2045f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
2046f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
2047f05cddf9SRui Paulo 
2048f05cddf9SRui Paulo 	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
2049f05cddf9SRui Paulo 	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
2050f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, rsn_capab);
2051f05cddf9SRui Paulo 	pos += 2;
2052f05cddf9SRui Paulo 
2053f05cddf9SRui Paulo 	hdr->len = (pos - peer->rsnie_p) - 2;
2054f05cddf9SRui Paulo 	peer->rsnie_p_len = pos - peer->rsnie_p;
2055f05cddf9SRui Paulo #endif
2056f05cddf9SRui Paulo 
2057f05cddf9SRui Paulo 	/* temp fix: validation of RSNIE later */
2058f05cddf9SRui Paulo 	os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len);
2059f05cddf9SRui Paulo 	peer->rsnie_p_len = peer->rsnie_i_len;
2060f05cddf9SRui Paulo 
2061f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
2062f05cddf9SRui Paulo 		    peer->rsnie_p, peer->rsnie_p_len);
2063f05cddf9SRui Paulo 
2064f05cddf9SRui Paulo 	peer->lifetime = lifetime;
2065f05cddf9SRui Paulo 
2066f05cddf9SRui Paulo 	wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
2067f05cddf9SRui Paulo 
2068f05cddf9SRui Paulo skip_rsn_check:
20695b9c547cSRui Paulo #ifdef CONFIG_TDLS_TESTING
20705b9c547cSRui Paulo 	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT)
20715b9c547cSRui Paulo 		goto skip_add_peer;
20725b9c547cSRui Paulo #endif /* CONFIG_TDLS_TESTING */
20735b9c547cSRui Paulo 
20745b9c547cSRui Paulo 	/* add supported rates, capabilities, and qos_info to the TDLS peer */
20755b9c547cSRui Paulo 	if (wpa_tdls_addset_peer(sm, peer, 1) < 0)
20765b9c547cSRui Paulo 		goto error;
20775b9c547cSRui Paulo 
20785b9c547cSRui Paulo #ifdef CONFIG_TDLS_TESTING
20795b9c547cSRui Paulo skip_add_peer:
20805b9c547cSRui Paulo #endif /* CONFIG_TDLS_TESTING */
20815b9c547cSRui Paulo 	peer->tpk_in_progress = 1;
2082f05cddf9SRui Paulo 
2083f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
2084f05cddf9SRui Paulo 	if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
20855b9c547cSRui Paulo 		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
2086f05cddf9SRui Paulo 		goto error;
2087f05cddf9SRui Paulo 	}
2088f05cddf9SRui Paulo 
2089f05cddf9SRui Paulo 	return 0;
2090f05cddf9SRui Paulo 
2091f05cddf9SRui Paulo error:
20925b9c547cSRui Paulo 	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
2093f05cddf9SRui Paulo 			    status);
20945b9c547cSRui Paulo 	if (peer)
20955b9c547cSRui Paulo 		wpa_tdls_peer_free(sm, peer);
2096f05cddf9SRui Paulo 	return -1;
2097f05cddf9SRui Paulo }
2098f05cddf9SRui Paulo 
2099f05cddf9SRui Paulo 
21005b9c547cSRui Paulo static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
2101f05cddf9SRui Paulo {
2102f05cddf9SRui Paulo 	peer->tpk_success = 1;
21035b9c547cSRui Paulo 	peer->tpk_in_progress = 0;
2104f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
2105f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm)) {
2106f05cddf9SRui Paulo 		u32 lifetime = peer->lifetime;
2107f05cddf9SRui Paulo 		/*
2108f05cddf9SRui Paulo 		 * Start the initiator process a bit earlier to avoid race
2109f05cddf9SRui Paulo 		 * condition with the responder sending teardown request.
2110f05cddf9SRui Paulo 		 */
2111f05cddf9SRui Paulo 		if (lifetime > 3 && peer->initiator)
2112f05cddf9SRui Paulo 			lifetime -= 3;
2113f05cddf9SRui Paulo 		eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout,
2114f05cddf9SRui Paulo 				       sm, peer);
2115f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
2116f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) {
2117f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK "
2118f05cddf9SRui Paulo 			   "expiration");
2119f05cddf9SRui Paulo 		eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
2120f05cddf9SRui Paulo 	}
2121f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
2122f05cddf9SRui Paulo 	}
2123f05cddf9SRui Paulo 
21245b9c547cSRui Paulo 	if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) {
21255b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Could not configure key to the "
21265b9c547cSRui Paulo 			   "driver");
21275b9c547cSRui Paulo 		return -1;
21285b9c547cSRui Paulo 	}
21295b9c547cSRui Paulo 	peer->reconfig_key = 0;
2130f05cddf9SRui Paulo 
21315b9c547cSRui Paulo 	return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr);
2132f05cddf9SRui Paulo }
2133f05cddf9SRui Paulo 
2134f05cddf9SRui Paulo 
2135f05cddf9SRui Paulo static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
2136f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
2137f05cddf9SRui Paulo {
2138f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
2139f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
2140f05cddf9SRui Paulo 	struct wpa_ie_data ie;
2141f05cddf9SRui Paulo 	int cipher;
2142f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
2143f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie *timeoutie;
2144f05cddf9SRui Paulo 	struct wpa_tdls_lnkid *lnkid;
2145f05cddf9SRui Paulo 	u32 lifetime;
2146f05cddf9SRui Paulo 	u8 dtoken;
2147f05cddf9SRui Paulo 	int ielen;
2148f05cddf9SRui Paulo 	u16 status;
2149f05cddf9SRui Paulo 	const u8 *pos;
21505b9c547cSRui Paulo 	int ret = 0;
2151f05cddf9SRui Paulo 
2152f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
2153f05cddf9SRui Paulo 		   "(Peer " MACSTR ")", MAC2STR(src_addr));
2154f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
2155f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
2156f05cddf9SRui Paulo 			break;
2157f05cddf9SRui Paulo 	}
2158f05cddf9SRui Paulo 	if (peer == NULL) {
2159f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
2160f05cddf9SRui Paulo 			   "TPK M2: " MACSTR, MAC2STR(src_addr));
2161f05cddf9SRui Paulo 		return -1;
2162f05cddf9SRui Paulo 	}
21635b9c547cSRui Paulo 	if (!peer->initiator) {
21645b9c547cSRui Paulo 		/*
21655b9c547cSRui Paulo 		 * This may happen if both devices try to initiate TDLS at the
21665b9c547cSRui Paulo 		 * same time and we accept the TPK M1 from the peer in
21675b9c547cSRui Paulo 		 * wpa_tdls_process_tpk_m1() and clear our previous state.
21685b9c547cSRui Paulo 		 */
21695b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so "
21705b9c547cSRui Paulo 			   "ignore TPK M2 from " MACSTR, MAC2STR(src_addr));
21715b9c547cSRui Paulo 		return -1;
21725b9c547cSRui Paulo 	}
2173f05cddf9SRui Paulo 	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
2174f05cddf9SRui Paulo 
21755b9c547cSRui Paulo 	if (len < 3 + 2 + 1) {
21765b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2177f05cddf9SRui Paulo 		return -1;
21785b9c547cSRui Paulo 	}
21795b9c547cSRui Paulo 
2180f05cddf9SRui Paulo 	pos = buf;
2181f05cddf9SRui Paulo 	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
2182f05cddf9SRui Paulo 	status = WPA_GET_LE16(pos);
2183f05cddf9SRui Paulo 	pos += 2 /* status code */;
2184f05cddf9SRui Paulo 
2185f05cddf9SRui Paulo 	if (status != WLAN_STATUS_SUCCESS) {
2186f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u",
2187f05cddf9SRui Paulo 			   status);
21885b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2189f05cddf9SRui Paulo 		return -1;
2190f05cddf9SRui Paulo 	}
2191f05cddf9SRui Paulo 
2192f05cddf9SRui Paulo 	status = WLAN_STATUS_UNSPECIFIED_FAILURE;
2193f05cddf9SRui Paulo 
2194f05cddf9SRui Paulo 	/* TODO: need to verify dialog token matches here or in kernel */
2195f05cddf9SRui Paulo 	dtoken = *pos++; /* dialog token */
2196f05cddf9SRui Paulo 
2197f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken);
2198f05cddf9SRui Paulo 
21995b9c547cSRui Paulo 	if (len < 3 + 2 + 1 + 2) {
22005b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2201f05cddf9SRui Paulo 		return -1;
22025b9c547cSRui Paulo 	}
2203f05cddf9SRui Paulo 
2204f05cddf9SRui Paulo 	/* capability information */
2205f05cddf9SRui Paulo 	peer->capability = WPA_GET_LE16(pos);
2206f05cddf9SRui Paulo 	pos += 2;
2207f05cddf9SRui Paulo 
2208f05cddf9SRui Paulo 	ielen = len - (pos - buf); /* start of IE in buf */
22095b9c547cSRui Paulo 
22105b9c547cSRui Paulo 	/*
22115b9c547cSRui Paulo 	 * Don't reject the message if failing to parse IEs. The IEs we need are
22125b9c547cSRui Paulo 	 * explicitly checked below. Some APs may add arbitrary padding to the
22135b9c547cSRui Paulo 	 * end of short TDLS frames and that would look like invalid IEs.
22145b9c547cSRui Paulo 	 */
22155b9c547cSRui Paulo 	if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0)
22165b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
22175b9c547cSRui Paulo 			   "TDLS: Failed to parse IEs in TPK M2 - ignore as an interop workaround");
2218f05cddf9SRui Paulo 
2219f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
2220f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_DECLINE_RESP) {
2221f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response");
2222f05cddf9SRui Paulo 		status = WLAN_STATUS_REQUEST_DECLINED;
2223f05cddf9SRui Paulo 		goto error;
2224f05cddf9SRui Paulo 	}
2225f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
2226f05cddf9SRui Paulo 
2227f05cddf9SRui Paulo 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
2228f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
2229f05cddf9SRui Paulo 			   "TPK M2");
2230f05cddf9SRui Paulo 		goto error;
2231f05cddf9SRui Paulo 	}
2232f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2",
2233f05cddf9SRui Paulo 		    kde.lnkid, kde.lnkid_len);
2234f05cddf9SRui Paulo 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
2235f05cddf9SRui Paulo 
2236f05cddf9SRui Paulo 	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
2237f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
2238f05cddf9SRui Paulo 		status = WLAN_STATUS_NOT_IN_SAME_BSS;
2239f05cddf9SRui Paulo 		goto error;
2240f05cddf9SRui Paulo 	}
2241f05cddf9SRui Paulo 
2242f05cddf9SRui Paulo 	if (copy_supp_rates(&kde, peer) < 0)
2243f05cddf9SRui Paulo 		goto error;
2244f05cddf9SRui Paulo 
22455b9c547cSRui Paulo 	if (copy_peer_ht_capab(&kde, peer) < 0)
22465b9c547cSRui Paulo 		goto error;
22475b9c547cSRui Paulo 
22485b9c547cSRui Paulo 	if (copy_peer_vht_capab(&kde, peer) < 0)
22495b9c547cSRui Paulo 		goto error;
22505b9c547cSRui Paulo 
22515b9c547cSRui Paulo 	if (copy_peer_ext_capab(&kde, peer) < 0)
22525b9c547cSRui Paulo 		goto error;
22535b9c547cSRui Paulo 
22545b9c547cSRui Paulo 	if (copy_peer_supp_channels(&kde, peer) < 0)
22555b9c547cSRui Paulo 		goto error;
22565b9c547cSRui Paulo 
22575b9c547cSRui Paulo 	if (copy_peer_supp_oper_classes(&kde, peer) < 0)
22585b9c547cSRui Paulo 		goto error;
22595b9c547cSRui Paulo 
22605b9c547cSRui Paulo 	peer->qos_info = kde.qosinfo;
22615b9c547cSRui Paulo 
22625b9c547cSRui Paulo 	/* Overwrite with the qos_info obtained in WMM IE */
22635b9c547cSRui Paulo 	if (copy_peer_wmm_capab(&kde, peer) < 0)
22645b9c547cSRui Paulo 		goto error;
22655b9c547cSRui Paulo 
22665b9c547cSRui Paulo 	peer->aid = kde.aid;
22675b9c547cSRui Paulo 
2268f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm)) {
2269f05cddf9SRui Paulo 		peer->rsnie_p_len = 0;
2270f05cddf9SRui Paulo 		peer->cipher = WPA_CIPHER_NONE;
2271f05cddf9SRui Paulo 		goto skip_rsn;
2272f05cddf9SRui Paulo 	}
2273f05cddf9SRui Paulo 
2274f05cddf9SRui Paulo 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
2275f05cddf9SRui Paulo 	    kde.rsn_ie == NULL) {
2276f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2");
2277f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_PARAMETERS;
2278f05cddf9SRui Paulo 		goto error;
2279f05cddf9SRui Paulo 	}
2280f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
2281f05cddf9SRui Paulo 		    kde.rsn_ie, kde.rsn_ie_len);
2282f05cddf9SRui Paulo 
22835b9c547cSRui Paulo 	if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
22845b9c547cSRui Paulo 		wpa_printf(MSG_INFO,
22855b9c547cSRui Paulo 			   "TDLS: Too long Responder RSN IE in TPK M2");
22865b9c547cSRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
22875b9c547cSRui Paulo 		goto error;
22885b9c547cSRui Paulo 	}
22895b9c547cSRui Paulo 
2290f05cddf9SRui Paulo 	/*
2291f05cddf9SRui Paulo 	 * FIX: bitwise comparison of RSN IE is not the correct way of
2292f05cddf9SRui Paulo 	 * validation this. It can be different, but certain fields must
2293f05cddf9SRui Paulo 	 * match. Since we list only a single pairwise cipher in TPK M1, the
2294f05cddf9SRui Paulo 	 * memcmp is likely to work in most cases, though.
2295f05cddf9SRui Paulo 	 */
2296f05cddf9SRui Paulo 	if (kde.rsn_ie_len != peer->rsnie_i_len ||
2297f05cddf9SRui Paulo 	    os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) {
2298f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does "
2299f05cddf9SRui Paulo 			   "not match with RSN IE used in TPK M1");
2300f05cddf9SRui Paulo 		wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1",
2301f05cddf9SRui Paulo 			    peer->rsnie_i, peer->rsnie_i_len);
2302f05cddf9SRui Paulo 		wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
2303f05cddf9SRui Paulo 			    kde.rsn_ie, kde.rsn_ie_len);
2304f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
2305f05cddf9SRui Paulo 		goto error;
2306f05cddf9SRui Paulo 	}
2307f05cddf9SRui Paulo 
2308f05cddf9SRui Paulo 	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
2309f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2");
2310f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
2311f05cddf9SRui Paulo 		goto error;
2312f05cddf9SRui Paulo 	}
2313f05cddf9SRui Paulo 
2314f05cddf9SRui Paulo 	cipher = ie.pairwise_cipher;
2315f05cddf9SRui Paulo 	if (cipher == WPA_CIPHER_CCMP) {
2316f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
2317f05cddf9SRui Paulo 		cipher = WPA_CIPHER_CCMP;
2318f05cddf9SRui Paulo 	} else {
2319f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2");
2320f05cddf9SRui Paulo 		status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
2321f05cddf9SRui Paulo 		goto error;
2322f05cddf9SRui Paulo 	}
2323f05cddf9SRui Paulo 
2324f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2",
2325f05cddf9SRui Paulo 		    kde.ftie, sizeof(*ftie));
2326f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
2327f05cddf9SRui Paulo 
2328f05cddf9SRui Paulo 	if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
2329f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does "
2330f05cddf9SRui Paulo 			   "not match with FTIE SNonce used in TPK M1");
2331f05cddf9SRui Paulo 		/* Silently discard the frame */
2332f05cddf9SRui Paulo 		return -1;
2333f05cddf9SRui Paulo 	}
2334f05cddf9SRui Paulo 
2335f05cddf9SRui Paulo 	/* Responder Nonce and RSN IE */
2336f05cddf9SRui Paulo 	os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN);
2337f05cddf9SRui Paulo 	os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len);
2338f05cddf9SRui Paulo 	peer->rsnie_p_len = kde.rsn_ie_len;
2339f05cddf9SRui Paulo 	peer->cipher = cipher;
2340f05cddf9SRui Paulo 
2341f05cddf9SRui Paulo 	/* Lifetime */
2342f05cddf9SRui Paulo 	if (kde.key_lifetime == NULL) {
2343f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2");
2344f05cddf9SRui Paulo 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
2345f05cddf9SRui Paulo 		goto error;
2346f05cddf9SRui Paulo 	}
2347f05cddf9SRui Paulo 	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
2348f05cddf9SRui Paulo 	lifetime = WPA_GET_LE32(timeoutie->value);
2349f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2",
2350f05cddf9SRui Paulo 		   lifetime);
2351f05cddf9SRui Paulo 	if (lifetime != peer->lifetime) {
2352f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
2353f05cddf9SRui Paulo 			   "TPK M2 (expected %u)", lifetime, peer->lifetime);
2354f05cddf9SRui Paulo 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
2355f05cddf9SRui Paulo 		goto error;
2356f05cddf9SRui Paulo 	}
2357f05cddf9SRui Paulo 
2358f05cddf9SRui Paulo 	wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
2359f05cddf9SRui Paulo 
2360f05cddf9SRui Paulo 	/* Process MIC check to see if TPK M2 is right */
2361f05cddf9SRui Paulo 	if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
2362f05cddf9SRui Paulo 					   (u8 *) timeoutie, ftie) < 0) {
2363f05cddf9SRui Paulo 		/* Discard the frame */
2364f05cddf9SRui Paulo 		wpa_tdls_del_key(sm, peer);
23655b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2366f05cddf9SRui Paulo 		return -1;
2367f05cddf9SRui Paulo 	}
2368f05cddf9SRui Paulo 
23695b9c547cSRui Paulo 	if (wpa_tdls_set_key(sm, peer) < 0) {
23705b9c547cSRui Paulo 		/*
23715b9c547cSRui Paulo 		 * Some drivers may not be able to config the key prior to full
23725b9c547cSRui Paulo 		 * STA entry having been configured.
23735b9c547cSRui Paulo 		 */
23745b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
23755b9c547cSRui Paulo 			   "STA entry is complete");
23765b9c547cSRui Paulo 		peer->reconfig_key = 1;
23775b9c547cSRui Paulo 	}
2378f05cddf9SRui Paulo 
2379f05cddf9SRui Paulo skip_rsn:
2380f05cddf9SRui Paulo 	peer->dtoken = dtoken;
2381f05cddf9SRui Paulo 
23825b9c547cSRui Paulo 	/* add supported rates, capabilities, and qos_info to the TDLS peer */
23835b9c547cSRui Paulo 	if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
23845b9c547cSRui Paulo 		goto error;
23855b9c547cSRui Paulo 
2386f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
2387f05cddf9SRui Paulo 		   "TPK Handshake Message 3");
23885b9c547cSRui Paulo 	if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
23895b9c547cSRui Paulo 		goto error;
2390f05cddf9SRui Paulo 
23915b9c547cSRui Paulo 	if (!peer->tpk_success) {
23925b9c547cSRui Paulo 		/*
23935b9c547cSRui Paulo 		 * Enable Link only when tpk_success is 0, signifying that this
23945b9c547cSRui Paulo 		 * processing of TPK M2 frame is not because of a retransmission
23955b9c547cSRui Paulo 		 * during TDLS setup handshake.
23965b9c547cSRui Paulo 		 */
23975b9c547cSRui Paulo 		ret = wpa_tdls_enable_link(sm, peer);
23985b9c547cSRui Paulo 		if (ret < 0) {
23995b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
24005b9c547cSRui Paulo 			wpa_tdls_do_teardown(
24015b9c547cSRui Paulo 				sm, peer,
24025b9c547cSRui Paulo 				WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
24035b9c547cSRui Paulo 		}
24045b9c547cSRui Paulo 	}
24055b9c547cSRui Paulo 	return ret;
2406f05cddf9SRui Paulo 
2407f05cddf9SRui Paulo error:
24085b9c547cSRui Paulo 	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1,
2409f05cddf9SRui Paulo 			    status);
24105b9c547cSRui Paulo 	wpa_tdls_disable_peer_link(sm, peer);
2411f05cddf9SRui Paulo 	return -1;
2412f05cddf9SRui Paulo }
2413f05cddf9SRui Paulo 
2414f05cddf9SRui Paulo 
2415f05cddf9SRui Paulo static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
2416f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
2417f05cddf9SRui Paulo {
2418f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
2419f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
2420f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
2421f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie *timeoutie;
2422f05cddf9SRui Paulo 	struct wpa_tdls_lnkid *lnkid;
2423f05cddf9SRui Paulo 	int ielen;
2424f05cddf9SRui Paulo 	u16 status;
2425f05cddf9SRui Paulo 	const u8 *pos;
2426f05cddf9SRui Paulo 	u32 lifetime;
24275b9c547cSRui Paulo 	int ret = 0;
2428f05cddf9SRui Paulo 
2429f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
2430f05cddf9SRui Paulo 		   "(Peer " MACSTR ")", MAC2STR(src_addr));
2431f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
2432f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
2433f05cddf9SRui Paulo 			break;
2434f05cddf9SRui Paulo 	}
2435f05cddf9SRui Paulo 	if (peer == NULL) {
2436f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
2437f05cddf9SRui Paulo 			   "TPK M3: " MACSTR, MAC2STR(src_addr));
2438f05cddf9SRui Paulo 		return -1;
2439f05cddf9SRui Paulo 	}
2440f05cddf9SRui Paulo 	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE);
2441f05cddf9SRui Paulo 
2442f05cddf9SRui Paulo 	if (len < 3 + 3)
24435b9c547cSRui Paulo 		goto error;
2444f05cddf9SRui Paulo 	pos = buf;
2445f05cddf9SRui Paulo 	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
2446f05cddf9SRui Paulo 
2447f05cddf9SRui Paulo 	status = WPA_GET_LE16(pos);
2448f05cddf9SRui Paulo 
2449f05cddf9SRui Paulo 	if (status != 0) {
2450f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u",
2451f05cddf9SRui Paulo 			   status);
24525b9c547cSRui Paulo 		goto error;
2453f05cddf9SRui Paulo 	}
2454f05cddf9SRui Paulo 	pos += 2 /* status code */ + 1 /* dialog token */;
2455f05cddf9SRui Paulo 
2456f05cddf9SRui Paulo 	ielen = len - (pos - buf); /* start of IE in buf */
24575b9c547cSRui Paulo 
24585b9c547cSRui Paulo 	/*
24595b9c547cSRui Paulo 	 * Don't reject the message if failing to parse IEs. The IEs we need are
24605b9c547cSRui Paulo 	 * explicitly checked below. Some APs piggy-back broken IEs to the end
24615b9c547cSRui Paulo 	 * of a TDLS Confirm packet, which will fail the link if we don't ignore
24625b9c547cSRui Paulo 	 * this error.
24635b9c547cSRui Paulo 	 */
2464f05cddf9SRui Paulo 	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
24655b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
24665b9c547cSRui Paulo 			   "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
2467f05cddf9SRui Paulo 	}
2468f05cddf9SRui Paulo 
2469f05cddf9SRui Paulo 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
2470f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3");
24715b9c547cSRui Paulo 		goto error;
2472f05cddf9SRui Paulo 	}
2473f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3",
2474f05cddf9SRui Paulo 		    (u8 *) kde.lnkid, kde.lnkid_len);
2475f05cddf9SRui Paulo 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
2476f05cddf9SRui Paulo 
2477f05cddf9SRui Paulo 	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
2478f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
24795b9c547cSRui Paulo 		goto error;
2480f05cddf9SRui Paulo 	}
2481f05cddf9SRui Paulo 
2482f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm))
2483f05cddf9SRui Paulo 		goto skip_rsn;
2484f05cddf9SRui Paulo 
2485f05cddf9SRui Paulo 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
2486f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3");
24875b9c547cSRui Paulo 		goto error;
2488f05cddf9SRui Paulo 	}
2489f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3",
2490f05cddf9SRui Paulo 		    kde.ftie, sizeof(*ftie));
2491f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
2492f05cddf9SRui Paulo 
2493f05cddf9SRui Paulo 	if (kde.rsn_ie == NULL) {
2494f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3");
24955b9c547cSRui Paulo 		goto error;
2496f05cddf9SRui Paulo 	}
2497f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3",
2498f05cddf9SRui Paulo 		    kde.rsn_ie, kde.rsn_ie_len);
2499f05cddf9SRui Paulo 	if (kde.rsn_ie_len != peer->rsnie_p_len ||
2500f05cddf9SRui Paulo 	    os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) {
2501f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match "
2502f05cddf9SRui Paulo 			   "with the one sent in TPK M2");
25035b9c547cSRui Paulo 		goto error;
2504f05cddf9SRui Paulo 	}
2505f05cddf9SRui Paulo 
2506f05cddf9SRui Paulo 	if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) {
2507f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does "
2508f05cddf9SRui Paulo 			   "not match with FTIE ANonce used in TPK M2");
25095b9c547cSRui Paulo 		goto error;
2510f05cddf9SRui Paulo 	}
2511f05cddf9SRui Paulo 
2512f05cddf9SRui Paulo 	if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
2513f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not "
2514f05cddf9SRui Paulo 			   "match with FTIE SNonce used in TPK M1");
25155b9c547cSRui Paulo 		goto error;
2516f05cddf9SRui Paulo 	}
2517f05cddf9SRui Paulo 
2518f05cddf9SRui Paulo 	if (kde.key_lifetime == NULL) {
2519f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
25205b9c547cSRui Paulo 		goto error;
2521f05cddf9SRui Paulo 	}
2522f05cddf9SRui Paulo 	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
2523f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3",
2524f05cddf9SRui Paulo 		    (u8 *) timeoutie, sizeof(*timeoutie));
2525f05cddf9SRui Paulo 	lifetime = WPA_GET_LE32(timeoutie->value);
2526f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3",
2527f05cddf9SRui Paulo 		   lifetime);
2528f05cddf9SRui Paulo 	if (lifetime != peer->lifetime) {
2529f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
2530f05cddf9SRui Paulo 			   "TPK M3 (expected %u)", lifetime, peer->lifetime);
25315b9c547cSRui Paulo 		goto error;
2532f05cddf9SRui Paulo 	}
2533f05cddf9SRui Paulo 
2534f05cddf9SRui Paulo 	if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
2535f05cddf9SRui Paulo 					   (u8 *) timeoutie, ftie) < 0) {
2536f05cddf9SRui Paulo 		wpa_tdls_del_key(sm, peer);
25375b9c547cSRui Paulo 		goto error;
2538f05cddf9SRui Paulo 	}
2539f05cddf9SRui Paulo 
25405b9c547cSRui Paulo 	if (wpa_tdls_set_key(sm, peer) < 0) {
25415b9c547cSRui Paulo 		/*
25425b9c547cSRui Paulo 		 * Some drivers may not be able to config the key prior to full
25435b9c547cSRui Paulo 		 * STA entry having been configured.
25445b9c547cSRui Paulo 		 */
25455b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
25465b9c547cSRui Paulo 			   "STA entry is complete");
25475b9c547cSRui Paulo 		peer->reconfig_key = 1;
25485b9c547cSRui Paulo 	}
2549f05cddf9SRui Paulo 
2550f05cddf9SRui Paulo skip_rsn:
25515b9c547cSRui Paulo 	/* add supported rates, capabilities, and qos_info to the TDLS peer */
25525b9c547cSRui Paulo 	if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
25535b9c547cSRui Paulo 		goto error;
2554f05cddf9SRui Paulo 
25555b9c547cSRui Paulo 	if (!peer->tpk_success) {
25565b9c547cSRui Paulo 		/*
25575b9c547cSRui Paulo 		 * Enable Link only when tpk_success is 0, signifying that this
25585b9c547cSRui Paulo 		 * processing of TPK M3 frame is not because of a retransmission
25595b9c547cSRui Paulo 		 * during TDLS setup handshake.
25605b9c547cSRui Paulo 		 */
25615b9c547cSRui Paulo 		ret = wpa_tdls_enable_link(sm, peer);
25625b9c547cSRui Paulo 		if (ret < 0) {
25635b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
25645b9c547cSRui Paulo 			goto error;
25655b9c547cSRui Paulo 		}
25665b9c547cSRui Paulo 	}
25675b9c547cSRui Paulo 	return ret;
25685b9c547cSRui Paulo error:
25695b9c547cSRui Paulo 	wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
25705b9c547cSRui Paulo 	return -1;
2571f05cddf9SRui Paulo }
2572f05cddf9SRui Paulo 
2573f05cddf9SRui Paulo 
2574f05cddf9SRui Paulo static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs)
2575f05cddf9SRui Paulo {
2576f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie;
2577f05cddf9SRui Paulo 
2578f05cddf9SRui Paulo 	os_memset(lifetime, 0, ie_len);
2579f05cddf9SRui Paulo 	lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL;
2580f05cddf9SRui Paulo 	lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2;
2581f05cddf9SRui Paulo 	lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME;
2582f05cddf9SRui Paulo 	WPA_PUT_LE32(lifetime->value, tsecs);
2583f05cddf9SRui Paulo 	os_memcpy(pos, ie, ie_len);
2584f05cddf9SRui Paulo 	return pos + ie_len;
2585f05cddf9SRui Paulo }
2586f05cddf9SRui Paulo 
2587f05cddf9SRui Paulo 
2588f05cddf9SRui Paulo /**
2589f05cddf9SRui Paulo  * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1)
2590f05cddf9SRui Paulo  * @sm: Pointer to WPA state machine data from wpa_sm_init()
2591f05cddf9SRui Paulo  * @peer: MAC address of the peer STA
2592f05cddf9SRui Paulo  * Returns: 0 on success, or -1 on failure
2593f05cddf9SRui Paulo  *
2594f05cddf9SRui Paulo  * Send TPK Handshake Message 1 info to driver to start TDLS
2595f05cddf9SRui Paulo  * handshake with the peer.
2596f05cddf9SRui Paulo  */
2597f05cddf9SRui Paulo int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
2598f05cddf9SRui Paulo {
2599f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
2600f05cddf9SRui Paulo 	int tdls_prohibited = sm->tdls_prohibited;
2601f05cddf9SRui Paulo 
2602f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
2603f05cddf9SRui Paulo 		return -1;
2604f05cddf9SRui Paulo 
2605f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
2606f05cddf9SRui Paulo 	if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
2607f05cddf9SRui Paulo 	    tdls_prohibited) {
2608f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
2609f05cddf9SRui Paulo 			   "on TDLS");
2610f05cddf9SRui Paulo 		tdls_prohibited = 0;
2611f05cddf9SRui Paulo 	}
2612f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
2613f05cddf9SRui Paulo 
2614f05cddf9SRui Paulo 	if (tdls_prohibited) {
2615f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - "
2616f05cddf9SRui Paulo 			   "reject request to start setup");
2617f05cddf9SRui Paulo 		return -1;
2618f05cddf9SRui Paulo 	}
2619f05cddf9SRui Paulo 
26205b9c547cSRui Paulo 	peer = wpa_tdls_add_peer(sm, addr, NULL);
2621f05cddf9SRui Paulo 	if (peer == NULL)
2622f05cddf9SRui Paulo 		return -1;
26235b9c547cSRui Paulo 
26245b9c547cSRui Paulo 	if (peer->tpk_in_progress) {
26255b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer");
26265b9c547cSRui Paulo 		return 0;
2627f05cddf9SRui Paulo 	}
2628f05cddf9SRui Paulo 
2629f05cddf9SRui Paulo 	peer->initiator = 1;
2630f05cddf9SRui Paulo 
2631f05cddf9SRui Paulo 	/* add the peer to the driver as a "setup in progress" peer */
26325b9c547cSRui Paulo 	if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
26335b9c547cSRui Paulo 				    NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0)) {
26345b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
26355b9c547cSRui Paulo 		return -1;
26365b9c547cSRui Paulo 	}
26375b9c547cSRui Paulo 
26385b9c547cSRui Paulo 	peer->tpk_in_progress = 1;
2639f05cddf9SRui Paulo 
2640f05cddf9SRui Paulo 	if (wpa_tdls_send_tpk_m1(sm, peer) < 0) {
26415b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2642f05cddf9SRui Paulo 		return -1;
2643f05cddf9SRui Paulo 	}
2644f05cddf9SRui Paulo 
2645f05cddf9SRui Paulo 	return 0;
2646f05cddf9SRui Paulo }
2647f05cddf9SRui Paulo 
2648f05cddf9SRui Paulo 
26495b9c547cSRui Paulo void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr)
2650f05cddf9SRui Paulo {
2651f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
2652f05cddf9SRui Paulo 
2653f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
26545b9c547cSRui Paulo 		return;
2655f05cddf9SRui Paulo 
2656f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
2657f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
2658f05cddf9SRui Paulo 			break;
2659f05cddf9SRui Paulo 	}
2660f05cddf9SRui Paulo 
2661f05cddf9SRui Paulo 	if (peer == NULL || !peer->tpk_success)
26625b9c547cSRui Paulo 		return;
2663f05cddf9SRui Paulo 
2664f05cddf9SRui Paulo 	if (sm->tdls_external_setup) {
2665f05cddf9SRui Paulo 		/*
2666f05cddf9SRui Paulo 		 * Disable previous link to allow renegotiation to be completed
2667f05cddf9SRui Paulo 		 * on AP path.
2668f05cddf9SRui Paulo 		 */
26695b9c547cSRui Paulo 		wpa_tdls_do_teardown(sm, peer,
26705b9c547cSRui Paulo 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
2671f05cddf9SRui Paulo 	}
2672f05cddf9SRui Paulo }
2673f05cddf9SRui Paulo 
2674f05cddf9SRui Paulo 
2675f05cddf9SRui Paulo /**
2676f05cddf9SRui Paulo  * wpa_supplicant_rx_tdls - Receive TDLS data frame
2677f05cddf9SRui Paulo  *
2678f05cddf9SRui Paulo  * This function is called to receive TDLS (ethertype = 0x890d) data frames.
2679f05cddf9SRui Paulo  */
2680f05cddf9SRui Paulo static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr,
2681f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
2682f05cddf9SRui Paulo {
2683f05cddf9SRui Paulo 	struct wpa_sm *sm = ctx;
2684f05cddf9SRui Paulo 	struct wpa_tdls_frame *tf;
2685f05cddf9SRui Paulo 
2686f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation",
2687f05cddf9SRui Paulo 		    buf, len);
2688f05cddf9SRui Paulo 
2689f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported) {
2690f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled "
2691f05cddf9SRui Paulo 			   "or unsupported by driver");
2692f05cddf9SRui Paulo 		return;
2693f05cddf9SRui Paulo 	}
2694f05cddf9SRui Paulo 
2695f05cddf9SRui Paulo 	if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) {
2696f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message");
2697f05cddf9SRui Paulo 		return;
2698f05cddf9SRui Paulo 	}
2699f05cddf9SRui Paulo 
2700f05cddf9SRui Paulo 	if (len < sizeof(*tf)) {
2701f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Drop too short frame");
2702f05cddf9SRui Paulo 		return;
2703f05cddf9SRui Paulo 	}
2704f05cddf9SRui Paulo 
2705f05cddf9SRui Paulo 	/* Check to make sure its a valid encapsulated TDLS frame */
2706f05cddf9SRui Paulo 	tf = (struct wpa_tdls_frame *) buf;
2707f05cddf9SRui Paulo 	if (tf->payloadtype != 2 /* TDLS_RFTYPE */ ||
2708f05cddf9SRui Paulo 	    tf->category != WLAN_ACTION_TDLS) {
2709f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u "
2710f05cddf9SRui Paulo 			   "category=%u action=%u",
2711f05cddf9SRui Paulo 			   tf->payloadtype, tf->category, tf->action);
2712f05cddf9SRui Paulo 		return;
2713f05cddf9SRui Paulo 	}
2714f05cddf9SRui Paulo 
2715f05cddf9SRui Paulo 	switch (tf->action) {
2716f05cddf9SRui Paulo 	case WLAN_TDLS_SETUP_REQUEST:
2717f05cddf9SRui Paulo 		wpa_tdls_process_tpk_m1(sm, src_addr, buf, len);
2718f05cddf9SRui Paulo 		break;
2719f05cddf9SRui Paulo 	case WLAN_TDLS_SETUP_RESPONSE:
2720f05cddf9SRui Paulo 		wpa_tdls_process_tpk_m2(sm, src_addr, buf, len);
2721f05cddf9SRui Paulo 		break;
2722f05cddf9SRui Paulo 	case WLAN_TDLS_SETUP_CONFIRM:
2723f05cddf9SRui Paulo 		wpa_tdls_process_tpk_m3(sm, src_addr, buf, len);
2724f05cddf9SRui Paulo 		break;
2725f05cddf9SRui Paulo 	case WLAN_TDLS_TEARDOWN:
2726f05cddf9SRui Paulo 		wpa_tdls_recv_teardown(sm, src_addr, buf, len);
2727f05cddf9SRui Paulo 		break;
2728f05cddf9SRui Paulo 	case WLAN_TDLS_DISCOVERY_REQUEST:
2729f05cddf9SRui Paulo 		wpa_tdls_process_discovery_request(sm, src_addr, buf, len);
2730f05cddf9SRui Paulo 		break;
2731f05cddf9SRui Paulo 	default:
2732f05cddf9SRui Paulo 		/* Kernel code will process remaining frames */
2733f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u",
2734f05cddf9SRui Paulo 			   tf->action);
2735f05cddf9SRui Paulo 		break;
2736f05cddf9SRui Paulo 	}
2737f05cddf9SRui Paulo }
2738f05cddf9SRui Paulo 
2739f05cddf9SRui Paulo 
2740f05cddf9SRui Paulo /**
2741f05cddf9SRui Paulo  * wpa_tdls_init - Initialize driver interface parameters for TDLS
2742f05cddf9SRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
2743f05cddf9SRui Paulo  * Returns: 0 on success, -1 on failure
2744f05cddf9SRui Paulo  *
2745f05cddf9SRui Paulo  * This function is called to initialize driver interface parameters for TDLS.
2746f05cddf9SRui Paulo  * wpa_drv_init() must have been called before this function to initialize the
2747f05cddf9SRui Paulo  * driver interface.
2748f05cddf9SRui Paulo  */
2749f05cddf9SRui Paulo int wpa_tdls_init(struct wpa_sm *sm)
2750f05cddf9SRui Paulo {
2751f05cddf9SRui Paulo 	if (sm == NULL)
2752f05cddf9SRui Paulo 		return -1;
2753f05cddf9SRui Paulo 
2754f05cddf9SRui Paulo 	sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
2755f05cddf9SRui Paulo 				     sm->ifname,
2756f05cddf9SRui Paulo 				     sm->own_addr,
2757f05cddf9SRui Paulo 				     ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
2758f05cddf9SRui Paulo 				     sm, 0);
2759f05cddf9SRui Paulo 	if (sm->l2_tdls == NULL) {
2760f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet "
2761f05cddf9SRui Paulo 			   "connection");
2762f05cddf9SRui Paulo 		return -1;
2763f05cddf9SRui Paulo 	}
2764f05cddf9SRui Paulo 
2765f05cddf9SRui Paulo 	/*
2766f05cddf9SRui Paulo 	 * Drivers that support TDLS but don't implement the get_capa callback
2767f05cddf9SRui Paulo 	 * are assumed to perform everything internally
2768f05cddf9SRui Paulo 	 */
2769f05cddf9SRui Paulo 	if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported,
27705b9c547cSRui Paulo 				 &sm->tdls_external_setup,
27715b9c547cSRui Paulo 				 &sm->tdls_chan_switch) < 0) {
2772f05cddf9SRui Paulo 		sm->tdls_supported = 1;
2773f05cddf9SRui Paulo 		sm->tdls_external_setup = 0;
2774f05cddf9SRui Paulo 	}
2775f05cddf9SRui Paulo 
2776f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by "
2777f05cddf9SRui Paulo 		   "driver", sm->tdls_supported ? "" : " not");
2778f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup",
2779f05cddf9SRui Paulo 		   sm->tdls_external_setup ? "external" : "internal");
27805b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching",
27815b9c547cSRui Paulo 		   sm->tdls_chan_switch ? "supports" : "does not support");
2782f05cddf9SRui Paulo 
2783f05cddf9SRui Paulo 	return 0;
2784f05cddf9SRui Paulo }
2785f05cddf9SRui Paulo 
2786f05cddf9SRui Paulo 
27875b9c547cSRui Paulo void wpa_tdls_teardown_peers(struct wpa_sm *sm)
27885b9c547cSRui Paulo {
27895b9c547cSRui Paulo 	struct wpa_tdls_peer *peer, *tmp;
27905b9c547cSRui Paulo 
27915b9c547cSRui Paulo 	if (!sm)
27925b9c547cSRui Paulo 		return;
27935b9c547cSRui Paulo 	peer = sm->tdls;
27945b9c547cSRui Paulo 
27955b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
27965b9c547cSRui Paulo 
27975b9c547cSRui Paulo 	while (peer) {
27985b9c547cSRui Paulo 		tmp = peer->next;
27995b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR,
28005b9c547cSRui Paulo 			   MAC2STR(peer->addr));
28015b9c547cSRui Paulo 		if (sm->tdls_external_setup)
28025b9c547cSRui Paulo 			wpa_tdls_do_teardown(sm, peer,
28035b9c547cSRui Paulo 					     WLAN_REASON_DEAUTH_LEAVING);
28045b9c547cSRui Paulo 		else
28055b9c547cSRui Paulo 			wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
28065b9c547cSRui Paulo 
28075b9c547cSRui Paulo 		peer = tmp;
28085b9c547cSRui Paulo 	}
28095b9c547cSRui Paulo }
28105b9c547cSRui Paulo 
28115b9c547cSRui Paulo 
2812f05cddf9SRui Paulo static void wpa_tdls_remove_peers(struct wpa_sm *sm)
2813f05cddf9SRui Paulo {
2814f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer, *tmp;
2815f05cddf9SRui Paulo 
2816f05cddf9SRui Paulo 	peer = sm->tdls;
2817f05cddf9SRui Paulo 
2818f05cddf9SRui Paulo 	while (peer) {
2819f05cddf9SRui Paulo 		int res;
2820f05cddf9SRui Paulo 		tmp = peer->next;
2821f05cddf9SRui Paulo 		res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
2822f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
2823f05cddf9SRui Paulo 			   MAC2STR(peer->addr), res);
2824f05cddf9SRui Paulo 		wpa_tdls_peer_free(sm, peer);
2825f05cddf9SRui Paulo 		peer = tmp;
2826f05cddf9SRui Paulo 	}
2827f05cddf9SRui Paulo }
2828f05cddf9SRui Paulo 
2829f05cddf9SRui Paulo 
2830f05cddf9SRui Paulo /**
2831f05cddf9SRui Paulo  * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS
2832f05cddf9SRui Paulo  *
2833f05cddf9SRui Paulo  * This function is called to recover driver interface parameters for TDLS
2834f05cddf9SRui Paulo  * and frees resources allocated for it.
2835f05cddf9SRui Paulo  */
2836f05cddf9SRui Paulo void wpa_tdls_deinit(struct wpa_sm *sm)
2837f05cddf9SRui Paulo {
2838f05cddf9SRui Paulo 	if (sm == NULL)
2839f05cddf9SRui Paulo 		return;
2840f05cddf9SRui Paulo 
2841f05cddf9SRui Paulo 	if (sm->l2_tdls)
2842f05cddf9SRui Paulo 		l2_packet_deinit(sm->l2_tdls);
2843f05cddf9SRui Paulo 	sm->l2_tdls = NULL;
2844f05cddf9SRui Paulo 
2845f05cddf9SRui Paulo 	wpa_tdls_remove_peers(sm);
2846f05cddf9SRui Paulo }
2847f05cddf9SRui Paulo 
2848f05cddf9SRui Paulo 
2849f05cddf9SRui Paulo void wpa_tdls_assoc(struct wpa_sm *sm)
2850f05cddf9SRui Paulo {
2851f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association");
2852f05cddf9SRui Paulo 	wpa_tdls_remove_peers(sm);
2853f05cddf9SRui Paulo }
2854f05cddf9SRui Paulo 
2855f05cddf9SRui Paulo 
2856f05cddf9SRui Paulo void wpa_tdls_disassoc(struct wpa_sm *sm)
2857f05cddf9SRui Paulo {
2858f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation");
2859f05cddf9SRui Paulo 	wpa_tdls_remove_peers(sm);
2860f05cddf9SRui Paulo }
2861f05cddf9SRui Paulo 
2862f05cddf9SRui Paulo 
2863*325151a3SRui Paulo static int wpa_tdls_prohibited(struct ieee802_11_elems *elems)
2864f05cddf9SRui Paulo {
2865f05cddf9SRui Paulo 	/* bit 38 - TDLS Prohibited */
28665b9c547cSRui Paulo 	return !!(elems->ext_capab[2 + 4] & 0x40);
28675b9c547cSRui Paulo }
28685b9c547cSRui Paulo 
28695b9c547cSRui Paulo 
2870*325151a3SRui Paulo static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems)
28715b9c547cSRui Paulo {
28725b9c547cSRui Paulo 	/* bit 39 - TDLS Channel Switch Prohibited */
28735b9c547cSRui Paulo 	return !!(elems->ext_capab[2 + 4] & 0x80);
2874f05cddf9SRui Paulo }
2875f05cddf9SRui Paulo 
2876f05cddf9SRui Paulo 
2877f05cddf9SRui Paulo void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
2878f05cddf9SRui Paulo {
2879*325151a3SRui Paulo 	struct ieee802_11_elems elems;
28805b9c547cSRui Paulo 
28815b9c547cSRui Paulo 	sm->tdls_prohibited = 0;
28825b9c547cSRui Paulo 	sm->tdls_chan_switch_prohibited = 0;
28835b9c547cSRui Paulo 
2884*325151a3SRui Paulo 	if (ies == NULL ||
2885*325151a3SRui Paulo 	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
28865b9c547cSRui Paulo 	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
28875b9c547cSRui Paulo 		return;
28885b9c547cSRui Paulo 
28895b9c547cSRui Paulo 	sm->tdls_prohibited = wpa_tdls_prohibited(&elems);
2890f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS",
2891f05cddf9SRui Paulo 		   sm->tdls_prohibited ? "prohibited" : "allowed");
28925b9c547cSRui Paulo 	sm->tdls_chan_switch_prohibited =
28935b9c547cSRui Paulo 		wpa_tdls_chan_switch_prohibited(&elems);
28945b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS",
28955b9c547cSRui Paulo 		   sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed");
2896f05cddf9SRui Paulo }
2897f05cddf9SRui Paulo 
2898f05cddf9SRui Paulo 
2899f05cddf9SRui Paulo void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
2900f05cddf9SRui Paulo {
2901*325151a3SRui Paulo 	struct ieee802_11_elems elems;
29025b9c547cSRui Paulo 
2903*325151a3SRui Paulo 	if (ies == NULL ||
2904*325151a3SRui Paulo 	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
29055b9c547cSRui Paulo 	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
29065b9c547cSRui Paulo 		return;
29075b9c547cSRui Paulo 
29085b9c547cSRui Paulo 	if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) {
2909f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on "
2910f05cddf9SRui Paulo 			   "(Re)Association Response IEs");
2911f05cddf9SRui Paulo 		sm->tdls_prohibited = 1;
2912f05cddf9SRui Paulo 	}
29135b9c547cSRui Paulo 
29145b9c547cSRui Paulo 	if (!sm->tdls_chan_switch_prohibited &&
29155b9c547cSRui Paulo 	    wpa_tdls_chan_switch_prohibited(&elems)) {
29165b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
29175b9c547cSRui Paulo 			   "TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs");
29185b9c547cSRui Paulo 		sm->tdls_chan_switch_prohibited = 1;
29195b9c547cSRui Paulo 	}
2920f05cddf9SRui Paulo }
2921f05cddf9SRui Paulo 
2922f05cddf9SRui Paulo 
2923f05cddf9SRui Paulo void wpa_tdls_enable(struct wpa_sm *sm, int enabled)
2924f05cddf9SRui Paulo {
2925f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled");
2926f05cddf9SRui Paulo 	sm->tdls_disabled = !enabled;
2927f05cddf9SRui Paulo }
2928f05cddf9SRui Paulo 
2929f05cddf9SRui Paulo 
2930f05cddf9SRui Paulo int wpa_tdls_is_external_setup(struct wpa_sm *sm)
2931f05cddf9SRui Paulo {
2932f05cddf9SRui Paulo 	return sm->tdls_external_setup;
2933f05cddf9SRui Paulo }
29345b9c547cSRui Paulo 
29355b9c547cSRui Paulo 
29365b9c547cSRui Paulo int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
29375b9c547cSRui Paulo 				u8 oper_class,
29385b9c547cSRui Paulo 				struct hostapd_freq_params *freq_params)
29395b9c547cSRui Paulo {
29405b9c547cSRui Paulo 	struct wpa_tdls_peer *peer;
29415b9c547cSRui Paulo 	int ret;
29425b9c547cSRui Paulo 
29435b9c547cSRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
29445b9c547cSRui Paulo 		return -1;
29455b9c547cSRui Paulo 
29465b9c547cSRui Paulo 	if (!sm->tdls_chan_switch) {
29475b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
29485b9c547cSRui Paulo 			   "TDLS: Channel switching not supported by the driver");
29495b9c547cSRui Paulo 		return -1;
29505b9c547cSRui Paulo 	}
29515b9c547cSRui Paulo 
29525b9c547cSRui Paulo 	if (sm->tdls_chan_switch_prohibited) {
29535b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
29545b9c547cSRui Paulo 			   "TDLS: Channel switching is prohibited in this BSS - reject request to switch channel");
29555b9c547cSRui Paulo 		return -1;
29565b9c547cSRui Paulo 	}
29575b9c547cSRui Paulo 
29585b9c547cSRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
29595b9c547cSRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
29605b9c547cSRui Paulo 			break;
29615b9c547cSRui Paulo 	}
29625b9c547cSRui Paulo 
29635b9c547cSRui Paulo 	if (peer == NULL || !peer->tpk_success) {
29645b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR
29655b9c547cSRui Paulo 			   " not found for channel switching", MAC2STR(addr));
29665b9c547cSRui Paulo 		return -1;
29675b9c547cSRui Paulo 	}
29685b9c547cSRui Paulo 
29695b9c547cSRui Paulo 	if (peer->chan_switch_enabled) {
29705b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
29715b9c547cSRui Paulo 			   " already has channel switching enabled",
29725b9c547cSRui Paulo 			   MAC2STR(addr));
29735b9c547cSRui Paulo 		return 0;
29745b9c547cSRui Paulo 	}
29755b9c547cSRui Paulo 
29765b9c547cSRui Paulo 	ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr,
29775b9c547cSRui Paulo 						oper_class, freq_params);
29785b9c547cSRui Paulo 	if (!ret)
29795b9c547cSRui Paulo 		peer->chan_switch_enabled = 1;
29805b9c547cSRui Paulo 
29815b9c547cSRui Paulo 	return ret;
29825b9c547cSRui Paulo }
29835b9c547cSRui Paulo 
29845b9c547cSRui Paulo 
29855b9c547cSRui Paulo int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr)
29865b9c547cSRui Paulo {
29875b9c547cSRui Paulo 	struct wpa_tdls_peer *peer;
29885b9c547cSRui Paulo 
29895b9c547cSRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
29905b9c547cSRui Paulo 		return -1;
29915b9c547cSRui Paulo 
29925b9c547cSRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
29935b9c547cSRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
29945b9c547cSRui Paulo 			break;
29955b9c547cSRui Paulo 	}
29965b9c547cSRui Paulo 
29975b9c547cSRui Paulo 	if (!peer || !peer->chan_switch_enabled) {
29985b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for "
29995b9c547cSRui Paulo 			   MACSTR, MAC2STR(addr));
30005b9c547cSRui Paulo 		return -1;
30015b9c547cSRui Paulo 	}
30025b9c547cSRui Paulo 
30035b9c547cSRui Paulo 	/* ignore the return value */
30045b9c547cSRui Paulo 	wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
30055b9c547cSRui Paulo 
30065b9c547cSRui Paulo 	peer->chan_switch_enabled = 0;
30075b9c547cSRui Paulo 	return 0;
30085b9c547cSRui Paulo }
3009