xref: /freebsd/contrib/wpa/src/rsn_supp/tdls.c (revision 5b9c547c072b84410b50897cc53710c75b2f6b74)
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"
15f05cddf9SRui Paulo #include "crypto/sha256.h"
16f05cddf9SRui Paulo #include "crypto/crypto.h"
17f05cddf9SRui Paulo #include "crypto/aes_wrap.h"
18f05cddf9SRui Paulo #include "rsn_supp/wpa.h"
19f05cddf9SRui Paulo #include "rsn_supp/wpa_ie.h"
20f05cddf9SRui Paulo #include "rsn_supp/wpa_i.h"
21f05cddf9SRui Paulo #include "drivers/driver.h"
22f05cddf9SRui Paulo #include "l2_packet/l2_packet.h"
23f05cddf9SRui Paulo 
24f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
25f05cddf9SRui Paulo #define TDLS_TESTING_LONG_FRAME BIT(0)
26f05cddf9SRui Paulo #define TDLS_TESTING_ALT_RSN_IE BIT(1)
27f05cddf9SRui Paulo #define TDLS_TESTING_DIFF_BSSID BIT(2)
28f05cddf9SRui Paulo #define TDLS_TESTING_SHORT_LIFETIME BIT(3)
29f05cddf9SRui Paulo #define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4)
30f05cddf9SRui Paulo #define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5)
31f05cddf9SRui Paulo #define TDLS_TESTING_LONG_LIFETIME BIT(6)
32f05cddf9SRui Paulo #define TDLS_TESTING_CONCURRENT_INIT BIT(7)
33f05cddf9SRui Paulo #define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8)
34f05cddf9SRui Paulo #define TDLS_TESTING_DECLINE_RESP BIT(9)
35f05cddf9SRui Paulo #define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
36*5b9c547cSRui Paulo #define TDLS_TESTING_WRONG_MIC BIT(11)
37f05cddf9SRui Paulo unsigned int tdls_testing = 0;
38f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
39f05cddf9SRui Paulo 
40f05cddf9SRui Paulo #define TPK_LIFETIME 43200 /* 12 hours */
41*5b9c547cSRui Paulo #define TPK_M1_RETRY_COUNT 3
42*5b9c547cSRui Paulo #define TPK_M1_TIMEOUT 5000 /* in milliseconds */
43*5b9c547cSRui Paulo #define TPK_M2_RETRY_COUNT 10
44*5b9c547cSRui Paulo #define TPK_M2_TIMEOUT 500 /* in milliseconds */
45f05cddf9SRui Paulo 
46f05cddf9SRui Paulo #define TDLS_MIC_LEN		16
47f05cddf9SRui Paulo 
48f05cddf9SRui Paulo #define TDLS_TIMEOUT_LEN	4
49f05cddf9SRui Paulo 
50f05cddf9SRui Paulo struct wpa_tdls_ftie {
51f05cddf9SRui Paulo 	u8 ie_type; /* FTIE */
52f05cddf9SRui Paulo 	u8 ie_len;
53f05cddf9SRui Paulo 	u8 mic_ctrl[2];
54f05cddf9SRui Paulo 	u8 mic[TDLS_MIC_LEN];
55f05cddf9SRui Paulo 	u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */
56f05cddf9SRui Paulo 	u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */
57f05cddf9SRui Paulo 	/* followed by optional elements */
58f05cddf9SRui Paulo } STRUCT_PACKED;
59f05cddf9SRui Paulo 
60f05cddf9SRui Paulo struct wpa_tdls_timeoutie {
61f05cddf9SRui Paulo 	u8 ie_type; /* Timeout IE */
62f05cddf9SRui Paulo 	u8 ie_len;
63f05cddf9SRui Paulo 	u8 interval_type;
64f05cddf9SRui Paulo 	u8 value[TDLS_TIMEOUT_LEN];
65f05cddf9SRui Paulo } STRUCT_PACKED;
66f05cddf9SRui Paulo 
67f05cddf9SRui Paulo struct wpa_tdls_lnkid {
68f05cddf9SRui Paulo 	u8 ie_type; /* Link Identifier IE */
69f05cddf9SRui Paulo 	u8 ie_len;
70f05cddf9SRui Paulo 	u8 bssid[ETH_ALEN];
71f05cddf9SRui Paulo 	u8 init_sta[ETH_ALEN];
72f05cddf9SRui Paulo 	u8 resp_sta[ETH_ALEN];
73f05cddf9SRui Paulo } STRUCT_PACKED;
74f05cddf9SRui Paulo 
75f05cddf9SRui Paulo /* TDLS frame headers as per IEEE Std 802.11z-2010 */
76f05cddf9SRui Paulo struct wpa_tdls_frame {
77f05cddf9SRui Paulo 	u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */
78f05cddf9SRui Paulo 	u8 category; /* Category */
79f05cddf9SRui Paulo 	u8 action; /* Action (enum tdls_frame_type) */
80f05cddf9SRui Paulo } STRUCT_PACKED;
81f05cddf9SRui Paulo 
82f05cddf9SRui Paulo static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs);
83f05cddf9SRui Paulo static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx);
84f05cddf9SRui Paulo static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer);
85*5b9c547cSRui Paulo static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
86*5b9c547cSRui Paulo 				       struct wpa_tdls_peer *peer);
87*5b9c547cSRui Paulo static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
88*5b9c547cSRui Paulo 				  u16 reason_code);
89f05cddf9SRui Paulo 
90f05cddf9SRui Paulo 
91f05cddf9SRui Paulo #define TDLS_MAX_IE_LEN 80
92f05cddf9SRui Paulo #define IEEE80211_MAX_SUPP_RATES 32
93f05cddf9SRui Paulo 
94f05cddf9SRui Paulo struct wpa_tdls_peer {
95f05cddf9SRui Paulo 	struct wpa_tdls_peer *next;
96*5b9c547cSRui Paulo 	unsigned int reconfig_key:1;
97f05cddf9SRui Paulo 	int initiator; /* whether this end was initiator for TDLS setup */
98f05cddf9SRui Paulo 	u8 addr[ETH_ALEN]; /* other end MAC address */
99f05cddf9SRui Paulo 	u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
100f05cddf9SRui Paulo 	u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */
101f05cddf9SRui Paulo 	u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */
102f05cddf9SRui Paulo 	size_t rsnie_i_len;
103f05cddf9SRui Paulo 	u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */
104f05cddf9SRui Paulo 	size_t rsnie_p_len;
105f05cddf9SRui Paulo 	u32 lifetime;
106f05cddf9SRui Paulo 	int cipher; /* Selected cipher (WPA_CIPHER_*) */
107f05cddf9SRui Paulo 	u8 dtoken;
108f05cddf9SRui Paulo 
109f05cddf9SRui Paulo 	struct tpk {
110f05cddf9SRui Paulo 		u8 kck[16]; /* TPK-KCK */
111f05cddf9SRui Paulo 		u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */
112f05cddf9SRui Paulo 	} tpk;
113f05cddf9SRui Paulo 	int tpk_set;
114f05cddf9SRui Paulo 	int tpk_success;
115*5b9c547cSRui Paulo 	int tpk_in_progress;
116f05cddf9SRui Paulo 
117f05cddf9SRui Paulo 	struct tpk_timer {
118f05cddf9SRui Paulo 		u8 dest[ETH_ALEN];
119f05cddf9SRui Paulo 		int count;      /* Retry Count */
120f05cddf9SRui Paulo 		int timer;      /* Timeout in milliseconds */
121f05cddf9SRui Paulo 		u8 action_code; /* TDLS frame type */
122f05cddf9SRui Paulo 		u8 dialog_token;
123f05cddf9SRui Paulo 		u16 status_code;
124*5b9c547cSRui Paulo 		u32 peer_capab;
125f05cddf9SRui Paulo 		int buf_len;    /* length of TPK message for retransmission */
126f05cddf9SRui Paulo 		u8 *buf;        /* buffer for TPK message */
127f05cddf9SRui Paulo 	} sm_tmr;
128f05cddf9SRui Paulo 
129f05cddf9SRui Paulo 	u16 capability;
130f05cddf9SRui Paulo 
131f05cddf9SRui Paulo 	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
132f05cddf9SRui Paulo 	size_t supp_rates_len;
133*5b9c547cSRui Paulo 
134*5b9c547cSRui Paulo 	struct ieee80211_ht_capabilities *ht_capabilities;
135*5b9c547cSRui Paulo 	struct ieee80211_vht_capabilities *vht_capabilities;
136*5b9c547cSRui Paulo 
137*5b9c547cSRui Paulo 	u8 qos_info;
138*5b9c547cSRui Paulo 
139*5b9c547cSRui Paulo 	u16 aid;
140*5b9c547cSRui Paulo 
141*5b9c547cSRui Paulo 	u8 *ext_capab;
142*5b9c547cSRui Paulo 	size_t ext_capab_len;
143*5b9c547cSRui Paulo 
144*5b9c547cSRui Paulo 	u8 *supp_channels;
145*5b9c547cSRui Paulo 	size_t supp_channels_len;
146*5b9c547cSRui Paulo 
147*5b9c547cSRui Paulo 	u8 *supp_oper_classes;
148*5b9c547cSRui Paulo 	size_t supp_oper_classes_len;
149*5b9c547cSRui Paulo 
150*5b9c547cSRui Paulo 	u8 wmm_capable;
151*5b9c547cSRui Paulo 
152*5b9c547cSRui Paulo 	/* channel switch currently enabled */
153*5b9c547cSRui Paulo 	int chan_switch_enabled;
154f05cddf9SRui Paulo };
155f05cddf9SRui Paulo 
156f05cddf9SRui Paulo 
157f05cddf9SRui Paulo static int wpa_tdls_get_privacy(struct wpa_sm *sm)
158f05cddf9SRui Paulo {
159f05cddf9SRui Paulo 	/*
160f05cddf9SRui Paulo 	 * Get info needed from supplicant to check if the current BSS supports
161f05cddf9SRui Paulo 	 * security. Other than OPEN mode, rest are considered secured
162f05cddf9SRui Paulo 	 * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake.
163f05cddf9SRui Paulo 	 */
164f05cddf9SRui Paulo 	return sm->pairwise_cipher != WPA_CIPHER_NONE;
165f05cddf9SRui Paulo }
166f05cddf9SRui Paulo 
167f05cddf9SRui Paulo 
168f05cddf9SRui Paulo static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
169f05cddf9SRui Paulo {
170f05cddf9SRui Paulo 	os_memcpy(pos, ie, ie_len);
171f05cddf9SRui Paulo 	return pos + ie_len;
172f05cddf9SRui Paulo }
173f05cddf9SRui Paulo 
174f05cddf9SRui Paulo 
175f05cddf9SRui Paulo static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
176f05cddf9SRui Paulo {
177f05cddf9SRui Paulo 	if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
178f05cddf9SRui Paulo 			   0, 0, NULL, 0, NULL, 0) < 0) {
179f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
180f05cddf9SRui Paulo 			   "the driver");
181f05cddf9SRui Paulo 		return -1;
182f05cddf9SRui Paulo 	}
183f05cddf9SRui Paulo 
184f05cddf9SRui Paulo 	return 0;
185f05cddf9SRui Paulo }
186f05cddf9SRui Paulo 
187f05cddf9SRui Paulo 
188f05cddf9SRui Paulo static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
189f05cddf9SRui Paulo {
190f05cddf9SRui Paulo 	u8 key_len;
191f05cddf9SRui Paulo 	u8 rsc[6];
192f05cddf9SRui Paulo 	enum wpa_alg alg;
193f05cddf9SRui Paulo 
194f05cddf9SRui Paulo 	os_memset(rsc, 0, 6);
195f05cddf9SRui Paulo 
196f05cddf9SRui Paulo 	switch (peer->cipher) {
197f05cddf9SRui Paulo 	case WPA_CIPHER_CCMP:
198f05cddf9SRui Paulo 		alg = WPA_ALG_CCMP;
199f05cddf9SRui Paulo 		key_len = 16;
200f05cddf9SRui Paulo 		break;
201f05cddf9SRui Paulo 	case WPA_CIPHER_NONE:
202f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: "
203f05cddf9SRui Paulo 			   "NONE - do not use pairwise keys");
204f05cddf9SRui Paulo 		return -1;
205f05cddf9SRui Paulo 	default:
206f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d",
207f05cddf9SRui Paulo 			   sm->pairwise_cipher);
208f05cddf9SRui Paulo 		return -1;
209f05cddf9SRui Paulo 	}
210f05cddf9SRui Paulo 
211f05cddf9SRui Paulo 	if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1,
212f05cddf9SRui Paulo 			   rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) {
213f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
214f05cddf9SRui Paulo 			   "driver");
215f05cddf9SRui Paulo 		return -1;
216f05cddf9SRui Paulo 	}
217f05cddf9SRui Paulo 	return 0;
218f05cddf9SRui Paulo }
219f05cddf9SRui Paulo 
220f05cddf9SRui Paulo 
221f05cddf9SRui Paulo static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
222f05cddf9SRui Paulo 				 u8 action_code, u8 dialog_token,
223*5b9c547cSRui Paulo 				 u16 status_code, u32 peer_capab,
224*5b9c547cSRui Paulo 				 int initiator, const u8 *buf, size_t len)
225f05cddf9SRui Paulo {
226f05cddf9SRui Paulo 	return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
227*5b9c547cSRui Paulo 				     status_code, peer_capab, initiator, buf,
228*5b9c547cSRui Paulo 				     len);
229f05cddf9SRui Paulo }
230f05cddf9SRui Paulo 
231f05cddf9SRui Paulo 
232f05cddf9SRui Paulo static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
233*5b9c547cSRui Paulo 			     u8 dialog_token, u16 status_code, u32 peer_capab,
234*5b9c547cSRui Paulo 			     int initiator, const u8 *msg, size_t msg_len)
235f05cddf9SRui Paulo {
236f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
237f05cddf9SRui Paulo 
238f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u "
239*5b9c547cSRui Paulo 		   "dialog_token=%u status_code=%u peer_capab=%u initiator=%d "
240*5b9c547cSRui Paulo 		   "msg_len=%u",
241f05cddf9SRui Paulo 		   MAC2STR(dest), action_code, dialog_token, status_code,
242*5b9c547cSRui Paulo 		   peer_capab, initiator, (unsigned int) msg_len);
243f05cddf9SRui Paulo 
244f05cddf9SRui Paulo 	if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
245*5b9c547cSRui Paulo 				  status_code, peer_capab, initiator, msg,
246*5b9c547cSRui Paulo 				  msg_len)) {
247f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Failed to send message "
248f05cddf9SRui Paulo 			   "(action_code=%u)", action_code);
249f05cddf9SRui Paulo 		return -1;
250f05cddf9SRui Paulo 	}
251f05cddf9SRui Paulo 
252f05cddf9SRui Paulo 	if (action_code == WLAN_TDLS_SETUP_CONFIRM ||
253f05cddf9SRui Paulo 	    action_code == WLAN_TDLS_TEARDOWN ||
254f05cddf9SRui Paulo 	    action_code == WLAN_TDLS_DISCOVERY_REQUEST ||
255f05cddf9SRui Paulo 	    action_code == WLAN_TDLS_DISCOVERY_RESPONSE)
256f05cddf9SRui Paulo 		return 0; /* No retries */
257f05cddf9SRui Paulo 
258f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
259f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0)
260f05cddf9SRui Paulo 			break;
261f05cddf9SRui Paulo 	}
262f05cddf9SRui Paulo 
263f05cddf9SRui Paulo 	if (peer == NULL) {
264f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
265f05cddf9SRui Paulo 			   "retry " MACSTR, MAC2STR(dest));
266f05cddf9SRui Paulo 		return 0;
267f05cddf9SRui Paulo 	}
268f05cddf9SRui Paulo 
269f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
270f05cddf9SRui Paulo 
271*5b9c547cSRui Paulo 	if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
272*5b9c547cSRui Paulo 		peer->sm_tmr.count = TPK_M2_RETRY_COUNT;
273*5b9c547cSRui Paulo 		peer->sm_tmr.timer = TPK_M2_TIMEOUT;
274*5b9c547cSRui Paulo 	} else {
275*5b9c547cSRui Paulo 		peer->sm_tmr.count = TPK_M1_RETRY_COUNT;
276*5b9c547cSRui Paulo 		peer->sm_tmr.timer = TPK_M1_TIMEOUT;
277*5b9c547cSRui Paulo 	}
278f05cddf9SRui Paulo 
279f05cddf9SRui Paulo 	/* Copy message to resend on timeout */
280f05cddf9SRui Paulo 	os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN);
281f05cddf9SRui Paulo 	peer->sm_tmr.action_code = action_code;
282f05cddf9SRui Paulo 	peer->sm_tmr.dialog_token = dialog_token;
283f05cddf9SRui Paulo 	peer->sm_tmr.status_code = status_code;
284*5b9c547cSRui Paulo 	peer->sm_tmr.peer_capab = peer_capab;
285f05cddf9SRui Paulo 	peer->sm_tmr.buf_len = msg_len;
286f05cddf9SRui Paulo 	os_free(peer->sm_tmr.buf);
287f05cddf9SRui Paulo 	peer->sm_tmr.buf = os_malloc(msg_len);
288f05cddf9SRui Paulo 	if (peer->sm_tmr.buf == NULL)
289f05cddf9SRui Paulo 		return -1;
290f05cddf9SRui Paulo 	os_memcpy(peer->sm_tmr.buf, msg, msg_len);
291f05cddf9SRui Paulo 
292f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
293f05cddf9SRui Paulo 		   "(action_code=%u)", action_code);
294*5b9c547cSRui Paulo 	eloop_register_timeout(peer->sm_tmr.timer / 1000,
295*5b9c547cSRui Paulo 			       (peer->sm_tmr.timer % 1000) * 1000,
296f05cddf9SRui Paulo 			       wpa_tdls_tpk_retry_timeout, sm, peer);
297f05cddf9SRui Paulo 	return 0;
298f05cddf9SRui Paulo }
299f05cddf9SRui Paulo 
300f05cddf9SRui Paulo 
301f05cddf9SRui Paulo static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
302*5b9c547cSRui Paulo 				u16 reason_code)
303f05cddf9SRui Paulo {
304f05cddf9SRui Paulo 	int ret;
305f05cddf9SRui Paulo 
306f05cddf9SRui Paulo 	ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code);
307f05cddf9SRui Paulo 	/* disable the link after teardown was sent */
308*5b9c547cSRui Paulo 	wpa_tdls_disable_peer_link(sm, peer);
309f05cddf9SRui Paulo 
310f05cddf9SRui Paulo 	return ret;
311f05cddf9SRui Paulo }
312f05cddf9SRui Paulo 
313f05cddf9SRui Paulo 
314f05cddf9SRui Paulo static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx)
315f05cddf9SRui Paulo {
316f05cddf9SRui Paulo 
317f05cddf9SRui Paulo 	struct wpa_sm *sm = eloop_ctx;
318f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer = timeout_ctx;
319f05cddf9SRui Paulo 
320f05cddf9SRui Paulo 	if (peer->sm_tmr.count) {
321f05cddf9SRui Paulo 		peer->sm_tmr.count--;
322f05cddf9SRui Paulo 
323f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Retrying sending of message "
324f05cddf9SRui Paulo 			   "(action_code=%u)",
325f05cddf9SRui Paulo 			   peer->sm_tmr.action_code);
326f05cddf9SRui Paulo 
327f05cddf9SRui Paulo 		if (peer->sm_tmr.buf == NULL) {
328f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: No retry buffer available "
329f05cddf9SRui Paulo 				   "for action_code=%u",
330f05cddf9SRui Paulo 				   peer->sm_tmr.action_code);
331f05cddf9SRui Paulo 			eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm,
332f05cddf9SRui Paulo 					     peer);
333f05cddf9SRui Paulo 			return;
334f05cddf9SRui Paulo 		}
335f05cddf9SRui Paulo 
336f05cddf9SRui Paulo 		/* resend TPK Handshake Message to Peer */
337f05cddf9SRui Paulo 		if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest,
338f05cddf9SRui Paulo 					  peer->sm_tmr.action_code,
339f05cddf9SRui Paulo 					  peer->sm_tmr.dialog_token,
340f05cddf9SRui Paulo 					  peer->sm_tmr.status_code,
341*5b9c547cSRui Paulo 					  peer->sm_tmr.peer_capab,
342*5b9c547cSRui Paulo 					  peer->initiator,
343f05cddf9SRui Paulo 					  peer->sm_tmr.buf,
344f05cddf9SRui Paulo 					  peer->sm_tmr.buf_len)) {
345f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: Failed to retry "
346f05cddf9SRui Paulo 				   "transmission");
347f05cddf9SRui Paulo 		}
348f05cddf9SRui Paulo 
349f05cddf9SRui Paulo 		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
350*5b9c547cSRui Paulo 		eloop_register_timeout(peer->sm_tmr.timer / 1000,
351*5b9c547cSRui Paulo 				       (peer->sm_tmr.timer % 1000) * 1000,
352f05cddf9SRui Paulo 				       wpa_tdls_tpk_retry_timeout, sm, peer);
353f05cddf9SRui Paulo 	} else {
354f05cddf9SRui Paulo 		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
355f05cddf9SRui Paulo 
356f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request");
357f05cddf9SRui Paulo 		wpa_tdls_do_teardown(sm, peer,
358*5b9c547cSRui Paulo 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
359f05cddf9SRui Paulo 	}
360f05cddf9SRui Paulo }
361f05cddf9SRui Paulo 
362f05cddf9SRui Paulo 
363f05cddf9SRui Paulo static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm,
364f05cddf9SRui Paulo 					      struct wpa_tdls_peer *peer,
365f05cddf9SRui Paulo 					      u8 action_code)
366f05cddf9SRui Paulo {
367f05cddf9SRui Paulo 	if (action_code == peer->sm_tmr.action_code) {
368f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for "
369f05cddf9SRui Paulo 			   "action_code=%u", action_code);
370f05cddf9SRui Paulo 
371f05cddf9SRui Paulo 		/* Cancel Timeout registered */
372f05cddf9SRui Paulo 		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
373f05cddf9SRui Paulo 
374f05cddf9SRui Paulo 		/* free all resources meant for retry */
375f05cddf9SRui Paulo 		os_free(peer->sm_tmr.buf);
376f05cddf9SRui Paulo 		peer->sm_tmr.buf = NULL;
377f05cddf9SRui Paulo 
378f05cddf9SRui Paulo 		peer->sm_tmr.count = 0;
379f05cddf9SRui Paulo 		peer->sm_tmr.timer = 0;
380f05cddf9SRui Paulo 		peer->sm_tmr.buf_len = 0;
381f05cddf9SRui Paulo 		peer->sm_tmr.action_code = 0xff;
382f05cddf9SRui Paulo 	} else {
383f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout "
384f05cddf9SRui Paulo 			   "(Unknown action_code=%u)", action_code);
385f05cddf9SRui Paulo 	}
386f05cddf9SRui Paulo }
387f05cddf9SRui Paulo 
388f05cddf9SRui Paulo 
389f05cddf9SRui Paulo static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer,
390f05cddf9SRui Paulo 				  const u8 *own_addr, const u8 *bssid)
391f05cddf9SRui Paulo {
392f05cddf9SRui Paulo 	u8 key_input[SHA256_MAC_LEN];
393f05cddf9SRui Paulo 	const u8 *nonce[2];
394f05cddf9SRui Paulo 	size_t len[2];
395f05cddf9SRui Paulo 	u8 data[3 * ETH_ALEN];
396f05cddf9SRui Paulo 
397f05cddf9SRui Paulo 	/* IEEE Std 802.11z-2010 8.5.9.1:
398f05cddf9SRui Paulo 	 * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
399f05cddf9SRui Paulo 	 */
400f05cddf9SRui Paulo 	len[0] = WPA_NONCE_LEN;
401f05cddf9SRui Paulo 	len[1] = WPA_NONCE_LEN;
402f05cddf9SRui Paulo 	if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) {
403f05cddf9SRui Paulo 		nonce[0] = peer->inonce;
404f05cddf9SRui Paulo 		nonce[1] = peer->rnonce;
405f05cddf9SRui Paulo 	} else {
406f05cddf9SRui Paulo 		nonce[0] = peer->rnonce;
407f05cddf9SRui Paulo 		nonce[1] = peer->inonce;
408f05cddf9SRui Paulo 	}
409f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN);
410f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN);
411f05cddf9SRui Paulo 	sha256_vector(2, nonce, len, key_input);
412f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
413f05cddf9SRui Paulo 			key_input, SHA256_MAC_LEN);
414f05cddf9SRui Paulo 
415f05cddf9SRui Paulo 	/*
416f05cddf9SRui Paulo 	 * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
417f05cddf9SRui Paulo 	 *	min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
418f05cddf9SRui Paulo 	 * TODO: is N_KEY really included in KDF Context and if so, in which
419f05cddf9SRui Paulo 	 * presentation format (little endian 16-bit?) is it used? It gets
420f05cddf9SRui Paulo 	 * added by the KDF anyway..
421f05cddf9SRui Paulo 	 */
422f05cddf9SRui Paulo 
423f05cddf9SRui Paulo 	if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
424f05cddf9SRui Paulo 		os_memcpy(data, own_addr, ETH_ALEN);
425f05cddf9SRui Paulo 		os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN);
426f05cddf9SRui Paulo 	} else {
427f05cddf9SRui Paulo 		os_memcpy(data, peer->addr, ETH_ALEN);
428f05cddf9SRui Paulo 		os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN);
429f05cddf9SRui Paulo 	}
430f05cddf9SRui Paulo 	os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
431f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
432f05cddf9SRui Paulo 
433f05cddf9SRui Paulo 	sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
434f05cddf9SRui Paulo 		   (u8 *) &peer->tpk, sizeof(peer->tpk));
435f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
436f05cddf9SRui Paulo 			peer->tpk.kck, sizeof(peer->tpk.kck));
437f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
438f05cddf9SRui Paulo 			peer->tpk.tk, sizeof(peer->tpk.tk));
439f05cddf9SRui Paulo 	peer->tpk_set = 1;
440f05cddf9SRui Paulo }
441f05cddf9SRui Paulo 
442f05cddf9SRui Paulo 
443f05cddf9SRui Paulo /**
444f05cddf9SRui Paulo  * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
445f05cddf9SRui Paulo  * @kck: TPK-KCK
446f05cddf9SRui Paulo  * @lnkid: Pointer to the beginning of Link Identifier IE
447f05cddf9SRui Paulo  * @rsnie: Pointer to the beginning of RSN IE used for handshake
448f05cddf9SRui Paulo  * @timeoutie: Pointer to the beginning of Timeout IE used for handshake
449f05cddf9SRui Paulo  * @ftie: Pointer to the beginning of FT IE
450f05cddf9SRui Paulo  * @mic: Pointer for writing MIC
451f05cddf9SRui Paulo  *
452f05cddf9SRui Paulo  * Calculate MIC for TDLS frame.
453f05cddf9SRui Paulo  */
454f05cddf9SRui Paulo static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
455f05cddf9SRui Paulo 			     const u8 *rsnie, const u8 *timeoutie,
456f05cddf9SRui Paulo 			     const u8 *ftie, u8 *mic)
457f05cddf9SRui Paulo {
458f05cddf9SRui Paulo 	u8 *buf, *pos;
459f05cddf9SRui Paulo 	struct wpa_tdls_ftie *_ftie;
460f05cddf9SRui Paulo 	const struct wpa_tdls_lnkid *_lnkid;
461f05cddf9SRui Paulo 	int ret;
462f05cddf9SRui Paulo 	int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] +
463f05cddf9SRui Paulo 		2 + timeoutie[1] + 2 + ftie[1];
464f05cddf9SRui Paulo 	buf = os_zalloc(len);
465f05cddf9SRui Paulo 	if (!buf) {
466f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
467f05cddf9SRui Paulo 		return -1;
468f05cddf9SRui Paulo 	}
469f05cddf9SRui Paulo 
470f05cddf9SRui Paulo 	pos = buf;
471f05cddf9SRui Paulo 	_lnkid = (const struct wpa_tdls_lnkid *) lnkid;
472f05cddf9SRui Paulo 	/* 1) TDLS initiator STA MAC address */
473f05cddf9SRui Paulo 	os_memcpy(pos, _lnkid->init_sta, ETH_ALEN);
474f05cddf9SRui Paulo 	pos += ETH_ALEN;
475f05cddf9SRui Paulo 	/* 2) TDLS responder STA MAC address */
476f05cddf9SRui Paulo 	os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN);
477f05cddf9SRui Paulo 	pos += ETH_ALEN;
478f05cddf9SRui Paulo 	/* 3) Transaction Sequence number */
479f05cddf9SRui Paulo 	*pos++ = trans_seq;
480f05cddf9SRui Paulo 	/* 4) Link Identifier IE */
481f05cddf9SRui Paulo 	os_memcpy(pos, lnkid, 2 + lnkid[1]);
482f05cddf9SRui Paulo 	pos += 2 + lnkid[1];
483f05cddf9SRui Paulo 	/* 5) RSN IE */
484f05cddf9SRui Paulo 	os_memcpy(pos, rsnie, 2 + rsnie[1]);
485f05cddf9SRui Paulo 	pos += 2 + rsnie[1];
486f05cddf9SRui Paulo 	/* 6) Timeout Interval IE */
487f05cddf9SRui Paulo 	os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
488f05cddf9SRui Paulo 	pos += 2 + timeoutie[1];
489f05cddf9SRui Paulo 	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
490f05cddf9SRui Paulo 	os_memcpy(pos, ftie, 2 + ftie[1]);
491f05cddf9SRui Paulo 	_ftie = (struct wpa_tdls_ftie *) pos;
492f05cddf9SRui Paulo 	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
493f05cddf9SRui Paulo 	pos += 2 + ftie[1];
494f05cddf9SRui Paulo 
495f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
496f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
497f05cddf9SRui Paulo 	ret = omac1_aes_128(kck, buf, pos - buf, mic);
498f05cddf9SRui Paulo 	os_free(buf);
499f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
500f05cddf9SRui Paulo 	return ret;
501f05cddf9SRui Paulo }
502f05cddf9SRui Paulo 
503f05cddf9SRui Paulo 
504f05cddf9SRui Paulo /**
505f05cddf9SRui Paulo  * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame
506f05cddf9SRui Paulo  * @kck: TPK-KCK
507f05cddf9SRui Paulo  * @trans_seq: Transaction Sequence Number (4 - Teardown)
508f05cddf9SRui Paulo  * @rcode: Reason code for Teardown
509f05cddf9SRui Paulo  * @dtoken: Dialog Token used for that particular link
510f05cddf9SRui Paulo  * @lnkid: Pointer to the beginning of Link Identifier IE
511f05cddf9SRui Paulo  * @ftie: Pointer to the beginning of FT IE
512f05cddf9SRui Paulo  * @mic: Pointer for writing MIC
513f05cddf9SRui Paulo  *
514f05cddf9SRui Paulo  * Calculate MIC for TDLS frame.
515f05cddf9SRui Paulo  */
516f05cddf9SRui Paulo static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
517f05cddf9SRui Paulo 				     u8 dtoken, const u8 *lnkid,
518f05cddf9SRui Paulo 				     const u8 *ftie, u8 *mic)
519f05cddf9SRui Paulo {
520f05cddf9SRui Paulo 	u8 *buf, *pos;
521f05cddf9SRui Paulo 	struct wpa_tdls_ftie *_ftie;
522f05cddf9SRui Paulo 	int ret;
523f05cddf9SRui Paulo 	int len;
524f05cddf9SRui Paulo 
525f05cddf9SRui Paulo 	if (lnkid == NULL)
526f05cddf9SRui Paulo 		return -1;
527f05cddf9SRui Paulo 
528f05cddf9SRui Paulo 	len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
529f05cddf9SRui Paulo 		sizeof(trans_seq) + 2 + ftie[1];
530f05cddf9SRui Paulo 
531f05cddf9SRui Paulo 	buf = os_zalloc(len);
532f05cddf9SRui Paulo 	if (!buf) {
533f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
534f05cddf9SRui Paulo 		return -1;
535f05cddf9SRui Paulo 	}
536f05cddf9SRui Paulo 
537f05cddf9SRui Paulo 	pos = buf;
538f05cddf9SRui Paulo 	/* 1) Link Identifier IE */
539f05cddf9SRui Paulo 	os_memcpy(pos, lnkid, 2 + lnkid[1]);
540f05cddf9SRui Paulo 	pos += 2 + lnkid[1];
541f05cddf9SRui Paulo 	/* 2) Reason Code */
542f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, rcode);
543f05cddf9SRui Paulo 	pos += sizeof(rcode);
544f05cddf9SRui Paulo 	/* 3) Dialog token */
545f05cddf9SRui Paulo 	*pos++ = dtoken;
546f05cddf9SRui Paulo 	/* 4) Transaction Sequence number */
547f05cddf9SRui Paulo 	*pos++ = trans_seq;
548f05cddf9SRui Paulo 	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
549f05cddf9SRui Paulo 	os_memcpy(pos, ftie, 2 + ftie[1]);
550f05cddf9SRui Paulo 	_ftie = (struct wpa_tdls_ftie *) pos;
551f05cddf9SRui Paulo 	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
552f05cddf9SRui Paulo 	pos += 2 + ftie[1];
553f05cddf9SRui Paulo 
554f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
555f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
556f05cddf9SRui Paulo 	ret = omac1_aes_128(kck, buf, pos - buf, mic);
557f05cddf9SRui Paulo 	os_free(buf);
558f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
559f05cddf9SRui Paulo 	return ret;
560f05cddf9SRui Paulo }
561f05cddf9SRui Paulo 
562f05cddf9SRui Paulo 
563f05cddf9SRui Paulo static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
564f05cddf9SRui Paulo 					  struct wpa_tdls_peer *peer,
565f05cddf9SRui Paulo 					  const u8 *lnkid, const u8 *timeoutie,
566f05cddf9SRui Paulo 					  const struct wpa_tdls_ftie *ftie)
567f05cddf9SRui Paulo {
568f05cddf9SRui Paulo 	u8 mic[16];
569f05cddf9SRui Paulo 
570f05cddf9SRui Paulo 	if (peer->tpk_set) {
571f05cddf9SRui Paulo 		wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
572f05cddf9SRui Paulo 				  peer->rsnie_p, timeoutie, (u8 *) ftie,
573f05cddf9SRui Paulo 				  mic);
574*5b9c547cSRui Paulo 		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
575f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
576f05cddf9SRui Paulo 				   "dropping packet");
577f05cddf9SRui Paulo 			wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC",
578f05cddf9SRui Paulo 				    ftie->mic, 16);
579f05cddf9SRui Paulo 			wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC",
580f05cddf9SRui Paulo 				    mic, 16);
581f05cddf9SRui Paulo 			return -1;
582f05cddf9SRui Paulo 		}
583f05cddf9SRui Paulo 	} else {
584f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, "
585f05cddf9SRui Paulo 			   "TPK not set - dropping packet");
586f05cddf9SRui Paulo 		return -1;
587f05cddf9SRui Paulo 	}
588f05cddf9SRui Paulo 	return 0;
589f05cddf9SRui Paulo }
590f05cddf9SRui Paulo 
591f05cddf9SRui Paulo 
592f05cddf9SRui Paulo static int wpa_supplicant_verify_tdls_mic_teardown(
593f05cddf9SRui Paulo 	u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
594f05cddf9SRui Paulo 	const u8 *lnkid, const struct wpa_tdls_ftie *ftie)
595f05cddf9SRui Paulo {
596f05cddf9SRui Paulo 	u8 mic[16];
597f05cddf9SRui Paulo 
598f05cddf9SRui Paulo 	if (peer->tpk_set) {
599f05cddf9SRui Paulo 		wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
600f05cddf9SRui Paulo 					  dtoken, lnkid, (u8 *) ftie, mic);
601*5b9c547cSRui Paulo 		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
602f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
603f05cddf9SRui Paulo 				   "dropping packet");
604f05cddf9SRui Paulo 			return -1;
605f05cddf9SRui Paulo 		}
606f05cddf9SRui Paulo 	} else {
607f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown "
608f05cddf9SRui Paulo 			   "MIC, TPK not set - dropping packet");
609f05cddf9SRui Paulo 		return -1;
610f05cddf9SRui Paulo 	}
611f05cddf9SRui Paulo 	return 0;
612f05cddf9SRui Paulo }
613f05cddf9SRui Paulo 
614f05cddf9SRui Paulo 
615f05cddf9SRui Paulo static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx)
616f05cddf9SRui Paulo {
617f05cddf9SRui Paulo 	struct wpa_sm *sm = eloop_ctx;
618f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer = timeout_ctx;
619f05cddf9SRui Paulo 
620f05cddf9SRui Paulo 	/*
621f05cddf9SRui Paulo 	 * On TPK lifetime expiration, we have an option of either tearing down
622f05cddf9SRui Paulo 	 * the direct link or trying to re-initiate it. The selection of what
623f05cddf9SRui Paulo 	 * to do is not strictly speaking controlled by our role in the expired
624f05cddf9SRui Paulo 	 * link, but for now, use that to select whether to renew or tear down
625f05cddf9SRui Paulo 	 * the link.
626f05cddf9SRui Paulo 	 */
627f05cddf9SRui Paulo 
628f05cddf9SRui Paulo 	if (peer->initiator) {
629f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
630f05cddf9SRui Paulo 			   " - try to renew", MAC2STR(peer->addr));
631f05cddf9SRui Paulo 		wpa_tdls_start(sm, peer->addr);
632f05cddf9SRui Paulo 	} else {
633f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
634f05cddf9SRui Paulo 			   " - tear down", MAC2STR(peer->addr));
635f05cddf9SRui Paulo 		wpa_tdls_do_teardown(sm, peer,
636*5b9c547cSRui Paulo 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
637f05cddf9SRui Paulo 	}
638f05cddf9SRui Paulo }
639f05cddf9SRui Paulo 
640f05cddf9SRui Paulo 
641*5b9c547cSRui Paulo static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm,
642*5b9c547cSRui Paulo 					   struct wpa_tdls_peer *peer)
643*5b9c547cSRui Paulo {
644*5b9c547cSRui Paulo 	struct wpa_tdls_peer *cur, *prev;
645*5b9c547cSRui Paulo 
646*5b9c547cSRui Paulo 	cur = sm->tdls;
647*5b9c547cSRui Paulo 	prev = NULL;
648*5b9c547cSRui Paulo 	while (cur && cur != peer) {
649*5b9c547cSRui Paulo 		prev = cur;
650*5b9c547cSRui Paulo 		cur = cur->next;
651*5b9c547cSRui Paulo 	}
652*5b9c547cSRui Paulo 
653*5b9c547cSRui Paulo 	if (cur != peer) {
654*5b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR
655*5b9c547cSRui Paulo 			   " to remove it from the list",
656*5b9c547cSRui Paulo 			   MAC2STR(peer->addr));
657*5b9c547cSRui Paulo 		return;
658*5b9c547cSRui Paulo 	}
659*5b9c547cSRui Paulo 
660*5b9c547cSRui Paulo 	if (prev)
661*5b9c547cSRui Paulo 		prev->next = peer->next;
662*5b9c547cSRui Paulo 	else
663*5b9c547cSRui Paulo 		sm->tdls = peer->next;
664*5b9c547cSRui Paulo }
665*5b9c547cSRui Paulo 
666*5b9c547cSRui Paulo 
667*5b9c547cSRui Paulo static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
668f05cddf9SRui Paulo {
669f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
670f05cddf9SRui Paulo 		   MAC2STR(peer->addr));
671f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
672f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
673*5b9c547cSRui Paulo 	peer->reconfig_key = 0;
674f05cddf9SRui Paulo 	peer->initiator = 0;
675*5b9c547cSRui Paulo 	peer->tpk_in_progress = 0;
676f05cddf9SRui Paulo 	os_free(peer->sm_tmr.buf);
677f05cddf9SRui Paulo 	peer->sm_tmr.buf = NULL;
678*5b9c547cSRui Paulo 	os_free(peer->ht_capabilities);
679*5b9c547cSRui Paulo 	peer->ht_capabilities = NULL;
680*5b9c547cSRui Paulo 	os_free(peer->vht_capabilities);
681*5b9c547cSRui Paulo 	peer->vht_capabilities = NULL;
682*5b9c547cSRui Paulo 	os_free(peer->ext_capab);
683*5b9c547cSRui Paulo 	peer->ext_capab = NULL;
684*5b9c547cSRui Paulo 	os_free(peer->supp_channels);
685*5b9c547cSRui Paulo 	peer->supp_channels = NULL;
686*5b9c547cSRui Paulo 	os_free(peer->supp_oper_classes);
687*5b9c547cSRui Paulo 	peer->supp_oper_classes = NULL;
688f05cddf9SRui Paulo 	peer->rsnie_i_len = peer->rsnie_p_len = 0;
689f05cddf9SRui Paulo 	peer->cipher = 0;
690*5b9c547cSRui Paulo 	peer->qos_info = 0;
691*5b9c547cSRui Paulo 	peer->wmm_capable = 0;
692f05cddf9SRui Paulo 	peer->tpk_set = peer->tpk_success = 0;
693*5b9c547cSRui Paulo 	peer->chan_switch_enabled = 0;
694f05cddf9SRui Paulo 	os_memset(&peer->tpk, 0, sizeof(peer->tpk));
695f05cddf9SRui Paulo 	os_memset(peer->inonce, 0, WPA_NONCE_LEN);
696f05cddf9SRui Paulo 	os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
697f05cddf9SRui Paulo }
698f05cddf9SRui Paulo 
699f05cddf9SRui Paulo 
700*5b9c547cSRui Paulo static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
701*5b9c547cSRui Paulo {
702*5b9c547cSRui Paulo 	wpa_tdls_peer_clear(sm, peer);
703*5b9c547cSRui Paulo 	wpa_tdls_peer_remove_from_list(sm, peer);
704*5b9c547cSRui Paulo 	os_free(peer);
705*5b9c547cSRui Paulo }
706*5b9c547cSRui Paulo 
707*5b9c547cSRui Paulo 
708f05cddf9SRui Paulo static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
709f05cddf9SRui Paulo 			    struct wpa_tdls_lnkid *lnkid)
710f05cddf9SRui Paulo {
711f05cddf9SRui Paulo 	lnkid->ie_type = WLAN_EID_LINK_ID;
712f05cddf9SRui Paulo 	lnkid->ie_len = 3 * ETH_ALEN;
713f05cddf9SRui Paulo 	os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN);
714f05cddf9SRui Paulo 	if (peer->initiator) {
715f05cddf9SRui Paulo 		os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN);
716f05cddf9SRui Paulo 		os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN);
717f05cddf9SRui Paulo 	} else {
718f05cddf9SRui Paulo 		os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN);
719f05cddf9SRui Paulo 		os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN);
720f05cddf9SRui Paulo 	}
721f05cddf9SRui Paulo }
722f05cddf9SRui Paulo 
723f05cddf9SRui Paulo 
724*5b9c547cSRui Paulo static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
725*5b9c547cSRui Paulo 				  u16 reason_code)
726f05cddf9SRui Paulo {
727f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
728f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
729f05cddf9SRui Paulo 	struct wpa_tdls_lnkid lnkid;
730f05cddf9SRui Paulo 	u8 dialog_token;
731f05cddf9SRui Paulo 	u8 *rbuf, *pos;
732f05cddf9SRui Paulo 	int ielen;
733f05cddf9SRui Paulo 
734f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
735f05cddf9SRui Paulo 		return -1;
736f05cddf9SRui Paulo 
737f05cddf9SRui Paulo 	/* Find the node and free from the list */
738f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
739f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
740f05cddf9SRui Paulo 			break;
741f05cddf9SRui Paulo 	}
742f05cddf9SRui Paulo 
743f05cddf9SRui Paulo 	if (peer == NULL) {
744f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
745f05cddf9SRui Paulo 			   "Teardown " MACSTR, MAC2STR(addr));
746f05cddf9SRui Paulo 		return 0;
747f05cddf9SRui Paulo 	}
748f05cddf9SRui Paulo 
749*5b9c547cSRui Paulo 	/* Cancel active channel switch before teardown */
750*5b9c547cSRui Paulo 	if (peer->chan_switch_enabled) {
751*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR
752*5b9c547cSRui Paulo 			   " to base channel", MAC2STR(addr));
753*5b9c547cSRui Paulo 		wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
754*5b9c547cSRui Paulo 	}
755*5b9c547cSRui Paulo 
756f05cddf9SRui Paulo 	dialog_token = peer->dtoken;
757f05cddf9SRui Paulo 
758f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR,
759f05cddf9SRui Paulo 		   MAC2STR(addr));
760f05cddf9SRui Paulo 
761f05cddf9SRui Paulo 	ielen = 0;
762f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) {
763f05cddf9SRui Paulo 		/* To add FTIE for Teardown request and compute MIC */
764f05cddf9SRui Paulo 		ielen += sizeof(*ftie);
765f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
766f05cddf9SRui Paulo 		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
767f05cddf9SRui Paulo 			ielen += 170;
768f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
769f05cddf9SRui Paulo 	}
770f05cddf9SRui Paulo 
771f05cddf9SRui Paulo 	rbuf = os_zalloc(ielen + 1);
772f05cddf9SRui Paulo 	if (rbuf == NULL)
773f05cddf9SRui Paulo 		return -1;
774f05cddf9SRui Paulo 	pos = rbuf;
775f05cddf9SRui Paulo 
776f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
777f05cddf9SRui Paulo 		goto skip_ies;
778f05cddf9SRui Paulo 
779f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) pos;
780f05cddf9SRui Paulo 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
781f05cddf9SRui Paulo 	/* Using the recent nonce which should be for CONFIRM frame */
782f05cddf9SRui Paulo 	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
783f05cddf9SRui Paulo 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
784f05cddf9SRui Paulo 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
785f05cddf9SRui Paulo 	pos = (u8 *) (ftie + 1);
786f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
787f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
788f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
789f05cddf9SRui Paulo 			   "FTIE");
790f05cddf9SRui Paulo 		ftie->ie_len += 170;
791f05cddf9SRui Paulo 		*pos++ = 255; /* FTIE subelem */
792f05cddf9SRui Paulo 		*pos++ = 168; /* FTIE subelem length */
793f05cddf9SRui Paulo 		pos += 168;
794f05cddf9SRui Paulo 	}
795f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
796f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake",
797f05cddf9SRui Paulo 		    (u8 *) ftie, pos - (u8 *) ftie);
798f05cddf9SRui Paulo 
799f05cddf9SRui Paulo 	/* compute MIC before sending */
800f05cddf9SRui Paulo 	wpa_tdls_linkid(sm, peer, &lnkid);
801f05cddf9SRui Paulo 	wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
802f05cddf9SRui Paulo 				  dialog_token, (u8 *) &lnkid, (u8 *) ftie,
803f05cddf9SRui Paulo 				  ftie->mic);
804f05cddf9SRui Paulo 
805f05cddf9SRui Paulo skip_ies:
806f05cddf9SRui Paulo 	/* TODO: register for a Timeout handler, if Teardown is not received at
807f05cddf9SRui Paulo 	 * the other end, then try again another time */
808f05cddf9SRui Paulo 
809f05cddf9SRui Paulo 	/* request driver to send Teardown using this FTIE */
810f05cddf9SRui Paulo 	wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
811*5b9c547cSRui Paulo 			  reason_code, 0, peer->initiator, rbuf, pos - rbuf);
812f05cddf9SRui Paulo 	os_free(rbuf);
813f05cddf9SRui Paulo 
814f05cddf9SRui Paulo 	return 0;
815f05cddf9SRui Paulo }
816f05cddf9SRui Paulo 
817f05cddf9SRui Paulo 
818f05cddf9SRui Paulo int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
819f05cddf9SRui Paulo {
820f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
821f05cddf9SRui Paulo 
822f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
823f05cddf9SRui Paulo 		return -1;
824f05cddf9SRui Paulo 
825f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
826f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
827f05cddf9SRui Paulo 			break;
828f05cddf9SRui Paulo 	}
829f05cddf9SRui Paulo 
830f05cddf9SRui Paulo 	if (peer == NULL) {
831f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR
832f05cddf9SRui Paulo 		   " for link Teardown", MAC2STR(addr));
833f05cddf9SRui Paulo 		return -1;
834f05cddf9SRui Paulo 	}
835f05cddf9SRui Paulo 
836f05cddf9SRui Paulo 	if (!peer->tpk_success) {
837f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
838f05cddf9SRui Paulo 		   " not connected - cannot Teardown link", MAC2STR(addr));
839f05cddf9SRui Paulo 		return -1;
840f05cddf9SRui Paulo 	}
841f05cddf9SRui Paulo 
842*5b9c547cSRui Paulo 	return wpa_tdls_do_teardown(sm, peer, reason_code);
843f05cddf9SRui Paulo }
844f05cddf9SRui Paulo 
845f05cddf9SRui Paulo 
846*5b9c547cSRui Paulo static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
847*5b9c547cSRui Paulo 				       struct wpa_tdls_peer *peer)
848*5b9c547cSRui Paulo {
849*5b9c547cSRui Paulo 	wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
850*5b9c547cSRui Paulo 	wpa_tdls_peer_free(sm, peer);
851*5b9c547cSRui Paulo }
852*5b9c547cSRui Paulo 
853*5b9c547cSRui Paulo 
854*5b9c547cSRui Paulo void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr)
855f05cddf9SRui Paulo {
856f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
857f05cddf9SRui Paulo 
858f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
859f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
860f05cddf9SRui Paulo 			break;
861f05cddf9SRui Paulo 	}
862f05cddf9SRui Paulo 
863*5b9c547cSRui Paulo 	if (!peer || !peer->tpk_success) {
864*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
865*5b9c547cSRui Paulo 			   " not connected - cannot teardown unreachable link",
866*5b9c547cSRui Paulo 			   MAC2STR(addr));
867*5b9c547cSRui Paulo 		return;
868f05cddf9SRui Paulo 	}
869*5b9c547cSRui Paulo 
870*5b9c547cSRui Paulo 	if (wpa_tdls_is_external_setup(sm)) {
871*5b9c547cSRui Paulo 		/*
872*5b9c547cSRui Paulo 		 * Get us on the base channel, disable the link, send a
873*5b9c547cSRui Paulo 		 * teardown packet through the AP, and then reset link data.
874*5b9c547cSRui Paulo 		 */
875*5b9c547cSRui Paulo 		if (peer->chan_switch_enabled)
876*5b9c547cSRui Paulo 			wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
877*5b9c547cSRui Paulo 		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
878*5b9c547cSRui Paulo 		wpa_tdls_send_teardown(sm, addr,
879*5b9c547cSRui Paulo 				       WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE);
880*5b9c547cSRui Paulo 		wpa_tdls_peer_free(sm, peer);
881*5b9c547cSRui Paulo 	} else {
882*5b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
883*5b9c547cSRui Paulo 	}
884*5b9c547cSRui Paulo }
885*5b9c547cSRui Paulo 
886*5b9c547cSRui Paulo 
887*5b9c547cSRui Paulo const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr)
888*5b9c547cSRui Paulo {
889*5b9c547cSRui Paulo 	struct wpa_tdls_peer *peer;
890*5b9c547cSRui Paulo 
891*5b9c547cSRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
892*5b9c547cSRui Paulo 		return "disabled";
893*5b9c547cSRui Paulo 
894*5b9c547cSRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
895*5b9c547cSRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
896*5b9c547cSRui Paulo 			break;
897*5b9c547cSRui Paulo 	}
898*5b9c547cSRui Paulo 
899*5b9c547cSRui Paulo 	if (peer == NULL)
900*5b9c547cSRui Paulo 		return "peer does not exist";
901*5b9c547cSRui Paulo 
902*5b9c547cSRui Paulo 	if (!peer->tpk_success)
903*5b9c547cSRui Paulo 		return "peer not connected";
904*5b9c547cSRui Paulo 
905*5b9c547cSRui Paulo 	return "connected";
906f05cddf9SRui Paulo }
907f05cddf9SRui Paulo 
908f05cddf9SRui Paulo 
909f05cddf9SRui Paulo static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr,
910f05cddf9SRui Paulo 				  const u8 *buf, size_t len)
911f05cddf9SRui Paulo {
912f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer = NULL;
913f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
914f05cddf9SRui Paulo 	struct wpa_tdls_lnkid *lnkid;
915f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
916f05cddf9SRui Paulo 	u16 reason_code;
917f05cddf9SRui Paulo 	const u8 *pos;
918f05cddf9SRui Paulo 	int ielen;
919f05cddf9SRui Paulo 
920f05cddf9SRui Paulo 	/* Find the node and free from the list */
921f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
922f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
923f05cddf9SRui Paulo 			break;
924f05cddf9SRui Paulo 	}
925f05cddf9SRui Paulo 
926f05cddf9SRui Paulo 	if (peer == NULL) {
927f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
928f05cddf9SRui Paulo 			   "Teardown " MACSTR, MAC2STR(src_addr));
929f05cddf9SRui Paulo 		return 0;
930f05cddf9SRui Paulo 	}
931f05cddf9SRui Paulo 
932f05cddf9SRui Paulo 	pos = buf;
933f05cddf9SRui Paulo 	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
934f05cddf9SRui Paulo 
935f05cddf9SRui Paulo 	reason_code = WPA_GET_LE16(pos);
936f05cddf9SRui Paulo 	pos += 2;
937f05cddf9SRui Paulo 
938f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR
939f05cddf9SRui Paulo 		   " (reason code %u)", MAC2STR(src_addr), reason_code);
940f05cddf9SRui Paulo 
941f05cddf9SRui Paulo 	ielen = len - (pos - buf); /* start of IE in buf */
942*5b9c547cSRui Paulo 
943*5b9c547cSRui Paulo 	/*
944*5b9c547cSRui Paulo 	 * Don't reject the message if failing to parse IEs. The IEs we need are
945*5b9c547cSRui Paulo 	 * explicitly checked below. Some APs may add arbitrary padding to the
946*5b9c547cSRui Paulo 	 * end of short TDLS frames and that would look like invalid IEs.
947*5b9c547cSRui Paulo 	 */
948*5b9c547cSRui Paulo 	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0)
949*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
950*5b9c547cSRui Paulo 			   "TDLS: Failed to parse IEs in Teardown - ignore as an interop workaround");
951f05cddf9SRui Paulo 
952f05cddf9SRui Paulo 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
953f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS "
954f05cddf9SRui Paulo 			   "Teardown");
955f05cddf9SRui Paulo 		return -1;
956f05cddf9SRui Paulo 	}
957f05cddf9SRui Paulo 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
958f05cddf9SRui Paulo 
959f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
960f05cddf9SRui Paulo 		goto skip_ftie;
961f05cddf9SRui Paulo 
962f05cddf9SRui Paulo 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
963f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown");
964f05cddf9SRui Paulo 		return -1;
965f05cddf9SRui Paulo 	}
966f05cddf9SRui Paulo 
967f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
968f05cddf9SRui Paulo 
969f05cddf9SRui Paulo 	/* Process MIC check to see if TDLS Teardown is right */
970f05cddf9SRui Paulo 	if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
971f05cddf9SRui Paulo 						    peer->dtoken, peer,
972f05cddf9SRui Paulo 						    (u8 *) lnkid, ftie) < 0) {
973f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
974f05cddf9SRui Paulo 			   "Teardown Request from " MACSTR, MAC2STR(src_addr));
975f05cddf9SRui Paulo 		return -1;
976f05cddf9SRui Paulo 	}
977f05cddf9SRui Paulo 
978f05cddf9SRui Paulo skip_ftie:
979f05cddf9SRui Paulo 	/*
980f05cddf9SRui Paulo 	 * Request the driver to disable the direct link and clear associated
981f05cddf9SRui Paulo 	 * keys.
982f05cddf9SRui Paulo 	 */
983*5b9c547cSRui Paulo 	wpa_tdls_disable_peer_link(sm, peer);
984f05cddf9SRui Paulo 	return 0;
985f05cddf9SRui Paulo }
986f05cddf9SRui Paulo 
987f05cddf9SRui Paulo 
988f05cddf9SRui Paulo /**
989f05cddf9SRui Paulo  * wpa_tdls_send_error - To send suitable TDLS status response with
990f05cddf9SRui Paulo  *	appropriate status code mentioning reason for error/failure.
991f05cddf9SRui Paulo  * @dst 	- MAC addr of Peer station
992f05cddf9SRui Paulo  * @tdls_action - TDLS frame type for which error code is sent
993*5b9c547cSRui Paulo  * @initiator   - was this end the initiator of the connection
994f05cddf9SRui Paulo  * @status 	- status code mentioning reason
995f05cddf9SRui Paulo  */
996f05cddf9SRui Paulo 
997f05cddf9SRui Paulo static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
998*5b9c547cSRui Paulo 			       u8 tdls_action, u8 dialog_token, int initiator,
999*5b9c547cSRui Paulo 			       u16 status)
1000f05cddf9SRui Paulo {
1001f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR
1002f05cddf9SRui Paulo 		   " (action=%u status=%u)",
1003f05cddf9SRui Paulo 		   MAC2STR(dst), tdls_action, status);
1004f05cddf9SRui Paulo 	return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
1005*5b9c547cSRui Paulo 				 0, initiator, NULL, 0);
1006f05cddf9SRui Paulo }
1007f05cddf9SRui Paulo 
1008f05cddf9SRui Paulo 
1009f05cddf9SRui Paulo static struct wpa_tdls_peer *
1010*5b9c547cSRui Paulo wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing)
1011f05cddf9SRui Paulo {
1012f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
1013f05cddf9SRui Paulo 
1014*5b9c547cSRui Paulo 	if (existing)
1015*5b9c547cSRui Paulo 		*existing = 0;
1016*5b9c547cSRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
1017*5b9c547cSRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) {
1018*5b9c547cSRui Paulo 			if (existing)
1019*5b9c547cSRui Paulo 				*existing = 1;
1020*5b9c547cSRui Paulo 			return peer; /* re-use existing entry */
1021*5b9c547cSRui Paulo 		}
1022*5b9c547cSRui Paulo 	}
1023*5b9c547cSRui Paulo 
1024f05cddf9SRui Paulo 	wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR,
1025f05cddf9SRui Paulo 		   MAC2STR(addr));
1026f05cddf9SRui Paulo 
1027f05cddf9SRui Paulo 	peer = os_zalloc(sizeof(*peer));
1028f05cddf9SRui Paulo 	if (peer == NULL)
1029f05cddf9SRui Paulo 		return NULL;
1030f05cddf9SRui Paulo 
1031f05cddf9SRui Paulo 	os_memcpy(peer->addr, addr, ETH_ALEN);
1032f05cddf9SRui Paulo 	peer->next = sm->tdls;
1033f05cddf9SRui Paulo 	sm->tdls = peer;
1034f05cddf9SRui Paulo 
1035f05cddf9SRui Paulo 	return peer;
1036f05cddf9SRui Paulo }
1037f05cddf9SRui Paulo 
1038f05cddf9SRui Paulo 
1039f05cddf9SRui Paulo static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm,
1040f05cddf9SRui Paulo 				struct wpa_tdls_peer *peer)
1041f05cddf9SRui Paulo {
1042f05cddf9SRui Paulo 	size_t buf_len;
1043f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie timeoutie;
1044f05cddf9SRui Paulo 	u16 rsn_capab;
1045f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
1046f05cddf9SRui Paulo 	u8 *rbuf, *pos, *count_pos;
1047f05cddf9SRui Paulo 	u16 count;
1048f05cddf9SRui Paulo 	struct rsn_ie_hdr *hdr;
1049*5b9c547cSRui Paulo 	int status;
1050f05cddf9SRui Paulo 
1051f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm)) {
1052f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No security used on the link");
1053f05cddf9SRui Paulo 		peer->rsnie_i_len = 0;
1054f05cddf9SRui Paulo 		goto skip_rsnie;
1055f05cddf9SRui Paulo 	}
1056f05cddf9SRui Paulo 
1057f05cddf9SRui Paulo 	/*
1058f05cddf9SRui Paulo 	 * TPK Handshake Message 1:
1059f05cddf9SRui Paulo 	 * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I,
1060f05cddf9SRui Paulo 	 * Timeout Interval IE))
1061f05cddf9SRui Paulo 	 */
1062f05cddf9SRui Paulo 
1063f05cddf9SRui Paulo 	/* Filling RSN IE */
1064f05cddf9SRui Paulo 	hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
1065f05cddf9SRui Paulo 	hdr->elem_id = WLAN_EID_RSN;
1066f05cddf9SRui Paulo 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
1067f05cddf9SRui Paulo 
1068f05cddf9SRui Paulo 	pos = (u8 *) (hdr + 1);
1069f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
1070f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
1071f05cddf9SRui Paulo 	count_pos = pos;
1072f05cddf9SRui Paulo 	pos += 2;
1073f05cddf9SRui Paulo 
1074f05cddf9SRui Paulo 	count = 0;
1075f05cddf9SRui Paulo 
1076f05cddf9SRui Paulo 	/*
1077f05cddf9SRui Paulo 	 * AES-CCMP is the default Encryption preferred for TDLS, so
1078f05cddf9SRui Paulo 	 * RSN IE is filled only with CCMP CIPHER
1079f05cddf9SRui Paulo 	 * Note: TKIP is not used to encrypt TDLS link.
1080f05cddf9SRui Paulo 	 *
1081f05cddf9SRui Paulo 	 * Regardless of the cipher used on the AP connection, select CCMP
1082f05cddf9SRui Paulo 	 * here.
1083f05cddf9SRui Paulo 	 */
1084f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
1085f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
1086f05cddf9SRui Paulo 	count++;
1087f05cddf9SRui Paulo 
1088f05cddf9SRui Paulo 	WPA_PUT_LE16(count_pos, count);
1089f05cddf9SRui Paulo 
1090f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, 1);
1091f05cddf9SRui Paulo 	pos += 2;
1092f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
1093f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
1094f05cddf9SRui Paulo 
1095f05cddf9SRui Paulo 	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
1096f05cddf9SRui Paulo 	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
1097f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1098f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
1099f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for "
1100f05cddf9SRui Paulo 			   "testing");
1101f05cddf9SRui Paulo 		rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
1102f05cddf9SRui Paulo 	}
1103f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1104f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, rsn_capab);
1105f05cddf9SRui Paulo 	pos += 2;
1106f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1107f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
1108f05cddf9SRui Paulo 		/* Number of PMKIDs */
1109f05cddf9SRui Paulo 		*pos++ = 0x00;
1110f05cddf9SRui Paulo 		*pos++ = 0x00;
1111f05cddf9SRui Paulo 	}
1112f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1113f05cddf9SRui Paulo 
1114f05cddf9SRui Paulo 	hdr->len = (pos - peer->rsnie_i) - 2;
1115f05cddf9SRui Paulo 	peer->rsnie_i_len = pos - peer->rsnie_i;
1116f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
1117f05cddf9SRui Paulo 		    peer->rsnie_i, peer->rsnie_i_len);
1118f05cddf9SRui Paulo 
1119f05cddf9SRui Paulo skip_rsnie:
1120f05cddf9SRui Paulo 	buf_len = 0;
1121f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm))
1122f05cddf9SRui Paulo 		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
1123f05cddf9SRui Paulo 			sizeof(struct wpa_tdls_timeoutie);
1124f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1125f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm) &&
1126f05cddf9SRui Paulo 	    (tdls_testing & TDLS_TESTING_LONG_FRAME))
1127f05cddf9SRui Paulo 		buf_len += 170;
1128f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_DIFF_BSSID)
1129f05cddf9SRui Paulo 		buf_len += sizeof(struct wpa_tdls_lnkid);
1130f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1131f05cddf9SRui Paulo 	rbuf = os_zalloc(buf_len + 1);
1132f05cddf9SRui Paulo 	if (rbuf == NULL) {
1133f05cddf9SRui Paulo 		wpa_tdls_peer_free(sm, peer);
1134f05cddf9SRui Paulo 		return -1;
1135f05cddf9SRui Paulo 	}
1136f05cddf9SRui Paulo 	pos = rbuf;
1137f05cddf9SRui Paulo 
1138f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm))
1139f05cddf9SRui Paulo 		goto skip_ies;
1140f05cddf9SRui Paulo 
1141f05cddf9SRui Paulo 	/* Initiator RSN IE */
1142f05cddf9SRui Paulo 	pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
1143f05cddf9SRui Paulo 
1144f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) pos;
1145f05cddf9SRui Paulo 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
1146f05cddf9SRui Paulo 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
1147f05cddf9SRui Paulo 
1148f05cddf9SRui Paulo 	if (os_get_random(peer->inonce, WPA_NONCE_LEN)) {
1149f05cddf9SRui Paulo 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
1150f05cddf9SRui Paulo 			"TDLS: Failed to get random data for initiator Nonce");
1151f05cddf9SRui Paulo 		os_free(rbuf);
1152f05cddf9SRui Paulo 		wpa_tdls_peer_free(sm, peer);
1153f05cddf9SRui Paulo 		return -1;
1154f05cddf9SRui Paulo 	}
1155f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake",
1156f05cddf9SRui Paulo 		    peer->inonce, WPA_NONCE_LEN);
1157f05cddf9SRui Paulo 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
1158f05cddf9SRui Paulo 
1159f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1",
1160f05cddf9SRui Paulo 		    (u8 *) ftie, sizeof(struct wpa_tdls_ftie));
1161f05cddf9SRui Paulo 
1162f05cddf9SRui Paulo 	pos = (u8 *) (ftie + 1);
1163f05cddf9SRui Paulo 
1164f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1165f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
1166f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
1167f05cddf9SRui Paulo 			   "FTIE");
1168f05cddf9SRui Paulo 		ftie->ie_len += 170;
1169f05cddf9SRui Paulo 		*pos++ = 255; /* FTIE subelem */
1170f05cddf9SRui Paulo 		*pos++ = 168; /* FTIE subelem length */
1171f05cddf9SRui Paulo 		pos += 168;
1172f05cddf9SRui Paulo 	}
1173f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1174f05cddf9SRui Paulo 
1175f05cddf9SRui Paulo 	/* Lifetime */
1176f05cddf9SRui Paulo 	peer->lifetime = TPK_LIFETIME;
1177f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1178f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) {
1179f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK "
1180f05cddf9SRui Paulo 			   "lifetime");
1181f05cddf9SRui Paulo 		peer->lifetime = 301;
1182f05cddf9SRui Paulo 	}
1183f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) {
1184f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK "
1185f05cddf9SRui Paulo 			   "lifetime");
1186f05cddf9SRui Paulo 		peer->lifetime = 0xffffffff;
1187f05cddf9SRui Paulo 	}
1188f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1189f05cddf9SRui Paulo 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
1190f05cddf9SRui Paulo 				     sizeof(timeoutie), peer->lifetime);
1191f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
1192f05cddf9SRui Paulo 
1193f05cddf9SRui Paulo skip_ies:
1194f05cddf9SRui Paulo 
1195f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1196f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_DIFF_BSSID) {
1197f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in "
1198f05cddf9SRui Paulo 			   "Link Identifier");
1199f05cddf9SRui Paulo 		struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
1200f05cddf9SRui Paulo 		wpa_tdls_linkid(sm, peer, l);
1201f05cddf9SRui Paulo 		l->bssid[5] ^= 0x01;
1202f05cddf9SRui Paulo 		pos += sizeof(*l);
1203f05cddf9SRui Paulo 	}
1204f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1205f05cddf9SRui Paulo 
1206f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK "
1207f05cddf9SRui Paulo 		   "Handshake Message 1 (peer " MACSTR ")",
1208f05cddf9SRui Paulo 		   MAC2STR(peer->addr));
1209f05cddf9SRui Paulo 
1210*5b9c547cSRui Paulo 	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST,
1211*5b9c547cSRui Paulo 				   1, 0, 0, peer->initiator, rbuf, pos - rbuf);
1212f05cddf9SRui Paulo 	os_free(rbuf);
1213f05cddf9SRui Paulo 
1214*5b9c547cSRui Paulo 	return status;
1215f05cddf9SRui Paulo }
1216f05cddf9SRui Paulo 
1217f05cddf9SRui Paulo 
1218f05cddf9SRui Paulo static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm,
1219f05cddf9SRui Paulo 				const unsigned char *src_addr, u8 dtoken,
1220f05cddf9SRui Paulo 				struct wpa_tdls_lnkid *lnkid,
1221f05cddf9SRui Paulo 				const struct wpa_tdls_peer *peer)
1222f05cddf9SRui Paulo {
1223f05cddf9SRui Paulo 	u8 *rbuf, *pos;
1224f05cddf9SRui Paulo 	size_t buf_len;
1225f05cddf9SRui Paulo 	u32 lifetime;
1226f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie timeoutie;
1227f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
1228*5b9c547cSRui Paulo 	int status;
1229f05cddf9SRui Paulo 
1230f05cddf9SRui Paulo 	buf_len = 0;
1231f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm)) {
1232f05cddf9SRui Paulo 		/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
1233f05cddf9SRui Paulo 		 * Lifetime */
1234f05cddf9SRui Paulo 		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
1235f05cddf9SRui Paulo 			sizeof(struct wpa_tdls_timeoutie);
1236f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1237f05cddf9SRui Paulo 		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
1238f05cddf9SRui Paulo 			buf_len += 170;
1239f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1240f05cddf9SRui Paulo 	}
1241f05cddf9SRui Paulo 
1242f05cddf9SRui Paulo 	rbuf = os_zalloc(buf_len + 1);
1243f05cddf9SRui Paulo 	if (rbuf == NULL)
1244f05cddf9SRui Paulo 		return -1;
1245f05cddf9SRui Paulo 	pos = rbuf;
1246f05cddf9SRui Paulo 
1247f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm))
1248f05cddf9SRui Paulo 		goto skip_ies;
1249f05cddf9SRui Paulo 
1250f05cddf9SRui Paulo 	/* Peer RSN IE */
1251f05cddf9SRui Paulo 	pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
1252f05cddf9SRui Paulo 
1253f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) pos;
1254f05cddf9SRui Paulo 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
1255f05cddf9SRui Paulo 	/* TODO: ftie->mic_control to set 2-RESPONSE */
1256f05cddf9SRui Paulo 	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
1257f05cddf9SRui Paulo 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
1258f05cddf9SRui Paulo 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
1259f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2",
1260f05cddf9SRui Paulo 		    (u8 *) ftie, sizeof(*ftie));
1261f05cddf9SRui Paulo 
1262f05cddf9SRui Paulo 	pos = (u8 *) (ftie + 1);
1263f05cddf9SRui Paulo 
1264f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1265f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
1266f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
1267f05cddf9SRui Paulo 			   "FTIE");
1268f05cddf9SRui Paulo 		ftie->ie_len += 170;
1269f05cddf9SRui Paulo 		*pos++ = 255; /* FTIE subelem */
1270f05cddf9SRui Paulo 		*pos++ = 168; /* FTIE subelem length */
1271f05cddf9SRui Paulo 		pos += 168;
1272f05cddf9SRui Paulo 	}
1273f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1274f05cddf9SRui Paulo 
1275f05cddf9SRui Paulo 	/* Lifetime */
1276f05cddf9SRui Paulo 	lifetime = peer->lifetime;
1277f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1278f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) {
1279f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
1280f05cddf9SRui Paulo 			   "lifetime in response");
1281f05cddf9SRui Paulo 		lifetime++;
1282f05cddf9SRui Paulo 	}
1283f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1284f05cddf9SRui Paulo 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
1285f05cddf9SRui Paulo 				     sizeof(timeoutie), lifetime);
1286f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator",
1287f05cddf9SRui Paulo 		   lifetime);
1288f05cddf9SRui Paulo 
1289f05cddf9SRui Paulo 	/* compute MIC before sending */
1290f05cddf9SRui Paulo 	wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
1291f05cddf9SRui Paulo 			  (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
1292*5b9c547cSRui Paulo #ifdef CONFIG_TDLS_TESTING
1293*5b9c547cSRui Paulo 	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
1294*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
1295*5b9c547cSRui Paulo 		ftie->mic[0] ^= 0x01;
1296*5b9c547cSRui Paulo 	}
1297*5b9c547cSRui Paulo #endif /* CONFIG_TDLS_TESTING */
1298f05cddf9SRui Paulo 
1299f05cddf9SRui Paulo skip_ies:
1300*5b9c547cSRui Paulo 	status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
1301*5b9c547cSRui Paulo 				   dtoken, 0, 0, peer->initiator, rbuf,
1302*5b9c547cSRui Paulo 				   pos - rbuf);
1303f05cddf9SRui Paulo 	os_free(rbuf);
1304f05cddf9SRui Paulo 
1305*5b9c547cSRui Paulo 	return status;
1306f05cddf9SRui Paulo }
1307f05cddf9SRui Paulo 
1308f05cddf9SRui Paulo 
1309f05cddf9SRui Paulo static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm,
1310f05cddf9SRui Paulo 				const unsigned char *src_addr, u8 dtoken,
1311f05cddf9SRui Paulo 				struct wpa_tdls_lnkid *lnkid,
1312f05cddf9SRui Paulo 				const struct wpa_tdls_peer *peer)
1313f05cddf9SRui Paulo {
1314f05cddf9SRui Paulo 	u8 *rbuf, *pos;
1315f05cddf9SRui Paulo 	size_t buf_len;
1316f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
1317f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie timeoutie;
1318f05cddf9SRui Paulo 	u32 lifetime;
1319*5b9c547cSRui Paulo 	int status;
1320*5b9c547cSRui Paulo 	u32 peer_capab = 0;
1321f05cddf9SRui Paulo 
1322f05cddf9SRui Paulo 	buf_len = 0;
1323f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm)) {
1324f05cddf9SRui Paulo 		/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
1325f05cddf9SRui Paulo 		 * Lifetime */
1326f05cddf9SRui Paulo 		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
1327f05cddf9SRui Paulo 			sizeof(struct wpa_tdls_timeoutie);
1328f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1329f05cddf9SRui Paulo 		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
1330f05cddf9SRui Paulo 			buf_len += 170;
1331f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1332f05cddf9SRui Paulo 	}
1333f05cddf9SRui Paulo 
1334f05cddf9SRui Paulo 	rbuf = os_zalloc(buf_len + 1);
1335f05cddf9SRui Paulo 	if (rbuf == NULL)
1336f05cddf9SRui Paulo 		return -1;
1337f05cddf9SRui Paulo 	pos = rbuf;
1338f05cddf9SRui Paulo 
1339f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm))
1340f05cddf9SRui Paulo 		goto skip_ies;
1341f05cddf9SRui Paulo 
1342f05cddf9SRui Paulo 	/* Peer RSN IE */
1343f05cddf9SRui Paulo 	pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
1344f05cddf9SRui Paulo 
1345f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) pos;
1346f05cddf9SRui Paulo 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
1347f05cddf9SRui Paulo 	/*TODO: ftie->mic_control to set 3-CONFIRM */
1348f05cddf9SRui Paulo 	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
1349f05cddf9SRui Paulo 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
1350f05cddf9SRui Paulo 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
1351f05cddf9SRui Paulo 
1352f05cddf9SRui Paulo 	pos = (u8 *) (ftie + 1);
1353f05cddf9SRui Paulo 
1354f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1355f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
1356f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
1357f05cddf9SRui Paulo 			   "FTIE");
1358f05cddf9SRui Paulo 		ftie->ie_len += 170;
1359f05cddf9SRui Paulo 		*pos++ = 255; /* FTIE subelem */
1360f05cddf9SRui Paulo 		*pos++ = 168; /* FTIE subelem length */
1361f05cddf9SRui Paulo 		pos += 168;
1362f05cddf9SRui Paulo 	}
1363f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1364f05cddf9SRui Paulo 
1365f05cddf9SRui Paulo 	/* Lifetime */
1366f05cddf9SRui Paulo 	lifetime = peer->lifetime;
1367f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1368f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) {
1369f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
1370f05cddf9SRui Paulo 			   "lifetime in confirm");
1371f05cddf9SRui Paulo 		lifetime++;
1372f05cddf9SRui Paulo 	}
1373f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1374f05cddf9SRui Paulo 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
1375f05cddf9SRui Paulo 				     sizeof(timeoutie), lifetime);
1376f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds",
1377f05cddf9SRui Paulo 		   lifetime);
1378f05cddf9SRui Paulo 
1379f05cddf9SRui Paulo 	/* compute MIC before sending */
1380f05cddf9SRui Paulo 	wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
1381f05cddf9SRui Paulo 			  (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
1382*5b9c547cSRui Paulo #ifdef CONFIG_TDLS_TESTING
1383*5b9c547cSRui Paulo 	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
1384*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
1385*5b9c547cSRui Paulo 		ftie->mic[0] ^= 0x01;
1386*5b9c547cSRui Paulo 	}
1387*5b9c547cSRui Paulo #endif /* CONFIG_TDLS_TESTING */
1388f05cddf9SRui Paulo 
1389f05cddf9SRui Paulo skip_ies:
1390*5b9c547cSRui Paulo 
1391*5b9c547cSRui Paulo 	if (peer->vht_capabilities)
1392*5b9c547cSRui Paulo 		peer_capab |= TDLS_PEER_VHT;
1393*5b9c547cSRui Paulo 	if (peer->ht_capabilities)
1394*5b9c547cSRui Paulo 		peer_capab |= TDLS_PEER_HT;
1395*5b9c547cSRui Paulo 	if (peer->wmm_capable)
1396*5b9c547cSRui Paulo 		peer_capab |= TDLS_PEER_WMM;
1397*5b9c547cSRui Paulo 
1398*5b9c547cSRui Paulo 	status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
1399*5b9c547cSRui Paulo 				   dtoken, 0, peer_capab, peer->initiator,
1400f05cddf9SRui Paulo 				   rbuf, pos - rbuf);
1401f05cddf9SRui Paulo 	os_free(rbuf);
1402f05cddf9SRui Paulo 
1403*5b9c547cSRui Paulo 	return status;
1404f05cddf9SRui Paulo }
1405f05cddf9SRui Paulo 
1406f05cddf9SRui Paulo 
1407f05cddf9SRui Paulo static int wpa_tdls_send_discovery_response(struct wpa_sm *sm,
1408f05cddf9SRui Paulo 					    struct wpa_tdls_peer *peer,
1409f05cddf9SRui Paulo 					    u8 dialog_token)
1410f05cddf9SRui Paulo {
1411*5b9c547cSRui Paulo 	size_t buf_len = 0;
1412*5b9c547cSRui Paulo 	struct wpa_tdls_timeoutie timeoutie;
1413*5b9c547cSRui Paulo 	u16 rsn_capab;
1414*5b9c547cSRui Paulo 	u8 *rbuf, *pos, *count_pos;
1415*5b9c547cSRui Paulo 	u16 count;
1416*5b9c547cSRui Paulo 	struct rsn_ie_hdr *hdr;
1417*5b9c547cSRui Paulo 	int status;
1418*5b9c547cSRui Paulo 
1419f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response "
1420f05cddf9SRui Paulo 		   "(peer " MACSTR ")", MAC2STR(peer->addr));
1421*5b9c547cSRui Paulo 	if (!wpa_tdls_get_privacy(sm))
1422*5b9c547cSRui Paulo 		goto skip_rsn_ies;
1423f05cddf9SRui Paulo 
1424*5b9c547cSRui Paulo 	/* Filling RSN IE */
1425*5b9c547cSRui Paulo 	hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
1426*5b9c547cSRui Paulo 	hdr->elem_id = WLAN_EID_RSN;
1427*5b9c547cSRui Paulo 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
1428*5b9c547cSRui Paulo 	pos = (u8 *) (hdr + 1);
1429*5b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
1430*5b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
1431*5b9c547cSRui Paulo 	count_pos = pos;
1432*5b9c547cSRui Paulo 	pos += 2;
1433*5b9c547cSRui Paulo 	count = 0;
1434*5b9c547cSRui Paulo 
1435*5b9c547cSRui Paulo 	/*
1436*5b9c547cSRui Paulo 	* AES-CCMP is the default encryption preferred for TDLS, so
1437*5b9c547cSRui Paulo 	* RSN IE is filled only with CCMP cipher suite.
1438*5b9c547cSRui Paulo 	* Note: TKIP is not used to encrypt TDLS link.
1439*5b9c547cSRui Paulo 	*
1440*5b9c547cSRui Paulo 	* Regardless of the cipher used on the AP connection, select CCMP
1441*5b9c547cSRui Paulo 	* here.
1442*5b9c547cSRui Paulo 	*/
1443*5b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
1444*5b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
1445*5b9c547cSRui Paulo 	count++;
1446*5b9c547cSRui Paulo 	WPA_PUT_LE16(count_pos, count);
1447*5b9c547cSRui Paulo 	WPA_PUT_LE16(pos, 1);
1448*5b9c547cSRui Paulo 	pos += 2;
1449*5b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
1450*5b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
1451*5b9c547cSRui Paulo 
1452*5b9c547cSRui Paulo 	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
1453*5b9c547cSRui Paulo 	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
1454*5b9c547cSRui Paulo 	WPA_PUT_LE16(pos, rsn_capab);
1455*5b9c547cSRui Paulo 	pos += 2;
1456*5b9c547cSRui Paulo 	hdr->len = (pos - (u8 *) hdr) - 2;
1457*5b9c547cSRui Paulo 	peer->rsnie_i_len = pos - peer->rsnie_i;
1458*5b9c547cSRui Paulo 
1459*5b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response",
1460*5b9c547cSRui Paulo 		    (u8 *) hdr, hdr->len + 2);
1461*5b9c547cSRui Paulo skip_rsn_ies:
1462*5b9c547cSRui Paulo 	buf_len = 0;
1463*5b9c547cSRui Paulo 	if (wpa_tdls_get_privacy(sm)) {
1464*5b9c547cSRui Paulo 		/* Peer RSN IE, Lifetime */
1465*5b9c547cSRui Paulo 		buf_len += peer->rsnie_i_len +
1466*5b9c547cSRui Paulo 			sizeof(struct wpa_tdls_timeoutie);
1467*5b9c547cSRui Paulo 	}
1468*5b9c547cSRui Paulo 	rbuf = os_zalloc(buf_len + 1);
1469*5b9c547cSRui Paulo 	if (rbuf == NULL) {
1470*5b9c547cSRui Paulo 		wpa_tdls_peer_free(sm, peer);
1471*5b9c547cSRui Paulo 		return -1;
1472*5b9c547cSRui Paulo 	}
1473*5b9c547cSRui Paulo 	pos = rbuf;
1474*5b9c547cSRui Paulo 
1475*5b9c547cSRui Paulo 	if (!wpa_tdls_get_privacy(sm))
1476*5b9c547cSRui Paulo 		goto skip_ies;
1477*5b9c547cSRui Paulo 	/* Initiator RSN IE */
1478*5b9c547cSRui Paulo 	pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
1479*5b9c547cSRui Paulo 	/* Lifetime */
1480*5b9c547cSRui Paulo 	peer->lifetime = TPK_LIFETIME;
1481*5b9c547cSRui Paulo 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
1482*5b9c547cSRui Paulo 				     sizeof(timeoutie), peer->lifetime);
1483*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
1484*5b9c547cSRui Paulo skip_ies:
1485*5b9c547cSRui Paulo 	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
1486*5b9c547cSRui Paulo 				   dialog_token, 0, 0, 0, rbuf, pos - rbuf);
1487*5b9c547cSRui Paulo 	os_free(rbuf);
1488*5b9c547cSRui Paulo 
1489*5b9c547cSRui Paulo 	return status;
1490f05cddf9SRui Paulo }
1491f05cddf9SRui Paulo 
1492f05cddf9SRui Paulo 
1493f05cddf9SRui Paulo static int
1494f05cddf9SRui Paulo wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
1495f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
1496f05cddf9SRui Paulo {
1497f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
1498f05cddf9SRui Paulo 	const struct wpa_tdls_lnkid *lnkid;
1499f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
1500f05cddf9SRui Paulo 	size_t min_req_len = sizeof(struct wpa_tdls_frame) +
1501f05cddf9SRui Paulo 		1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid);
1502f05cddf9SRui Paulo 	u8 dialog_token;
1503f05cddf9SRui Paulo 
1504f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR,
1505f05cddf9SRui Paulo 		   MAC2STR(addr));
1506f05cddf9SRui Paulo 
1507f05cddf9SRui Paulo 	if (len < min_req_len) {
1508f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: "
1509f05cddf9SRui Paulo 			   "%d", (int) len);
1510f05cddf9SRui Paulo 		return -1;
1511f05cddf9SRui Paulo 	}
1512f05cddf9SRui Paulo 
1513f05cddf9SRui Paulo 	dialog_token = buf[sizeof(struct wpa_tdls_frame)];
1514f05cddf9SRui Paulo 
1515*5b9c547cSRui Paulo 	/*
1516*5b9c547cSRui Paulo 	 * Some APs will tack on a weird IE to the end of a TDLS
1517*5b9c547cSRui Paulo 	 * discovery request packet. This needn't fail the response,
1518*5b9c547cSRui Paulo 	 * since the required IE are verified separately.
1519*5b9c547cSRui Paulo 	 */
1520f05cddf9SRui Paulo 	if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
1521f05cddf9SRui Paulo 				     len - (sizeof(struct wpa_tdls_frame) + 1),
1522*5b9c547cSRui Paulo 				     &kde) < 0) {
1523*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
1524*5b9c547cSRui Paulo 			   "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround");
1525*5b9c547cSRui Paulo 	}
1526f05cddf9SRui Paulo 
1527f05cddf9SRui Paulo 	if (!kde.lnkid) {
1528f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
1529f05cddf9SRui Paulo 			   "Request");
1530f05cddf9SRui Paulo 		return -1;
1531f05cddf9SRui Paulo 	}
1532f05cddf9SRui Paulo 
1533f05cddf9SRui Paulo 	lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid;
1534f05cddf9SRui Paulo 
1535f05cddf9SRui Paulo 	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
1536f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different "
1537f05cddf9SRui Paulo 			   " BSS " MACSTR, MAC2STR(lnkid->bssid));
1538f05cddf9SRui Paulo 		return -1;
1539f05cddf9SRui Paulo 	}
1540f05cddf9SRui Paulo 
1541*5b9c547cSRui Paulo 	peer = wpa_tdls_add_peer(sm, addr, NULL);
1542f05cddf9SRui Paulo 	if (peer == NULL)
1543f05cddf9SRui Paulo 		return -1;
1544f05cddf9SRui Paulo 
1545f05cddf9SRui Paulo 	return wpa_tdls_send_discovery_response(sm, peer, dialog_token);
1546f05cddf9SRui Paulo }
1547f05cddf9SRui Paulo 
1548f05cddf9SRui Paulo 
1549f05cddf9SRui Paulo int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr)
1550f05cddf9SRui Paulo {
1551f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
1552f05cddf9SRui Paulo 		return -1;
1553f05cddf9SRui Paulo 
1554f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer "
1555f05cddf9SRui Paulo 		   MACSTR, MAC2STR(addr));
1556f05cddf9SRui Paulo 	return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST,
1557*5b9c547cSRui Paulo 				 1, 0, 0, 1, NULL, 0);
1558f05cddf9SRui Paulo }
1559f05cddf9SRui Paulo 
1560f05cddf9SRui Paulo 
1561f05cddf9SRui Paulo static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde,
1562f05cddf9SRui Paulo 			   struct wpa_tdls_peer *peer)
1563f05cddf9SRui Paulo {
1564f05cddf9SRui Paulo 	if (!kde->supp_rates) {
1565f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
1566f05cddf9SRui Paulo 		return -1;
1567f05cddf9SRui Paulo 	}
1568*5b9c547cSRui Paulo 	peer->supp_rates_len = merge_byte_arrays(
1569*5b9c547cSRui Paulo 		peer->supp_rates, sizeof(peer->supp_rates),
1570*5b9c547cSRui Paulo 		kde->supp_rates + 2, kde->supp_rates_len - 2,
1571*5b9c547cSRui Paulo 		kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL,
1572*5b9c547cSRui Paulo 		kde->ext_supp_rates_len - 2);
1573*5b9c547cSRui Paulo 	return 0;
1574f05cddf9SRui Paulo }
1575f05cddf9SRui Paulo 
1576*5b9c547cSRui Paulo 
1577*5b9c547cSRui Paulo static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde,
1578*5b9c547cSRui Paulo 			      struct wpa_tdls_peer *peer)
1579*5b9c547cSRui Paulo {
1580*5b9c547cSRui Paulo 	if (!kde->ht_capabilities ||
1581*5b9c547cSRui Paulo 	    kde->ht_capabilities_len <
1582*5b9c547cSRui Paulo 	    sizeof(struct ieee80211_ht_capabilities) ) {
1583*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities "
1584*5b9c547cSRui Paulo 			   "received");
1585f05cddf9SRui Paulo 		return 0;
1586f05cddf9SRui Paulo 	}
1587f05cddf9SRui Paulo 
1588*5b9c547cSRui Paulo 	if (!peer->ht_capabilities) {
1589*5b9c547cSRui Paulo 		peer->ht_capabilities =
1590*5b9c547cSRui Paulo                         os_zalloc(sizeof(struct ieee80211_ht_capabilities));
1591*5b9c547cSRui Paulo 		if (peer->ht_capabilities == NULL)
1592*5b9c547cSRui Paulo                         return -1;
1593*5b9c547cSRui Paulo 	}
1594*5b9c547cSRui Paulo 
1595*5b9c547cSRui Paulo 	os_memcpy(peer->ht_capabilities, kde->ht_capabilities,
1596*5b9c547cSRui Paulo                   sizeof(struct ieee80211_ht_capabilities));
1597*5b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities",
1598*5b9c547cSRui Paulo 		    (u8 *) peer->ht_capabilities,
1599*5b9c547cSRui Paulo 		    sizeof(struct ieee80211_ht_capabilities));
1600*5b9c547cSRui Paulo 
1601*5b9c547cSRui Paulo 	return 0;
1602*5b9c547cSRui Paulo }
1603*5b9c547cSRui Paulo 
1604*5b9c547cSRui Paulo 
1605*5b9c547cSRui Paulo static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde,
1606*5b9c547cSRui Paulo 			      struct wpa_tdls_peer *peer)
1607*5b9c547cSRui Paulo {
1608*5b9c547cSRui Paulo 	if (!kde->vht_capabilities ||
1609*5b9c547cSRui Paulo 	    kde->vht_capabilities_len <
1610*5b9c547cSRui Paulo 	    sizeof(struct ieee80211_vht_capabilities) ) {
1611*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities "
1612*5b9c547cSRui Paulo 			   "received");
1613*5b9c547cSRui Paulo 		return 0;
1614*5b9c547cSRui Paulo 	}
1615*5b9c547cSRui Paulo 
1616*5b9c547cSRui Paulo 	if (!peer->vht_capabilities) {
1617*5b9c547cSRui Paulo 		peer->vht_capabilities =
1618*5b9c547cSRui Paulo                         os_zalloc(sizeof(struct ieee80211_vht_capabilities));
1619*5b9c547cSRui Paulo 		if (peer->vht_capabilities == NULL)
1620*5b9c547cSRui Paulo                         return -1;
1621*5b9c547cSRui Paulo 	}
1622*5b9c547cSRui Paulo 
1623*5b9c547cSRui Paulo 	os_memcpy(peer->vht_capabilities, kde->vht_capabilities,
1624*5b9c547cSRui Paulo                   sizeof(struct ieee80211_vht_capabilities));
1625*5b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities",
1626*5b9c547cSRui Paulo 		    (u8 *) peer->vht_capabilities,
1627*5b9c547cSRui Paulo 		    sizeof(struct ieee80211_vht_capabilities));
1628*5b9c547cSRui Paulo 
1629*5b9c547cSRui Paulo 	return 0;
1630*5b9c547cSRui Paulo }
1631*5b9c547cSRui Paulo 
1632*5b9c547cSRui Paulo 
1633*5b9c547cSRui Paulo static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde,
1634*5b9c547cSRui Paulo 			       struct wpa_tdls_peer *peer)
1635*5b9c547cSRui Paulo {
1636*5b9c547cSRui Paulo 	if (!kde->ext_capab) {
1637*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities "
1638*5b9c547cSRui Paulo 			   "received");
1639*5b9c547cSRui Paulo 		return 0;
1640*5b9c547cSRui Paulo 	}
1641*5b9c547cSRui Paulo 
1642*5b9c547cSRui Paulo 	if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) {
1643*5b9c547cSRui Paulo 		/* Need to allocate buffer to fit the new information */
1644*5b9c547cSRui Paulo 		os_free(peer->ext_capab);
1645*5b9c547cSRui Paulo 		peer->ext_capab = os_zalloc(kde->ext_capab_len - 2);
1646*5b9c547cSRui Paulo 		if (peer->ext_capab == NULL)
1647*5b9c547cSRui Paulo 			return -1;
1648*5b9c547cSRui Paulo 	}
1649*5b9c547cSRui Paulo 
1650*5b9c547cSRui Paulo 	peer->ext_capab_len = kde->ext_capab_len - 2;
1651*5b9c547cSRui Paulo 	os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len);
1652*5b9c547cSRui Paulo 
1653*5b9c547cSRui Paulo 	return 0;
1654*5b9c547cSRui Paulo }
1655*5b9c547cSRui Paulo 
1656*5b9c547cSRui Paulo 
1657*5b9c547cSRui Paulo static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde,
1658*5b9c547cSRui Paulo 			       struct wpa_tdls_peer *peer)
1659*5b9c547cSRui Paulo {
1660*5b9c547cSRui Paulo 	struct wmm_information_element *wmm;
1661*5b9c547cSRui Paulo 
1662*5b9c547cSRui Paulo 	if (!kde->wmm) {
1663*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported WMM capabilities received");
1664*5b9c547cSRui Paulo 		return 0;
1665*5b9c547cSRui Paulo 	}
1666*5b9c547cSRui Paulo 
1667*5b9c547cSRui Paulo 	if (kde->wmm_len < sizeof(struct wmm_information_element)) {
1668*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Invalid supported WMM capabilities received");
1669*5b9c547cSRui Paulo 		return -1;
1670*5b9c547cSRui Paulo 	}
1671*5b9c547cSRui Paulo 
1672*5b9c547cSRui Paulo 	wmm = (struct wmm_information_element *) kde->wmm;
1673*5b9c547cSRui Paulo 	peer->qos_info = wmm->qos_info;
1674*5b9c547cSRui Paulo 
1675*5b9c547cSRui Paulo 	peer->wmm_capable = 1;
1676*5b9c547cSRui Paulo 
1677*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Peer WMM QOS Info 0x%x", peer->qos_info);
1678*5b9c547cSRui Paulo 	return 0;
1679*5b9c547cSRui Paulo }
1680*5b9c547cSRui Paulo 
1681*5b9c547cSRui Paulo 
1682*5b9c547cSRui Paulo static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde,
1683*5b9c547cSRui Paulo 				   struct wpa_tdls_peer *peer)
1684*5b9c547cSRui Paulo {
1685*5b9c547cSRui Paulo 	if (!kde->supp_channels) {
1686*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported channels received");
1687*5b9c547cSRui Paulo 		return 0;
1688*5b9c547cSRui Paulo 	}
1689*5b9c547cSRui Paulo 
1690*5b9c547cSRui Paulo 	if (!peer->supp_channels ||
1691*5b9c547cSRui Paulo 	    peer->supp_channels_len < kde->supp_channels_len) {
1692*5b9c547cSRui Paulo 		os_free(peer->supp_channels);
1693*5b9c547cSRui Paulo 		peer->supp_channels = os_zalloc(kde->supp_channels_len);
1694*5b9c547cSRui Paulo 		if (peer->supp_channels == NULL)
1695*5b9c547cSRui Paulo 			return -1;
1696*5b9c547cSRui Paulo 	}
1697*5b9c547cSRui Paulo 
1698*5b9c547cSRui Paulo 	peer->supp_channels_len = kde->supp_channels_len;
1699*5b9c547cSRui Paulo 
1700*5b9c547cSRui Paulo 	os_memcpy(peer->supp_channels, kde->supp_channels,
1701*5b9c547cSRui Paulo 		  peer->supp_channels_len);
1702*5b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels",
1703*5b9c547cSRui Paulo 		    (u8 *) peer->supp_channels, peer->supp_channels_len);
1704*5b9c547cSRui Paulo 	return 0;
1705*5b9c547cSRui Paulo }
1706*5b9c547cSRui Paulo 
1707*5b9c547cSRui Paulo 
1708*5b9c547cSRui Paulo static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde,
1709*5b9c547cSRui Paulo 				       struct wpa_tdls_peer *peer)
1710*5b9c547cSRui Paulo {
1711*5b9c547cSRui Paulo 	if (!kde->supp_oper_classes) {
1712*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received");
1713*5b9c547cSRui Paulo 		return 0;
1714*5b9c547cSRui Paulo 	}
1715*5b9c547cSRui Paulo 
1716*5b9c547cSRui Paulo 	if (!peer->supp_oper_classes ||
1717*5b9c547cSRui Paulo 	    peer->supp_oper_classes_len < kde->supp_oper_classes_len) {
1718*5b9c547cSRui Paulo 		os_free(peer->supp_oper_classes);
1719*5b9c547cSRui Paulo 		peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len);
1720*5b9c547cSRui Paulo 		if (peer->supp_oper_classes == NULL)
1721*5b9c547cSRui Paulo 			return -1;
1722*5b9c547cSRui Paulo 	}
1723*5b9c547cSRui Paulo 
1724*5b9c547cSRui Paulo 	peer->supp_oper_classes_len = kde->supp_oper_classes_len;
1725*5b9c547cSRui Paulo 	os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes,
1726*5b9c547cSRui Paulo 		  peer->supp_oper_classes_len);
1727*5b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes",
1728*5b9c547cSRui Paulo 		    (u8 *) peer->supp_oper_classes,
1729*5b9c547cSRui Paulo 		    peer->supp_oper_classes_len);
1730*5b9c547cSRui Paulo 	return 0;
1731*5b9c547cSRui Paulo }
1732*5b9c547cSRui Paulo 
1733*5b9c547cSRui Paulo 
1734*5b9c547cSRui Paulo static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
1735*5b9c547cSRui Paulo 				int add)
1736*5b9c547cSRui Paulo {
1737*5b9c547cSRui Paulo 	return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid,
1738*5b9c547cSRui Paulo 				       peer->capability,
1739*5b9c547cSRui Paulo 				       peer->supp_rates, peer->supp_rates_len,
1740*5b9c547cSRui Paulo 				       peer->ht_capabilities,
1741*5b9c547cSRui Paulo 				       peer->vht_capabilities,
1742*5b9c547cSRui Paulo 				       peer->qos_info, peer->wmm_capable,
1743*5b9c547cSRui Paulo 				       peer->ext_capab, peer->ext_capab_len,
1744*5b9c547cSRui Paulo 				       peer->supp_channels,
1745*5b9c547cSRui Paulo 				       peer->supp_channels_len,
1746*5b9c547cSRui Paulo 				       peer->supp_oper_classes,
1747*5b9c547cSRui Paulo 				       peer->supp_oper_classes_len);
1748*5b9c547cSRui Paulo }
1749*5b9c547cSRui Paulo 
1750f05cddf9SRui Paulo 
1751f05cddf9SRui Paulo static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
1752f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
1753f05cddf9SRui Paulo {
1754f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
1755f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
1756f05cddf9SRui Paulo 	struct wpa_ie_data ie;
1757f05cddf9SRui Paulo 	int cipher;
1758f05cddf9SRui Paulo 	const u8 *cpos;
1759f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie = NULL;
1760f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie *timeoutie;
1761f05cddf9SRui Paulo 	struct wpa_tdls_lnkid *lnkid;
1762f05cddf9SRui Paulo 	u32 lifetime = 0;
1763f05cddf9SRui Paulo #if 0
1764f05cddf9SRui Paulo 	struct rsn_ie_hdr *hdr;
1765f05cddf9SRui Paulo 	u8 *pos;
1766f05cddf9SRui Paulo 	u16 rsn_capab;
1767f05cddf9SRui Paulo 	u16 rsn_ver;
1768f05cddf9SRui Paulo #endif
1769f05cddf9SRui Paulo 	u8 dtoken;
1770f05cddf9SRui Paulo 	u16 ielen;
1771f05cddf9SRui Paulo 	u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
1772f05cddf9SRui Paulo 	int tdls_prohibited = sm->tdls_prohibited;
1773f05cddf9SRui Paulo 	int existing_peer = 0;
1774f05cddf9SRui Paulo 
1775f05cddf9SRui Paulo 	if (len < 3 + 3)
1776f05cddf9SRui Paulo 		return -1;
1777f05cddf9SRui Paulo 
1778f05cddf9SRui Paulo 	cpos = buf;
1779f05cddf9SRui Paulo 	cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
1780f05cddf9SRui Paulo 
1781f05cddf9SRui Paulo 	/* driver had already verified the frame format */
1782f05cddf9SRui Paulo 	dtoken = *cpos++; /* dialog token */
1783f05cddf9SRui Paulo 
1784f05cddf9SRui Paulo 	wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken);
1785f05cddf9SRui Paulo 
1786*5b9c547cSRui Paulo 	peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer);
1787f05cddf9SRui Paulo 	if (peer == NULL)
1788f05cddf9SRui Paulo 		goto error;
1789*5b9c547cSRui Paulo 
1790*5b9c547cSRui Paulo 	/* If found, use existing entry instead of adding a new one;
1791*5b9c547cSRui Paulo 	 * how to handle the case where both ends initiate at the
1792*5b9c547cSRui Paulo 	 * same time? */
1793*5b9c547cSRui Paulo 	if (existing_peer) {
1794*5b9c547cSRui Paulo 		if (peer->tpk_success) {
1795*5b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
1796*5b9c547cSRui Paulo 				   "direct link is enabled - tear down the "
1797*5b9c547cSRui Paulo 				   "old link first");
1798*5b9c547cSRui Paulo 			wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
1799*5b9c547cSRui Paulo 			wpa_tdls_peer_clear(sm, peer);
1800*5b9c547cSRui Paulo 		} else if (peer->initiator) {
1801*5b9c547cSRui Paulo 			/*
1802*5b9c547cSRui Paulo 			 * An entry is already present, so check if we already
1803*5b9c547cSRui Paulo 			 * sent a TDLS Setup Request. If so, compare MAC
1804*5b9c547cSRui Paulo 			 * addresses and let the STA with the lower MAC address
1805*5b9c547cSRui Paulo 			 * continue as the initiator. The other negotiation is
1806*5b9c547cSRui Paulo 			 * terminated.
1807*5b9c547cSRui Paulo 			 */
1808*5b9c547cSRui Paulo 			if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
1809*5b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "TDLS: Discard request "
1810*5b9c547cSRui Paulo 					   "from peer with higher address "
1811*5b9c547cSRui Paulo 					   MACSTR, MAC2STR(src_addr));
1812*5b9c547cSRui Paulo 				return -1;
1813*5b9c547cSRui Paulo 			} else {
1814*5b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "TDLS: Accept request "
1815*5b9c547cSRui Paulo 					   "from peer with lower address "
1816*5b9c547cSRui Paulo 					   MACSTR " (terminate previously "
1817*5b9c547cSRui Paulo 					   "initiated negotiation",
1818*5b9c547cSRui Paulo 					   MAC2STR(src_addr));
1819*5b9c547cSRui Paulo 				wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
1820*5b9c547cSRui Paulo 						 peer->addr);
1821*5b9c547cSRui Paulo 				wpa_tdls_peer_clear(sm, peer);
1822*5b9c547cSRui Paulo 			}
1823*5b9c547cSRui Paulo 		}
1824f05cddf9SRui Paulo 	}
1825f05cddf9SRui Paulo 
1826f05cddf9SRui Paulo 	/* capability information */
1827f05cddf9SRui Paulo 	peer->capability = WPA_GET_LE16(cpos);
1828f05cddf9SRui Paulo 	cpos += 2;
1829f05cddf9SRui Paulo 
1830f05cddf9SRui Paulo 	ielen = len - (cpos - buf); /* start of IE in buf */
1831*5b9c547cSRui Paulo 
1832*5b9c547cSRui Paulo 	/*
1833*5b9c547cSRui Paulo 	 * Don't reject the message if failing to parse IEs. The IEs we need are
1834*5b9c547cSRui Paulo 	 * explicitly checked below. Some APs may add arbitrary padding to the
1835*5b9c547cSRui Paulo 	 * end of short TDLS frames and that would look like invalid IEs.
1836*5b9c547cSRui Paulo 	 */
1837*5b9c547cSRui Paulo 	if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0)
1838*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
1839*5b9c547cSRui Paulo 			   "TDLS: Failed to parse IEs in TPK M1 - ignore as an interop workaround");
1840f05cddf9SRui Paulo 
1841f05cddf9SRui Paulo 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
1842f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
1843f05cddf9SRui Paulo 			   "TPK M1");
1844f05cddf9SRui Paulo 		goto error;
1845f05cddf9SRui Paulo 	}
1846f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1",
1847f05cddf9SRui Paulo 		    kde.lnkid, kde.lnkid_len);
1848f05cddf9SRui Paulo 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
1849f05cddf9SRui Paulo 	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
1850f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS");
1851*5b9c547cSRui Paulo 		status = WLAN_STATUS_REQUEST_DECLINED;
1852f05cddf9SRui Paulo 		goto error;
1853f05cddf9SRui Paulo 	}
1854f05cddf9SRui Paulo 
1855f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
1856f05cddf9SRui Paulo 		   MAC2STR(src_addr));
1857f05cddf9SRui Paulo 
1858f05cddf9SRui Paulo 	if (copy_supp_rates(&kde, peer) < 0)
1859f05cddf9SRui Paulo 		goto error;
1860f05cddf9SRui Paulo 
1861*5b9c547cSRui Paulo 	if (copy_peer_ht_capab(&kde, peer) < 0)
1862*5b9c547cSRui Paulo 		goto error;
1863*5b9c547cSRui Paulo 
1864*5b9c547cSRui Paulo 	if (copy_peer_vht_capab(&kde, peer) < 0)
1865*5b9c547cSRui Paulo 		goto error;
1866*5b9c547cSRui Paulo 
1867*5b9c547cSRui Paulo 	if (copy_peer_ext_capab(&kde, peer) < 0)
1868*5b9c547cSRui Paulo 		goto error;
1869*5b9c547cSRui Paulo 
1870*5b9c547cSRui Paulo 	if (copy_peer_supp_channels(&kde, peer) < 0)
1871*5b9c547cSRui Paulo 		goto error;
1872*5b9c547cSRui Paulo 
1873*5b9c547cSRui Paulo 	if (copy_peer_supp_oper_classes(&kde, peer) < 0)
1874*5b9c547cSRui Paulo 		goto error;
1875*5b9c547cSRui Paulo 
1876*5b9c547cSRui Paulo 	peer->qos_info = kde.qosinfo;
1877*5b9c547cSRui Paulo 
1878*5b9c547cSRui Paulo 	/* Overwrite with the qos_info obtained in WMM IE */
1879*5b9c547cSRui Paulo 	if (copy_peer_wmm_capab(&kde, peer) < 0)
1880*5b9c547cSRui Paulo 		goto error;
1881*5b9c547cSRui Paulo 
1882*5b9c547cSRui Paulo 	peer->aid = kde.aid;
1883*5b9c547cSRui Paulo 
1884f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1885f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
1886*5b9c547cSRui Paulo 		peer = wpa_tdls_add_peer(sm, src_addr, NULL);
1887f05cddf9SRui Paulo 		if (peer == NULL)
1888f05cddf9SRui Paulo 			goto error;
1889f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of "
1890f05cddf9SRui Paulo 			   "TDLS setup - send own request");
1891f05cddf9SRui Paulo 		peer->initiator = 1;
1892*5b9c547cSRui Paulo 		wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
1893*5b9c547cSRui Paulo 					NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0);
1894f05cddf9SRui Paulo 		wpa_tdls_send_tpk_m1(sm, peer);
1895f05cddf9SRui Paulo 	}
1896f05cddf9SRui Paulo 
1897f05cddf9SRui Paulo 	if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
1898f05cddf9SRui Paulo 	    tdls_prohibited) {
1899f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
1900f05cddf9SRui Paulo 			   "on TDLS");
1901f05cddf9SRui Paulo 		tdls_prohibited = 0;
1902f05cddf9SRui Paulo 	}
1903f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1904f05cddf9SRui Paulo 
1905f05cddf9SRui Paulo 	if (tdls_prohibited) {
1906f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS");
1907f05cddf9SRui Paulo 		status = WLAN_STATUS_REQUEST_DECLINED;
1908f05cddf9SRui Paulo 		goto error;
1909f05cddf9SRui Paulo 	}
1910f05cddf9SRui Paulo 
1911f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm)) {
1912f05cddf9SRui Paulo 		if (kde.rsn_ie) {
1913f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while "
1914f05cddf9SRui Paulo 				   "security is disabled");
1915f05cddf9SRui Paulo 			status = WLAN_STATUS_SECURITY_DISABLED;
1916f05cddf9SRui Paulo 			goto error;
1917f05cddf9SRui Paulo 		}
1918f05cddf9SRui Paulo 		goto skip_rsn;
1919f05cddf9SRui Paulo 	}
1920f05cddf9SRui Paulo 
1921f05cddf9SRui Paulo 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
1922f05cddf9SRui Paulo 	    kde.rsn_ie == NULL) {
1923f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1");
1924f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_PARAMETERS;
1925f05cddf9SRui Paulo 		goto error;
1926f05cddf9SRui Paulo 	}
1927f05cddf9SRui Paulo 
1928f05cddf9SRui Paulo 	if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
1929f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in "
1930f05cddf9SRui Paulo 			   "TPK M1");
1931f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
1932f05cddf9SRui Paulo 		goto error;
1933f05cddf9SRui Paulo 	}
1934f05cddf9SRui Paulo 
1935f05cddf9SRui Paulo 	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
1936f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1");
1937f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
1938f05cddf9SRui Paulo 		goto error;
1939f05cddf9SRui Paulo 	}
1940f05cddf9SRui Paulo 
1941f05cddf9SRui Paulo 	cipher = ie.pairwise_cipher;
1942f05cddf9SRui Paulo 	if (cipher & WPA_CIPHER_CCMP) {
1943f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
1944f05cddf9SRui Paulo 		cipher = WPA_CIPHER_CCMP;
1945f05cddf9SRui Paulo 	} else {
1946f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1");
1947f05cddf9SRui Paulo 		status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
1948f05cddf9SRui Paulo 		goto error;
1949f05cddf9SRui Paulo 	}
1950f05cddf9SRui Paulo 
1951f05cddf9SRui Paulo 	if ((ie.capabilities &
1952f05cddf9SRui Paulo 	     (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) !=
1953f05cddf9SRui Paulo 	    WPA_CAPABILITY_PEERKEY_ENABLED) {
1954f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in "
1955f05cddf9SRui Paulo 			   "TPK M1");
1956f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSN_IE_CAPAB;
1957f05cddf9SRui Paulo 		goto error;
1958f05cddf9SRui Paulo 	}
1959f05cddf9SRui Paulo 
1960f05cddf9SRui Paulo 	/* Lifetime */
1961f05cddf9SRui Paulo 	if (kde.key_lifetime == NULL) {
1962f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1");
1963f05cddf9SRui Paulo 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
1964f05cddf9SRui Paulo 		goto error;
1965f05cddf9SRui Paulo 	}
1966f05cddf9SRui Paulo 	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
1967f05cddf9SRui Paulo 	lifetime = WPA_GET_LE32(timeoutie->value);
1968f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime);
1969f05cddf9SRui Paulo 	if (lifetime < 300) {
1970f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime");
1971f05cddf9SRui Paulo 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
1972f05cddf9SRui Paulo 		goto error;
1973f05cddf9SRui Paulo 	}
1974f05cddf9SRui Paulo 
1975f05cddf9SRui Paulo skip_rsn:
1976f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1977f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
1978f05cddf9SRui Paulo 		if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) {
1979f05cddf9SRui Paulo 			/*
1980f05cddf9SRui Paulo 			 * The request frame from us is going to win, so do not
1981f05cddf9SRui Paulo 			 * replace information based on this request frame from
1982f05cddf9SRui Paulo 			 * the peer.
1983f05cddf9SRui Paulo 			 */
1984f05cddf9SRui Paulo 			goto skip_rsn_check;
1985f05cddf9SRui Paulo 		}
1986f05cddf9SRui Paulo 	}
1987f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1988f05cddf9SRui Paulo 
1989f05cddf9SRui Paulo 	peer->initiator = 0; /* Need to check */
1990f05cddf9SRui Paulo 	peer->dtoken = dtoken;
1991f05cddf9SRui Paulo 
1992f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm)) {
1993f05cddf9SRui Paulo 		peer->rsnie_i_len = 0;
1994f05cddf9SRui Paulo 		peer->rsnie_p_len = 0;
1995f05cddf9SRui Paulo 		peer->cipher = WPA_CIPHER_NONE;
1996f05cddf9SRui Paulo 		goto skip_rsn_check;
1997f05cddf9SRui Paulo 	}
1998f05cddf9SRui Paulo 
1999f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
2000f05cddf9SRui Paulo 	os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
2001f05cddf9SRui Paulo 	peer->rsnie_i_len = kde.rsn_ie_len;
2002f05cddf9SRui Paulo 	peer->cipher = cipher;
2003f05cddf9SRui Paulo 
2004*5b9c547cSRui Paulo 	if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) {
2005*5b9c547cSRui Paulo 		/*
2006*5b9c547cSRui Paulo 		 * There is no point in updating the RNonce for every obtained
2007*5b9c547cSRui Paulo 		 * TPK M1 frame (e.g., retransmission due to timeout) with the
2008*5b9c547cSRui Paulo 		 * same INonce (SNonce in FTIE). However, if the TPK M1 is
2009*5b9c547cSRui Paulo 		 * retransmitted with a different INonce, update the RNonce
2010*5b9c547cSRui Paulo 		 * since this is for a new TDLS session.
2011*5b9c547cSRui Paulo 		 */
2012*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
2013*5b9c547cSRui Paulo 			   "TDLS: New TPK M1 INonce - generate new RNonce");
2014*5b9c547cSRui Paulo 		os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN);
2015f05cddf9SRui Paulo 		if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
2016f05cddf9SRui Paulo 			wpa_msg(sm->ctx->ctx, MSG_WARNING,
2017f05cddf9SRui Paulo 				"TDLS: Failed to get random data for responder nonce");
2018f05cddf9SRui Paulo 			goto error;
2019f05cddf9SRui Paulo 		}
2020*5b9c547cSRui Paulo 	}
2021f05cddf9SRui Paulo 
2022f05cddf9SRui Paulo #if 0
2023f05cddf9SRui Paulo 	/* get version info from RSNIE received from Peer */
2024f05cddf9SRui Paulo 	hdr = (struct rsn_ie_hdr *) kde.rsn_ie;
2025f05cddf9SRui Paulo 	rsn_ver = WPA_GET_LE16(hdr->version);
2026f05cddf9SRui Paulo 
2027f05cddf9SRui Paulo 	/* use min(peer's version, out version) */
2028f05cddf9SRui Paulo 	if (rsn_ver > RSN_VERSION)
2029f05cddf9SRui Paulo 		rsn_ver = RSN_VERSION;
2030f05cddf9SRui Paulo 
2031f05cddf9SRui Paulo 	hdr = (struct rsn_ie_hdr *) peer->rsnie_p;
2032f05cddf9SRui Paulo 
2033f05cddf9SRui Paulo 	hdr->elem_id = WLAN_EID_RSN;
2034f05cddf9SRui Paulo 	WPA_PUT_LE16(hdr->version, rsn_ver);
2035f05cddf9SRui Paulo 	pos = (u8 *) (hdr + 1);
2036f05cddf9SRui Paulo 
2037f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
2038f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
2039f05cddf9SRui Paulo 	/* Include only the selected cipher in pairwise cipher suite */
2040f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, 1);
2041f05cddf9SRui Paulo 	pos += 2;
2042f05cddf9SRui Paulo 	if (cipher == WPA_CIPHER_CCMP)
2043f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
2044f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
2045f05cddf9SRui Paulo 
2046f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, 1);
2047f05cddf9SRui Paulo 	pos += 2;
2048f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
2049f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
2050f05cddf9SRui Paulo 
2051f05cddf9SRui Paulo 	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
2052f05cddf9SRui Paulo 	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
2053f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, rsn_capab);
2054f05cddf9SRui Paulo 	pos += 2;
2055f05cddf9SRui Paulo 
2056f05cddf9SRui Paulo 	hdr->len = (pos - peer->rsnie_p) - 2;
2057f05cddf9SRui Paulo 	peer->rsnie_p_len = pos - peer->rsnie_p;
2058f05cddf9SRui Paulo #endif
2059f05cddf9SRui Paulo 
2060f05cddf9SRui Paulo 	/* temp fix: validation of RSNIE later */
2061f05cddf9SRui Paulo 	os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len);
2062f05cddf9SRui Paulo 	peer->rsnie_p_len = peer->rsnie_i_len;
2063f05cddf9SRui Paulo 
2064f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
2065f05cddf9SRui Paulo 		    peer->rsnie_p, peer->rsnie_p_len);
2066f05cddf9SRui Paulo 
2067f05cddf9SRui Paulo 	peer->lifetime = lifetime;
2068f05cddf9SRui Paulo 
2069f05cddf9SRui Paulo 	wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
2070f05cddf9SRui Paulo 
2071f05cddf9SRui Paulo skip_rsn_check:
2072*5b9c547cSRui Paulo #ifdef CONFIG_TDLS_TESTING
2073*5b9c547cSRui Paulo 	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT)
2074*5b9c547cSRui Paulo 		goto skip_add_peer;
2075*5b9c547cSRui Paulo #endif /* CONFIG_TDLS_TESTING */
2076*5b9c547cSRui Paulo 
2077*5b9c547cSRui Paulo 	/* add supported rates, capabilities, and qos_info to the TDLS peer */
2078*5b9c547cSRui Paulo 	if (wpa_tdls_addset_peer(sm, peer, 1) < 0)
2079*5b9c547cSRui Paulo 		goto error;
2080*5b9c547cSRui Paulo 
2081*5b9c547cSRui Paulo #ifdef CONFIG_TDLS_TESTING
2082*5b9c547cSRui Paulo skip_add_peer:
2083*5b9c547cSRui Paulo #endif /* CONFIG_TDLS_TESTING */
2084*5b9c547cSRui Paulo 	peer->tpk_in_progress = 1;
2085f05cddf9SRui Paulo 
2086f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
2087f05cddf9SRui Paulo 	if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
2088*5b9c547cSRui Paulo 		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
2089f05cddf9SRui Paulo 		goto error;
2090f05cddf9SRui Paulo 	}
2091f05cddf9SRui Paulo 
2092f05cddf9SRui Paulo 	return 0;
2093f05cddf9SRui Paulo 
2094f05cddf9SRui Paulo error:
2095*5b9c547cSRui Paulo 	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
2096f05cddf9SRui Paulo 			    status);
2097*5b9c547cSRui Paulo 	if (peer)
2098*5b9c547cSRui Paulo 		wpa_tdls_peer_free(sm, peer);
2099f05cddf9SRui Paulo 	return -1;
2100f05cddf9SRui Paulo }
2101f05cddf9SRui Paulo 
2102f05cddf9SRui Paulo 
2103*5b9c547cSRui Paulo static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
2104f05cddf9SRui Paulo {
2105f05cddf9SRui Paulo 	peer->tpk_success = 1;
2106*5b9c547cSRui Paulo 	peer->tpk_in_progress = 0;
2107f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
2108f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm)) {
2109f05cddf9SRui Paulo 		u32 lifetime = peer->lifetime;
2110f05cddf9SRui Paulo 		/*
2111f05cddf9SRui Paulo 		 * Start the initiator process a bit earlier to avoid race
2112f05cddf9SRui Paulo 		 * condition with the responder sending teardown request.
2113f05cddf9SRui Paulo 		 */
2114f05cddf9SRui Paulo 		if (lifetime > 3 && peer->initiator)
2115f05cddf9SRui Paulo 			lifetime -= 3;
2116f05cddf9SRui Paulo 		eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout,
2117f05cddf9SRui Paulo 				       sm, peer);
2118f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
2119f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) {
2120f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK "
2121f05cddf9SRui Paulo 			   "expiration");
2122f05cddf9SRui Paulo 		eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
2123f05cddf9SRui Paulo 	}
2124f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
2125f05cddf9SRui Paulo 	}
2126f05cddf9SRui Paulo 
2127*5b9c547cSRui Paulo 	if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) {
2128*5b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Could not configure key to the "
2129*5b9c547cSRui Paulo 			   "driver");
2130*5b9c547cSRui Paulo 		return -1;
2131*5b9c547cSRui Paulo 	}
2132*5b9c547cSRui Paulo 	peer->reconfig_key = 0;
2133f05cddf9SRui Paulo 
2134*5b9c547cSRui Paulo 	return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr);
2135f05cddf9SRui Paulo }
2136f05cddf9SRui Paulo 
2137f05cddf9SRui Paulo 
2138f05cddf9SRui Paulo static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
2139f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
2140f05cddf9SRui Paulo {
2141f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
2142f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
2143f05cddf9SRui Paulo 	struct wpa_ie_data ie;
2144f05cddf9SRui Paulo 	int cipher;
2145f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
2146f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie *timeoutie;
2147f05cddf9SRui Paulo 	struct wpa_tdls_lnkid *lnkid;
2148f05cddf9SRui Paulo 	u32 lifetime;
2149f05cddf9SRui Paulo 	u8 dtoken;
2150f05cddf9SRui Paulo 	int ielen;
2151f05cddf9SRui Paulo 	u16 status;
2152f05cddf9SRui Paulo 	const u8 *pos;
2153*5b9c547cSRui Paulo 	int ret = 0;
2154f05cddf9SRui Paulo 
2155f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
2156f05cddf9SRui Paulo 		   "(Peer " MACSTR ")", MAC2STR(src_addr));
2157f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
2158f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
2159f05cddf9SRui Paulo 			break;
2160f05cddf9SRui Paulo 	}
2161f05cddf9SRui Paulo 	if (peer == NULL) {
2162f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
2163f05cddf9SRui Paulo 			   "TPK M2: " MACSTR, MAC2STR(src_addr));
2164f05cddf9SRui Paulo 		return -1;
2165f05cddf9SRui Paulo 	}
2166*5b9c547cSRui Paulo 	if (!peer->initiator) {
2167*5b9c547cSRui Paulo 		/*
2168*5b9c547cSRui Paulo 		 * This may happen if both devices try to initiate TDLS at the
2169*5b9c547cSRui Paulo 		 * same time and we accept the TPK M1 from the peer in
2170*5b9c547cSRui Paulo 		 * wpa_tdls_process_tpk_m1() and clear our previous state.
2171*5b9c547cSRui Paulo 		 */
2172*5b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so "
2173*5b9c547cSRui Paulo 			   "ignore TPK M2 from " MACSTR, MAC2STR(src_addr));
2174*5b9c547cSRui Paulo 		return -1;
2175*5b9c547cSRui Paulo 	}
2176f05cddf9SRui Paulo 	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
2177f05cddf9SRui Paulo 
2178*5b9c547cSRui Paulo 	if (len < 3 + 2 + 1) {
2179*5b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2180f05cddf9SRui Paulo 		return -1;
2181*5b9c547cSRui Paulo 	}
2182*5b9c547cSRui Paulo 
2183f05cddf9SRui Paulo 	pos = buf;
2184f05cddf9SRui Paulo 	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
2185f05cddf9SRui Paulo 	status = WPA_GET_LE16(pos);
2186f05cddf9SRui Paulo 	pos += 2 /* status code */;
2187f05cddf9SRui Paulo 
2188f05cddf9SRui Paulo 	if (status != WLAN_STATUS_SUCCESS) {
2189f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u",
2190f05cddf9SRui Paulo 			   status);
2191*5b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2192f05cddf9SRui Paulo 		return -1;
2193f05cddf9SRui Paulo 	}
2194f05cddf9SRui Paulo 
2195f05cddf9SRui Paulo 	status = WLAN_STATUS_UNSPECIFIED_FAILURE;
2196f05cddf9SRui Paulo 
2197f05cddf9SRui Paulo 	/* TODO: need to verify dialog token matches here or in kernel */
2198f05cddf9SRui Paulo 	dtoken = *pos++; /* dialog token */
2199f05cddf9SRui Paulo 
2200f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken);
2201f05cddf9SRui Paulo 
2202*5b9c547cSRui Paulo 	if (len < 3 + 2 + 1 + 2) {
2203*5b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2204f05cddf9SRui Paulo 		return -1;
2205*5b9c547cSRui Paulo 	}
2206f05cddf9SRui Paulo 
2207f05cddf9SRui Paulo 	/* capability information */
2208f05cddf9SRui Paulo 	peer->capability = WPA_GET_LE16(pos);
2209f05cddf9SRui Paulo 	pos += 2;
2210f05cddf9SRui Paulo 
2211f05cddf9SRui Paulo 	ielen = len - (pos - buf); /* start of IE in buf */
2212*5b9c547cSRui Paulo 
2213*5b9c547cSRui Paulo 	/*
2214*5b9c547cSRui Paulo 	 * Don't reject the message if failing to parse IEs. The IEs we need are
2215*5b9c547cSRui Paulo 	 * explicitly checked below. Some APs may add arbitrary padding to the
2216*5b9c547cSRui Paulo 	 * end of short TDLS frames and that would look like invalid IEs.
2217*5b9c547cSRui Paulo 	 */
2218*5b9c547cSRui Paulo 	if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0)
2219*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
2220*5b9c547cSRui Paulo 			   "TDLS: Failed to parse IEs in TPK M2 - ignore as an interop workaround");
2221f05cddf9SRui Paulo 
2222f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
2223f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_DECLINE_RESP) {
2224f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response");
2225f05cddf9SRui Paulo 		status = WLAN_STATUS_REQUEST_DECLINED;
2226f05cddf9SRui Paulo 		goto error;
2227f05cddf9SRui Paulo 	}
2228f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
2229f05cddf9SRui Paulo 
2230f05cddf9SRui Paulo 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
2231f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
2232f05cddf9SRui Paulo 			   "TPK M2");
2233f05cddf9SRui Paulo 		goto error;
2234f05cddf9SRui Paulo 	}
2235f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2",
2236f05cddf9SRui Paulo 		    kde.lnkid, kde.lnkid_len);
2237f05cddf9SRui Paulo 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
2238f05cddf9SRui Paulo 
2239f05cddf9SRui Paulo 	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
2240f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
2241f05cddf9SRui Paulo 		status = WLAN_STATUS_NOT_IN_SAME_BSS;
2242f05cddf9SRui Paulo 		goto error;
2243f05cddf9SRui Paulo 	}
2244f05cddf9SRui Paulo 
2245f05cddf9SRui Paulo 	if (copy_supp_rates(&kde, peer) < 0)
2246f05cddf9SRui Paulo 		goto error;
2247f05cddf9SRui Paulo 
2248*5b9c547cSRui Paulo 	if (copy_peer_ht_capab(&kde, peer) < 0)
2249*5b9c547cSRui Paulo 		goto error;
2250*5b9c547cSRui Paulo 
2251*5b9c547cSRui Paulo 	if (copy_peer_vht_capab(&kde, peer) < 0)
2252*5b9c547cSRui Paulo 		goto error;
2253*5b9c547cSRui Paulo 
2254*5b9c547cSRui Paulo 	if (copy_peer_ext_capab(&kde, peer) < 0)
2255*5b9c547cSRui Paulo 		goto error;
2256*5b9c547cSRui Paulo 
2257*5b9c547cSRui Paulo 	if (copy_peer_supp_channels(&kde, peer) < 0)
2258*5b9c547cSRui Paulo 		goto error;
2259*5b9c547cSRui Paulo 
2260*5b9c547cSRui Paulo 	if (copy_peer_supp_oper_classes(&kde, peer) < 0)
2261*5b9c547cSRui Paulo 		goto error;
2262*5b9c547cSRui Paulo 
2263*5b9c547cSRui Paulo 	peer->qos_info = kde.qosinfo;
2264*5b9c547cSRui Paulo 
2265*5b9c547cSRui Paulo 	/* Overwrite with the qos_info obtained in WMM IE */
2266*5b9c547cSRui Paulo 	if (copy_peer_wmm_capab(&kde, peer) < 0)
2267*5b9c547cSRui Paulo 		goto error;
2268*5b9c547cSRui Paulo 
2269*5b9c547cSRui Paulo 	peer->aid = kde.aid;
2270*5b9c547cSRui Paulo 
2271f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm)) {
2272f05cddf9SRui Paulo 		peer->rsnie_p_len = 0;
2273f05cddf9SRui Paulo 		peer->cipher = WPA_CIPHER_NONE;
2274f05cddf9SRui Paulo 		goto skip_rsn;
2275f05cddf9SRui Paulo 	}
2276f05cddf9SRui Paulo 
2277f05cddf9SRui Paulo 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
2278f05cddf9SRui Paulo 	    kde.rsn_ie == NULL) {
2279f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2");
2280f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_PARAMETERS;
2281f05cddf9SRui Paulo 		goto error;
2282f05cddf9SRui Paulo 	}
2283f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
2284f05cddf9SRui Paulo 		    kde.rsn_ie, kde.rsn_ie_len);
2285f05cddf9SRui Paulo 
2286*5b9c547cSRui Paulo 	if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
2287*5b9c547cSRui Paulo 		wpa_printf(MSG_INFO,
2288*5b9c547cSRui Paulo 			   "TDLS: Too long Responder RSN IE in TPK M2");
2289*5b9c547cSRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
2290*5b9c547cSRui Paulo 		goto error;
2291*5b9c547cSRui Paulo 	}
2292*5b9c547cSRui Paulo 
2293f05cddf9SRui Paulo 	/*
2294f05cddf9SRui Paulo 	 * FIX: bitwise comparison of RSN IE is not the correct way of
2295f05cddf9SRui Paulo 	 * validation this. It can be different, but certain fields must
2296f05cddf9SRui Paulo 	 * match. Since we list only a single pairwise cipher in TPK M1, the
2297f05cddf9SRui Paulo 	 * memcmp is likely to work in most cases, though.
2298f05cddf9SRui Paulo 	 */
2299f05cddf9SRui Paulo 	if (kde.rsn_ie_len != peer->rsnie_i_len ||
2300f05cddf9SRui Paulo 	    os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) {
2301f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does "
2302f05cddf9SRui Paulo 			   "not match with RSN IE used in TPK M1");
2303f05cddf9SRui Paulo 		wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1",
2304f05cddf9SRui Paulo 			    peer->rsnie_i, peer->rsnie_i_len);
2305f05cddf9SRui Paulo 		wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
2306f05cddf9SRui Paulo 			    kde.rsn_ie, kde.rsn_ie_len);
2307f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
2308f05cddf9SRui Paulo 		goto error;
2309f05cddf9SRui Paulo 	}
2310f05cddf9SRui Paulo 
2311f05cddf9SRui Paulo 	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
2312f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2");
2313f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
2314f05cddf9SRui Paulo 		goto error;
2315f05cddf9SRui Paulo 	}
2316f05cddf9SRui Paulo 
2317f05cddf9SRui Paulo 	cipher = ie.pairwise_cipher;
2318f05cddf9SRui Paulo 	if (cipher == WPA_CIPHER_CCMP) {
2319f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
2320f05cddf9SRui Paulo 		cipher = WPA_CIPHER_CCMP;
2321f05cddf9SRui Paulo 	} else {
2322f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2");
2323f05cddf9SRui Paulo 		status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
2324f05cddf9SRui Paulo 		goto error;
2325f05cddf9SRui Paulo 	}
2326f05cddf9SRui Paulo 
2327f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2",
2328f05cddf9SRui Paulo 		    kde.ftie, sizeof(*ftie));
2329f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
2330f05cddf9SRui Paulo 
2331f05cddf9SRui Paulo 	if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
2332f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does "
2333f05cddf9SRui Paulo 			   "not match with FTIE SNonce used in TPK M1");
2334f05cddf9SRui Paulo 		/* Silently discard the frame */
2335f05cddf9SRui Paulo 		return -1;
2336f05cddf9SRui Paulo 	}
2337f05cddf9SRui Paulo 
2338f05cddf9SRui Paulo 	/* Responder Nonce and RSN IE */
2339f05cddf9SRui Paulo 	os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN);
2340f05cddf9SRui Paulo 	os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len);
2341f05cddf9SRui Paulo 	peer->rsnie_p_len = kde.rsn_ie_len;
2342f05cddf9SRui Paulo 	peer->cipher = cipher;
2343f05cddf9SRui Paulo 
2344f05cddf9SRui Paulo 	/* Lifetime */
2345f05cddf9SRui Paulo 	if (kde.key_lifetime == NULL) {
2346f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2");
2347f05cddf9SRui Paulo 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
2348f05cddf9SRui Paulo 		goto error;
2349f05cddf9SRui Paulo 	}
2350f05cddf9SRui Paulo 	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
2351f05cddf9SRui Paulo 	lifetime = WPA_GET_LE32(timeoutie->value);
2352f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2",
2353f05cddf9SRui Paulo 		   lifetime);
2354f05cddf9SRui Paulo 	if (lifetime != peer->lifetime) {
2355f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
2356f05cddf9SRui Paulo 			   "TPK M2 (expected %u)", lifetime, peer->lifetime);
2357f05cddf9SRui Paulo 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
2358f05cddf9SRui Paulo 		goto error;
2359f05cddf9SRui Paulo 	}
2360f05cddf9SRui Paulo 
2361f05cddf9SRui Paulo 	wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
2362f05cddf9SRui Paulo 
2363f05cddf9SRui Paulo 	/* Process MIC check to see if TPK M2 is right */
2364f05cddf9SRui Paulo 	if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
2365f05cddf9SRui Paulo 					   (u8 *) timeoutie, ftie) < 0) {
2366f05cddf9SRui Paulo 		/* Discard the frame */
2367f05cddf9SRui Paulo 		wpa_tdls_del_key(sm, peer);
2368*5b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2369f05cddf9SRui Paulo 		return -1;
2370f05cddf9SRui Paulo 	}
2371f05cddf9SRui Paulo 
2372*5b9c547cSRui Paulo 	if (wpa_tdls_set_key(sm, peer) < 0) {
2373*5b9c547cSRui Paulo 		/*
2374*5b9c547cSRui Paulo 		 * Some drivers may not be able to config the key prior to full
2375*5b9c547cSRui Paulo 		 * STA entry having been configured.
2376*5b9c547cSRui Paulo 		 */
2377*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
2378*5b9c547cSRui Paulo 			   "STA entry is complete");
2379*5b9c547cSRui Paulo 		peer->reconfig_key = 1;
2380*5b9c547cSRui Paulo 	}
2381f05cddf9SRui Paulo 
2382f05cddf9SRui Paulo skip_rsn:
2383f05cddf9SRui Paulo 	peer->dtoken = dtoken;
2384f05cddf9SRui Paulo 
2385*5b9c547cSRui Paulo 	/* add supported rates, capabilities, and qos_info to the TDLS peer */
2386*5b9c547cSRui Paulo 	if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
2387*5b9c547cSRui Paulo 		goto error;
2388*5b9c547cSRui Paulo 
2389f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
2390f05cddf9SRui Paulo 		   "TPK Handshake Message 3");
2391*5b9c547cSRui Paulo 	if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
2392*5b9c547cSRui Paulo 		goto error;
2393f05cddf9SRui Paulo 
2394*5b9c547cSRui Paulo 	if (!peer->tpk_success) {
2395*5b9c547cSRui Paulo 		/*
2396*5b9c547cSRui Paulo 		 * Enable Link only when tpk_success is 0, signifying that this
2397*5b9c547cSRui Paulo 		 * processing of TPK M2 frame is not because of a retransmission
2398*5b9c547cSRui Paulo 		 * during TDLS setup handshake.
2399*5b9c547cSRui Paulo 		 */
2400*5b9c547cSRui Paulo 		ret = wpa_tdls_enable_link(sm, peer);
2401*5b9c547cSRui Paulo 		if (ret < 0) {
2402*5b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
2403*5b9c547cSRui Paulo 			wpa_tdls_do_teardown(
2404*5b9c547cSRui Paulo 				sm, peer,
2405*5b9c547cSRui Paulo 				WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
2406*5b9c547cSRui Paulo 		}
2407*5b9c547cSRui Paulo 	}
2408*5b9c547cSRui Paulo 	return ret;
2409f05cddf9SRui Paulo 
2410f05cddf9SRui Paulo error:
2411*5b9c547cSRui Paulo 	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1,
2412f05cddf9SRui Paulo 			    status);
2413*5b9c547cSRui Paulo 	wpa_tdls_disable_peer_link(sm, peer);
2414f05cddf9SRui Paulo 	return -1;
2415f05cddf9SRui Paulo }
2416f05cddf9SRui Paulo 
2417f05cddf9SRui Paulo 
2418f05cddf9SRui Paulo static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
2419f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
2420f05cddf9SRui Paulo {
2421f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
2422f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
2423f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
2424f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie *timeoutie;
2425f05cddf9SRui Paulo 	struct wpa_tdls_lnkid *lnkid;
2426f05cddf9SRui Paulo 	int ielen;
2427f05cddf9SRui Paulo 	u16 status;
2428f05cddf9SRui Paulo 	const u8 *pos;
2429f05cddf9SRui Paulo 	u32 lifetime;
2430*5b9c547cSRui Paulo 	int ret = 0;
2431f05cddf9SRui Paulo 
2432f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
2433f05cddf9SRui Paulo 		   "(Peer " MACSTR ")", MAC2STR(src_addr));
2434f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
2435f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
2436f05cddf9SRui Paulo 			break;
2437f05cddf9SRui Paulo 	}
2438f05cddf9SRui Paulo 	if (peer == NULL) {
2439f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
2440f05cddf9SRui Paulo 			   "TPK M3: " MACSTR, MAC2STR(src_addr));
2441f05cddf9SRui Paulo 		return -1;
2442f05cddf9SRui Paulo 	}
2443f05cddf9SRui Paulo 	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE);
2444f05cddf9SRui Paulo 
2445f05cddf9SRui Paulo 	if (len < 3 + 3)
2446*5b9c547cSRui Paulo 		goto error;
2447f05cddf9SRui Paulo 	pos = buf;
2448f05cddf9SRui Paulo 	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
2449f05cddf9SRui Paulo 
2450f05cddf9SRui Paulo 	status = WPA_GET_LE16(pos);
2451f05cddf9SRui Paulo 
2452f05cddf9SRui Paulo 	if (status != 0) {
2453f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u",
2454f05cddf9SRui Paulo 			   status);
2455*5b9c547cSRui Paulo 		goto error;
2456f05cddf9SRui Paulo 	}
2457f05cddf9SRui Paulo 	pos += 2 /* status code */ + 1 /* dialog token */;
2458f05cddf9SRui Paulo 
2459f05cddf9SRui Paulo 	ielen = len - (pos - buf); /* start of IE in buf */
2460*5b9c547cSRui Paulo 
2461*5b9c547cSRui Paulo 	/*
2462*5b9c547cSRui Paulo 	 * Don't reject the message if failing to parse IEs. The IEs we need are
2463*5b9c547cSRui Paulo 	 * explicitly checked below. Some APs piggy-back broken IEs to the end
2464*5b9c547cSRui Paulo 	 * of a TDLS Confirm packet, which will fail the link if we don't ignore
2465*5b9c547cSRui Paulo 	 * this error.
2466*5b9c547cSRui Paulo 	 */
2467f05cddf9SRui Paulo 	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
2468*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
2469*5b9c547cSRui Paulo 			   "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
2470f05cddf9SRui Paulo 	}
2471f05cddf9SRui Paulo 
2472f05cddf9SRui Paulo 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
2473f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3");
2474*5b9c547cSRui Paulo 		goto error;
2475f05cddf9SRui Paulo 	}
2476f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3",
2477f05cddf9SRui Paulo 		    (u8 *) kde.lnkid, kde.lnkid_len);
2478f05cddf9SRui Paulo 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
2479f05cddf9SRui Paulo 
2480f05cddf9SRui Paulo 	if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
2481f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
2482*5b9c547cSRui Paulo 		goto error;
2483f05cddf9SRui Paulo 	}
2484f05cddf9SRui Paulo 
2485f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm))
2486f05cddf9SRui Paulo 		goto skip_rsn;
2487f05cddf9SRui Paulo 
2488f05cddf9SRui Paulo 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
2489f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3");
2490*5b9c547cSRui Paulo 		goto error;
2491f05cddf9SRui Paulo 	}
2492f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3",
2493f05cddf9SRui Paulo 		    kde.ftie, sizeof(*ftie));
2494f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
2495f05cddf9SRui Paulo 
2496f05cddf9SRui Paulo 	if (kde.rsn_ie == NULL) {
2497f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3");
2498*5b9c547cSRui Paulo 		goto error;
2499f05cddf9SRui Paulo 	}
2500f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3",
2501f05cddf9SRui Paulo 		    kde.rsn_ie, kde.rsn_ie_len);
2502f05cddf9SRui Paulo 	if (kde.rsn_ie_len != peer->rsnie_p_len ||
2503f05cddf9SRui Paulo 	    os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) {
2504f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match "
2505f05cddf9SRui Paulo 			   "with the one sent in TPK M2");
2506*5b9c547cSRui Paulo 		goto error;
2507f05cddf9SRui Paulo 	}
2508f05cddf9SRui Paulo 
2509f05cddf9SRui Paulo 	if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) {
2510f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does "
2511f05cddf9SRui Paulo 			   "not match with FTIE ANonce used in TPK M2");
2512*5b9c547cSRui Paulo 		goto error;
2513f05cddf9SRui Paulo 	}
2514f05cddf9SRui Paulo 
2515f05cddf9SRui Paulo 	if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
2516f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not "
2517f05cddf9SRui Paulo 			   "match with FTIE SNonce used in TPK M1");
2518*5b9c547cSRui Paulo 		goto error;
2519f05cddf9SRui Paulo 	}
2520f05cddf9SRui Paulo 
2521f05cddf9SRui Paulo 	if (kde.key_lifetime == NULL) {
2522f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
2523*5b9c547cSRui Paulo 		goto error;
2524f05cddf9SRui Paulo 	}
2525f05cddf9SRui Paulo 	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
2526f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3",
2527f05cddf9SRui Paulo 		    (u8 *) timeoutie, sizeof(*timeoutie));
2528f05cddf9SRui Paulo 	lifetime = WPA_GET_LE32(timeoutie->value);
2529f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3",
2530f05cddf9SRui Paulo 		   lifetime);
2531f05cddf9SRui Paulo 	if (lifetime != peer->lifetime) {
2532f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
2533f05cddf9SRui Paulo 			   "TPK M3 (expected %u)", lifetime, peer->lifetime);
2534*5b9c547cSRui Paulo 		goto error;
2535f05cddf9SRui Paulo 	}
2536f05cddf9SRui Paulo 
2537f05cddf9SRui Paulo 	if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
2538f05cddf9SRui Paulo 					   (u8 *) timeoutie, ftie) < 0) {
2539f05cddf9SRui Paulo 		wpa_tdls_del_key(sm, peer);
2540*5b9c547cSRui Paulo 		goto error;
2541f05cddf9SRui Paulo 	}
2542f05cddf9SRui Paulo 
2543*5b9c547cSRui Paulo 	if (wpa_tdls_set_key(sm, peer) < 0) {
2544*5b9c547cSRui Paulo 		/*
2545*5b9c547cSRui Paulo 		 * Some drivers may not be able to config the key prior to full
2546*5b9c547cSRui Paulo 		 * STA entry having been configured.
2547*5b9c547cSRui Paulo 		 */
2548*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
2549*5b9c547cSRui Paulo 			   "STA entry is complete");
2550*5b9c547cSRui Paulo 		peer->reconfig_key = 1;
2551*5b9c547cSRui Paulo 	}
2552f05cddf9SRui Paulo 
2553f05cddf9SRui Paulo skip_rsn:
2554*5b9c547cSRui Paulo 	/* add supported rates, capabilities, and qos_info to the TDLS peer */
2555*5b9c547cSRui Paulo 	if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
2556*5b9c547cSRui Paulo 		goto error;
2557f05cddf9SRui Paulo 
2558*5b9c547cSRui Paulo 	if (!peer->tpk_success) {
2559*5b9c547cSRui Paulo 		/*
2560*5b9c547cSRui Paulo 		 * Enable Link only when tpk_success is 0, signifying that this
2561*5b9c547cSRui Paulo 		 * processing of TPK M3 frame is not because of a retransmission
2562*5b9c547cSRui Paulo 		 * during TDLS setup handshake.
2563*5b9c547cSRui Paulo 		 */
2564*5b9c547cSRui Paulo 		ret = wpa_tdls_enable_link(sm, peer);
2565*5b9c547cSRui Paulo 		if (ret < 0) {
2566*5b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
2567*5b9c547cSRui Paulo 			goto error;
2568*5b9c547cSRui Paulo 		}
2569*5b9c547cSRui Paulo 	}
2570*5b9c547cSRui Paulo 	return ret;
2571*5b9c547cSRui Paulo error:
2572*5b9c547cSRui Paulo 	wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
2573*5b9c547cSRui Paulo 	return -1;
2574f05cddf9SRui Paulo }
2575f05cddf9SRui Paulo 
2576f05cddf9SRui Paulo 
2577f05cddf9SRui Paulo static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs)
2578f05cddf9SRui Paulo {
2579f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie;
2580f05cddf9SRui Paulo 
2581f05cddf9SRui Paulo 	os_memset(lifetime, 0, ie_len);
2582f05cddf9SRui Paulo 	lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL;
2583f05cddf9SRui Paulo 	lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2;
2584f05cddf9SRui Paulo 	lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME;
2585f05cddf9SRui Paulo 	WPA_PUT_LE32(lifetime->value, tsecs);
2586f05cddf9SRui Paulo 	os_memcpy(pos, ie, ie_len);
2587f05cddf9SRui Paulo 	return pos + ie_len;
2588f05cddf9SRui Paulo }
2589f05cddf9SRui Paulo 
2590f05cddf9SRui Paulo 
2591f05cddf9SRui Paulo /**
2592f05cddf9SRui Paulo  * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1)
2593f05cddf9SRui Paulo  * @sm: Pointer to WPA state machine data from wpa_sm_init()
2594f05cddf9SRui Paulo  * @peer: MAC address of the peer STA
2595f05cddf9SRui Paulo  * Returns: 0 on success, or -1 on failure
2596f05cddf9SRui Paulo  *
2597f05cddf9SRui Paulo  * Send TPK Handshake Message 1 info to driver to start TDLS
2598f05cddf9SRui Paulo  * handshake with the peer.
2599f05cddf9SRui Paulo  */
2600f05cddf9SRui Paulo int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
2601f05cddf9SRui Paulo {
2602f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
2603f05cddf9SRui Paulo 	int tdls_prohibited = sm->tdls_prohibited;
2604f05cddf9SRui Paulo 
2605f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
2606f05cddf9SRui Paulo 		return -1;
2607f05cddf9SRui Paulo 
2608f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
2609f05cddf9SRui Paulo 	if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
2610f05cddf9SRui Paulo 	    tdls_prohibited) {
2611f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
2612f05cddf9SRui Paulo 			   "on TDLS");
2613f05cddf9SRui Paulo 		tdls_prohibited = 0;
2614f05cddf9SRui Paulo 	}
2615f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
2616f05cddf9SRui Paulo 
2617f05cddf9SRui Paulo 	if (tdls_prohibited) {
2618f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - "
2619f05cddf9SRui Paulo 			   "reject request to start setup");
2620f05cddf9SRui Paulo 		return -1;
2621f05cddf9SRui Paulo 	}
2622f05cddf9SRui Paulo 
2623*5b9c547cSRui Paulo 	peer = wpa_tdls_add_peer(sm, addr, NULL);
2624f05cddf9SRui Paulo 	if (peer == NULL)
2625f05cddf9SRui Paulo 		return -1;
2626*5b9c547cSRui Paulo 
2627*5b9c547cSRui Paulo 	if (peer->tpk_in_progress) {
2628*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer");
2629*5b9c547cSRui Paulo 		return 0;
2630f05cddf9SRui Paulo 	}
2631f05cddf9SRui Paulo 
2632f05cddf9SRui Paulo 	peer->initiator = 1;
2633f05cddf9SRui Paulo 
2634f05cddf9SRui Paulo 	/* add the peer to the driver as a "setup in progress" peer */
2635*5b9c547cSRui Paulo 	if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
2636*5b9c547cSRui Paulo 				    NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0)) {
2637*5b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2638*5b9c547cSRui Paulo 		return -1;
2639*5b9c547cSRui Paulo 	}
2640*5b9c547cSRui Paulo 
2641*5b9c547cSRui Paulo 	peer->tpk_in_progress = 1;
2642f05cddf9SRui Paulo 
2643f05cddf9SRui Paulo 	if (wpa_tdls_send_tpk_m1(sm, peer) < 0) {
2644*5b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2645f05cddf9SRui Paulo 		return -1;
2646f05cddf9SRui Paulo 	}
2647f05cddf9SRui Paulo 
2648f05cddf9SRui Paulo 	return 0;
2649f05cddf9SRui Paulo }
2650f05cddf9SRui Paulo 
2651f05cddf9SRui Paulo 
2652*5b9c547cSRui Paulo void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr)
2653f05cddf9SRui Paulo {
2654f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
2655f05cddf9SRui Paulo 
2656f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
2657*5b9c547cSRui Paulo 		return;
2658f05cddf9SRui Paulo 
2659f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
2660f05cddf9SRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
2661f05cddf9SRui Paulo 			break;
2662f05cddf9SRui Paulo 	}
2663f05cddf9SRui Paulo 
2664f05cddf9SRui Paulo 	if (peer == NULL || !peer->tpk_success)
2665*5b9c547cSRui Paulo 		return;
2666f05cddf9SRui Paulo 
2667f05cddf9SRui Paulo 	if (sm->tdls_external_setup) {
2668f05cddf9SRui Paulo 		/*
2669f05cddf9SRui Paulo 		 * Disable previous link to allow renegotiation to be completed
2670f05cddf9SRui Paulo 		 * on AP path.
2671f05cddf9SRui Paulo 		 */
2672*5b9c547cSRui Paulo 		wpa_tdls_do_teardown(sm, peer,
2673*5b9c547cSRui Paulo 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
2674f05cddf9SRui Paulo 	}
2675f05cddf9SRui Paulo }
2676f05cddf9SRui Paulo 
2677f05cddf9SRui Paulo 
2678f05cddf9SRui Paulo /**
2679f05cddf9SRui Paulo  * wpa_supplicant_rx_tdls - Receive TDLS data frame
2680f05cddf9SRui Paulo  *
2681f05cddf9SRui Paulo  * This function is called to receive TDLS (ethertype = 0x890d) data frames.
2682f05cddf9SRui Paulo  */
2683f05cddf9SRui Paulo static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr,
2684f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
2685f05cddf9SRui Paulo {
2686f05cddf9SRui Paulo 	struct wpa_sm *sm = ctx;
2687f05cddf9SRui Paulo 	struct wpa_tdls_frame *tf;
2688f05cddf9SRui Paulo 
2689f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation",
2690f05cddf9SRui Paulo 		    buf, len);
2691f05cddf9SRui Paulo 
2692f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported) {
2693f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled "
2694f05cddf9SRui Paulo 			   "or unsupported by driver");
2695f05cddf9SRui Paulo 		return;
2696f05cddf9SRui Paulo 	}
2697f05cddf9SRui Paulo 
2698f05cddf9SRui Paulo 	if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) {
2699f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message");
2700f05cddf9SRui Paulo 		return;
2701f05cddf9SRui Paulo 	}
2702f05cddf9SRui Paulo 
2703f05cddf9SRui Paulo 	if (len < sizeof(*tf)) {
2704f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Drop too short frame");
2705f05cddf9SRui Paulo 		return;
2706f05cddf9SRui Paulo 	}
2707f05cddf9SRui Paulo 
2708f05cddf9SRui Paulo 	/* Check to make sure its a valid encapsulated TDLS frame */
2709f05cddf9SRui Paulo 	tf = (struct wpa_tdls_frame *) buf;
2710f05cddf9SRui Paulo 	if (tf->payloadtype != 2 /* TDLS_RFTYPE */ ||
2711f05cddf9SRui Paulo 	    tf->category != WLAN_ACTION_TDLS) {
2712f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u "
2713f05cddf9SRui Paulo 			   "category=%u action=%u",
2714f05cddf9SRui Paulo 			   tf->payloadtype, tf->category, tf->action);
2715f05cddf9SRui Paulo 		return;
2716f05cddf9SRui Paulo 	}
2717f05cddf9SRui Paulo 
2718f05cddf9SRui Paulo 	switch (tf->action) {
2719f05cddf9SRui Paulo 	case WLAN_TDLS_SETUP_REQUEST:
2720f05cddf9SRui Paulo 		wpa_tdls_process_tpk_m1(sm, src_addr, buf, len);
2721f05cddf9SRui Paulo 		break;
2722f05cddf9SRui Paulo 	case WLAN_TDLS_SETUP_RESPONSE:
2723f05cddf9SRui Paulo 		wpa_tdls_process_tpk_m2(sm, src_addr, buf, len);
2724f05cddf9SRui Paulo 		break;
2725f05cddf9SRui Paulo 	case WLAN_TDLS_SETUP_CONFIRM:
2726f05cddf9SRui Paulo 		wpa_tdls_process_tpk_m3(sm, src_addr, buf, len);
2727f05cddf9SRui Paulo 		break;
2728f05cddf9SRui Paulo 	case WLAN_TDLS_TEARDOWN:
2729f05cddf9SRui Paulo 		wpa_tdls_recv_teardown(sm, src_addr, buf, len);
2730f05cddf9SRui Paulo 		break;
2731f05cddf9SRui Paulo 	case WLAN_TDLS_DISCOVERY_REQUEST:
2732f05cddf9SRui Paulo 		wpa_tdls_process_discovery_request(sm, src_addr, buf, len);
2733f05cddf9SRui Paulo 		break;
2734f05cddf9SRui Paulo 	default:
2735f05cddf9SRui Paulo 		/* Kernel code will process remaining frames */
2736f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u",
2737f05cddf9SRui Paulo 			   tf->action);
2738f05cddf9SRui Paulo 		break;
2739f05cddf9SRui Paulo 	}
2740f05cddf9SRui Paulo }
2741f05cddf9SRui Paulo 
2742f05cddf9SRui Paulo 
2743f05cddf9SRui Paulo /**
2744f05cddf9SRui Paulo  * wpa_tdls_init - Initialize driver interface parameters for TDLS
2745f05cddf9SRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
2746f05cddf9SRui Paulo  * Returns: 0 on success, -1 on failure
2747f05cddf9SRui Paulo  *
2748f05cddf9SRui Paulo  * This function is called to initialize driver interface parameters for TDLS.
2749f05cddf9SRui Paulo  * wpa_drv_init() must have been called before this function to initialize the
2750f05cddf9SRui Paulo  * driver interface.
2751f05cddf9SRui Paulo  */
2752f05cddf9SRui Paulo int wpa_tdls_init(struct wpa_sm *sm)
2753f05cddf9SRui Paulo {
2754f05cddf9SRui Paulo 	if (sm == NULL)
2755f05cddf9SRui Paulo 		return -1;
2756f05cddf9SRui Paulo 
2757f05cddf9SRui Paulo 	sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
2758f05cddf9SRui Paulo 				     sm->ifname,
2759f05cddf9SRui Paulo 				     sm->own_addr,
2760f05cddf9SRui Paulo 				     ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
2761f05cddf9SRui Paulo 				     sm, 0);
2762f05cddf9SRui Paulo 	if (sm->l2_tdls == NULL) {
2763f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet "
2764f05cddf9SRui Paulo 			   "connection");
2765f05cddf9SRui Paulo 		return -1;
2766f05cddf9SRui Paulo 	}
2767f05cddf9SRui Paulo 
2768f05cddf9SRui Paulo 	/*
2769f05cddf9SRui Paulo 	 * Drivers that support TDLS but don't implement the get_capa callback
2770f05cddf9SRui Paulo 	 * are assumed to perform everything internally
2771f05cddf9SRui Paulo 	 */
2772f05cddf9SRui Paulo 	if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported,
2773*5b9c547cSRui Paulo 				 &sm->tdls_external_setup,
2774*5b9c547cSRui Paulo 				 &sm->tdls_chan_switch) < 0) {
2775f05cddf9SRui Paulo 		sm->tdls_supported = 1;
2776f05cddf9SRui Paulo 		sm->tdls_external_setup = 0;
2777f05cddf9SRui Paulo 	}
2778f05cddf9SRui Paulo 
2779f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by "
2780f05cddf9SRui Paulo 		   "driver", sm->tdls_supported ? "" : " not");
2781f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup",
2782f05cddf9SRui Paulo 		   sm->tdls_external_setup ? "external" : "internal");
2783*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching",
2784*5b9c547cSRui Paulo 		   sm->tdls_chan_switch ? "supports" : "does not support");
2785f05cddf9SRui Paulo 
2786f05cddf9SRui Paulo 	return 0;
2787f05cddf9SRui Paulo }
2788f05cddf9SRui Paulo 
2789f05cddf9SRui Paulo 
2790*5b9c547cSRui Paulo void wpa_tdls_teardown_peers(struct wpa_sm *sm)
2791*5b9c547cSRui Paulo {
2792*5b9c547cSRui Paulo 	struct wpa_tdls_peer *peer, *tmp;
2793*5b9c547cSRui Paulo 
2794*5b9c547cSRui Paulo 	if (!sm)
2795*5b9c547cSRui Paulo 		return;
2796*5b9c547cSRui Paulo 	peer = sm->tdls;
2797*5b9c547cSRui Paulo 
2798*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
2799*5b9c547cSRui Paulo 
2800*5b9c547cSRui Paulo 	while (peer) {
2801*5b9c547cSRui Paulo 		tmp = peer->next;
2802*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR,
2803*5b9c547cSRui Paulo 			   MAC2STR(peer->addr));
2804*5b9c547cSRui Paulo 		if (sm->tdls_external_setup)
2805*5b9c547cSRui Paulo 			wpa_tdls_do_teardown(sm, peer,
2806*5b9c547cSRui Paulo 					     WLAN_REASON_DEAUTH_LEAVING);
2807*5b9c547cSRui Paulo 		else
2808*5b9c547cSRui Paulo 			wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
2809*5b9c547cSRui Paulo 
2810*5b9c547cSRui Paulo 		peer = tmp;
2811*5b9c547cSRui Paulo 	}
2812*5b9c547cSRui Paulo }
2813*5b9c547cSRui Paulo 
2814*5b9c547cSRui Paulo 
2815f05cddf9SRui Paulo static void wpa_tdls_remove_peers(struct wpa_sm *sm)
2816f05cddf9SRui Paulo {
2817f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer, *tmp;
2818f05cddf9SRui Paulo 
2819f05cddf9SRui Paulo 	peer = sm->tdls;
2820f05cddf9SRui Paulo 
2821f05cddf9SRui Paulo 	while (peer) {
2822f05cddf9SRui Paulo 		int res;
2823f05cddf9SRui Paulo 		tmp = peer->next;
2824f05cddf9SRui Paulo 		res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
2825f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
2826f05cddf9SRui Paulo 			   MAC2STR(peer->addr), res);
2827f05cddf9SRui Paulo 		wpa_tdls_peer_free(sm, peer);
2828f05cddf9SRui Paulo 		peer = tmp;
2829f05cddf9SRui Paulo 	}
2830f05cddf9SRui Paulo }
2831f05cddf9SRui Paulo 
2832f05cddf9SRui Paulo 
2833f05cddf9SRui Paulo /**
2834f05cddf9SRui Paulo  * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS
2835f05cddf9SRui Paulo  *
2836f05cddf9SRui Paulo  * This function is called to recover driver interface parameters for TDLS
2837f05cddf9SRui Paulo  * and frees resources allocated for it.
2838f05cddf9SRui Paulo  */
2839f05cddf9SRui Paulo void wpa_tdls_deinit(struct wpa_sm *sm)
2840f05cddf9SRui Paulo {
2841f05cddf9SRui Paulo 	if (sm == NULL)
2842f05cddf9SRui Paulo 		return;
2843f05cddf9SRui Paulo 
2844f05cddf9SRui Paulo 	if (sm->l2_tdls)
2845f05cddf9SRui Paulo 		l2_packet_deinit(sm->l2_tdls);
2846f05cddf9SRui Paulo 	sm->l2_tdls = NULL;
2847f05cddf9SRui Paulo 
2848f05cddf9SRui Paulo 	wpa_tdls_remove_peers(sm);
2849f05cddf9SRui Paulo }
2850f05cddf9SRui Paulo 
2851f05cddf9SRui Paulo 
2852f05cddf9SRui Paulo void wpa_tdls_assoc(struct wpa_sm *sm)
2853f05cddf9SRui Paulo {
2854f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association");
2855f05cddf9SRui Paulo 	wpa_tdls_remove_peers(sm);
2856f05cddf9SRui Paulo }
2857f05cddf9SRui Paulo 
2858f05cddf9SRui Paulo 
2859f05cddf9SRui Paulo void wpa_tdls_disassoc(struct wpa_sm *sm)
2860f05cddf9SRui Paulo {
2861f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation");
2862f05cddf9SRui Paulo 	wpa_tdls_remove_peers(sm);
2863f05cddf9SRui Paulo }
2864f05cddf9SRui Paulo 
2865f05cddf9SRui Paulo 
2866*5b9c547cSRui Paulo static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems)
2867f05cddf9SRui Paulo {
2868f05cddf9SRui Paulo 	/* bit 38 - TDLS Prohibited */
2869*5b9c547cSRui Paulo 	return !!(elems->ext_capab[2 + 4] & 0x40);
2870*5b9c547cSRui Paulo }
2871*5b9c547cSRui Paulo 
2872*5b9c547cSRui Paulo 
2873*5b9c547cSRui Paulo static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems)
2874*5b9c547cSRui Paulo {
2875*5b9c547cSRui Paulo 	/* bit 39 - TDLS Channel Switch Prohibited */
2876*5b9c547cSRui Paulo 	return !!(elems->ext_capab[2 + 4] & 0x80);
2877f05cddf9SRui Paulo }
2878f05cddf9SRui Paulo 
2879f05cddf9SRui Paulo 
2880f05cddf9SRui Paulo void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
2881f05cddf9SRui Paulo {
2882*5b9c547cSRui Paulo 	struct wpa_eapol_ie_parse elems;
2883*5b9c547cSRui Paulo 
2884*5b9c547cSRui Paulo 	sm->tdls_prohibited = 0;
2885*5b9c547cSRui Paulo 	sm->tdls_chan_switch_prohibited = 0;
2886*5b9c547cSRui Paulo 
2887*5b9c547cSRui Paulo 	if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
2888*5b9c547cSRui Paulo 	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
2889*5b9c547cSRui Paulo 		return;
2890*5b9c547cSRui Paulo 
2891*5b9c547cSRui Paulo 	sm->tdls_prohibited = wpa_tdls_prohibited(&elems);
2892f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS",
2893f05cddf9SRui Paulo 		   sm->tdls_prohibited ? "prohibited" : "allowed");
2894*5b9c547cSRui Paulo 	sm->tdls_chan_switch_prohibited =
2895*5b9c547cSRui Paulo 		wpa_tdls_chan_switch_prohibited(&elems);
2896*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS",
2897*5b9c547cSRui Paulo 		   sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed");
2898f05cddf9SRui Paulo }
2899f05cddf9SRui Paulo 
2900f05cddf9SRui Paulo 
2901f05cddf9SRui Paulo void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
2902f05cddf9SRui Paulo {
2903*5b9c547cSRui Paulo 	struct wpa_eapol_ie_parse elems;
2904*5b9c547cSRui Paulo 
2905*5b9c547cSRui Paulo 	if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
2906*5b9c547cSRui Paulo 	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
2907*5b9c547cSRui Paulo 		return;
2908*5b9c547cSRui Paulo 
2909*5b9c547cSRui Paulo 	if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) {
2910f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on "
2911f05cddf9SRui Paulo 			   "(Re)Association Response IEs");
2912f05cddf9SRui Paulo 		sm->tdls_prohibited = 1;
2913f05cddf9SRui Paulo 	}
2914*5b9c547cSRui Paulo 
2915*5b9c547cSRui Paulo 	if (!sm->tdls_chan_switch_prohibited &&
2916*5b9c547cSRui Paulo 	    wpa_tdls_chan_switch_prohibited(&elems)) {
2917*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
2918*5b9c547cSRui Paulo 			   "TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs");
2919*5b9c547cSRui Paulo 		sm->tdls_chan_switch_prohibited = 1;
2920*5b9c547cSRui Paulo 	}
2921f05cddf9SRui Paulo }
2922f05cddf9SRui Paulo 
2923f05cddf9SRui Paulo 
2924f05cddf9SRui Paulo void wpa_tdls_enable(struct wpa_sm *sm, int enabled)
2925f05cddf9SRui Paulo {
2926f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled");
2927f05cddf9SRui Paulo 	sm->tdls_disabled = !enabled;
2928f05cddf9SRui Paulo }
2929f05cddf9SRui Paulo 
2930f05cddf9SRui Paulo 
2931f05cddf9SRui Paulo int wpa_tdls_is_external_setup(struct wpa_sm *sm)
2932f05cddf9SRui Paulo {
2933f05cddf9SRui Paulo 	return sm->tdls_external_setup;
2934f05cddf9SRui Paulo }
2935*5b9c547cSRui Paulo 
2936*5b9c547cSRui Paulo 
2937*5b9c547cSRui Paulo int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
2938*5b9c547cSRui Paulo 				u8 oper_class,
2939*5b9c547cSRui Paulo 				struct hostapd_freq_params *freq_params)
2940*5b9c547cSRui Paulo {
2941*5b9c547cSRui Paulo 	struct wpa_tdls_peer *peer;
2942*5b9c547cSRui Paulo 	int ret;
2943*5b9c547cSRui Paulo 
2944*5b9c547cSRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
2945*5b9c547cSRui Paulo 		return -1;
2946*5b9c547cSRui Paulo 
2947*5b9c547cSRui Paulo 	if (!sm->tdls_chan_switch) {
2948*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
2949*5b9c547cSRui Paulo 			   "TDLS: Channel switching not supported by the driver");
2950*5b9c547cSRui Paulo 		return -1;
2951*5b9c547cSRui Paulo 	}
2952*5b9c547cSRui Paulo 
2953*5b9c547cSRui Paulo 	if (sm->tdls_chan_switch_prohibited) {
2954*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
2955*5b9c547cSRui Paulo 			   "TDLS: Channel switching is prohibited in this BSS - reject request to switch channel");
2956*5b9c547cSRui Paulo 		return -1;
2957*5b9c547cSRui Paulo 	}
2958*5b9c547cSRui Paulo 
2959*5b9c547cSRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
2960*5b9c547cSRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
2961*5b9c547cSRui Paulo 			break;
2962*5b9c547cSRui Paulo 	}
2963*5b9c547cSRui Paulo 
2964*5b9c547cSRui Paulo 	if (peer == NULL || !peer->tpk_success) {
2965*5b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR
2966*5b9c547cSRui Paulo 			   " not found for channel switching", MAC2STR(addr));
2967*5b9c547cSRui Paulo 		return -1;
2968*5b9c547cSRui Paulo 	}
2969*5b9c547cSRui Paulo 
2970*5b9c547cSRui Paulo 	if (peer->chan_switch_enabled) {
2971*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
2972*5b9c547cSRui Paulo 			   " already has channel switching enabled",
2973*5b9c547cSRui Paulo 			   MAC2STR(addr));
2974*5b9c547cSRui Paulo 		return 0;
2975*5b9c547cSRui Paulo 	}
2976*5b9c547cSRui Paulo 
2977*5b9c547cSRui Paulo 	ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr,
2978*5b9c547cSRui Paulo 						oper_class, freq_params);
2979*5b9c547cSRui Paulo 	if (!ret)
2980*5b9c547cSRui Paulo 		peer->chan_switch_enabled = 1;
2981*5b9c547cSRui Paulo 
2982*5b9c547cSRui Paulo 	return ret;
2983*5b9c547cSRui Paulo }
2984*5b9c547cSRui Paulo 
2985*5b9c547cSRui Paulo 
2986*5b9c547cSRui Paulo int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr)
2987*5b9c547cSRui Paulo {
2988*5b9c547cSRui Paulo 	struct wpa_tdls_peer *peer;
2989*5b9c547cSRui Paulo 
2990*5b9c547cSRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
2991*5b9c547cSRui Paulo 		return -1;
2992*5b9c547cSRui Paulo 
2993*5b9c547cSRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
2994*5b9c547cSRui Paulo 		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
2995*5b9c547cSRui Paulo 			break;
2996*5b9c547cSRui Paulo 	}
2997*5b9c547cSRui Paulo 
2998*5b9c547cSRui Paulo 	if (!peer || !peer->chan_switch_enabled) {
2999*5b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for "
3000*5b9c547cSRui Paulo 			   MACSTR, MAC2STR(addr));
3001*5b9c547cSRui Paulo 		return -1;
3002*5b9c547cSRui Paulo 	}
3003*5b9c547cSRui Paulo 
3004*5b9c547cSRui Paulo 	/* ignore the return value */
3005*5b9c547cSRui Paulo 	wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
3006*5b9c547cSRui Paulo 
3007*5b9c547cSRui Paulo 	peer->chan_switch_enabled = 0;
3008*5b9c547cSRui Paulo 	return 0;
3009*5b9c547cSRui Paulo }
3010