xref: /freebsd/contrib/wpa/src/rsn_supp/tdls.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
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"
15325151a3SRui Paulo #include "common/ieee802_11_common.h"
16f05cddf9SRui Paulo #include "crypto/sha256.h"
17f05cddf9SRui Paulo #include "crypto/crypto.h"
18f05cddf9SRui Paulo #include "crypto/aes_wrap.h"
19f05cddf9SRui Paulo #include "rsn_supp/wpa.h"
20f05cddf9SRui Paulo #include "rsn_supp/wpa_ie.h"
21f05cddf9SRui Paulo #include "rsn_supp/wpa_i.h"
22f05cddf9SRui Paulo #include "drivers/driver.h"
23f05cddf9SRui Paulo #include "l2_packet/l2_packet.h"
24f05cddf9SRui Paulo 
25f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
26f05cddf9SRui Paulo #define TDLS_TESTING_LONG_FRAME BIT(0)
27f05cddf9SRui Paulo #define TDLS_TESTING_ALT_RSN_IE BIT(1)
28f05cddf9SRui Paulo #define TDLS_TESTING_DIFF_BSSID BIT(2)
29f05cddf9SRui Paulo #define TDLS_TESTING_SHORT_LIFETIME BIT(3)
30f05cddf9SRui Paulo #define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4)
31f05cddf9SRui Paulo #define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5)
32f05cddf9SRui Paulo #define TDLS_TESTING_LONG_LIFETIME BIT(6)
33f05cddf9SRui Paulo #define TDLS_TESTING_CONCURRENT_INIT BIT(7)
34f05cddf9SRui Paulo #define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8)
35f05cddf9SRui Paulo #define TDLS_TESTING_DECLINE_RESP BIT(9)
36f05cddf9SRui Paulo #define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
375b9c547cSRui Paulo #define TDLS_TESTING_WRONG_MIC BIT(11)
3885732ac8SCy Schubert #define TDLS_TESTING_DOUBLE_TPK_M2 BIT(12)
39f05cddf9SRui Paulo unsigned int tdls_testing = 0;
40f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
41f05cddf9SRui Paulo 
42f05cddf9SRui Paulo #define TPK_LIFETIME 43200 /* 12 hours */
435b9c547cSRui Paulo #define TPK_M1_RETRY_COUNT 3
445b9c547cSRui Paulo #define TPK_M1_TIMEOUT 5000 /* in milliseconds */
455b9c547cSRui Paulo #define TPK_M2_RETRY_COUNT 10
465b9c547cSRui Paulo #define TPK_M2_TIMEOUT 500 /* in milliseconds */
47f05cddf9SRui Paulo 
48f05cddf9SRui Paulo #define TDLS_MIC_LEN		16
49f05cddf9SRui Paulo 
50f05cddf9SRui Paulo #define TDLS_TIMEOUT_LEN	4
51f05cddf9SRui Paulo 
52f05cddf9SRui Paulo struct wpa_tdls_ftie {
53f05cddf9SRui Paulo 	u8 ie_type; /* FTIE */
54f05cddf9SRui Paulo 	u8 ie_len;
55f05cddf9SRui Paulo 	u8 mic_ctrl[2];
56f05cddf9SRui Paulo 	u8 mic[TDLS_MIC_LEN];
57f05cddf9SRui Paulo 	u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */
58f05cddf9SRui Paulo 	u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */
59f05cddf9SRui Paulo 	/* followed by optional elements */
60f05cddf9SRui Paulo } STRUCT_PACKED;
61f05cddf9SRui Paulo 
62f05cddf9SRui Paulo struct wpa_tdls_timeoutie {
63f05cddf9SRui Paulo 	u8 ie_type; /* Timeout IE */
64f05cddf9SRui Paulo 	u8 ie_len;
65f05cddf9SRui Paulo 	u8 interval_type;
66f05cddf9SRui Paulo 	u8 value[TDLS_TIMEOUT_LEN];
67f05cddf9SRui Paulo } STRUCT_PACKED;
68f05cddf9SRui Paulo 
69f05cddf9SRui Paulo struct wpa_tdls_lnkid {
70f05cddf9SRui Paulo 	u8 ie_type; /* Link Identifier IE */
71f05cddf9SRui Paulo 	u8 ie_len;
72f05cddf9SRui Paulo 	u8 bssid[ETH_ALEN];
73f05cddf9SRui Paulo 	u8 init_sta[ETH_ALEN];
74f05cddf9SRui Paulo 	u8 resp_sta[ETH_ALEN];
75f05cddf9SRui Paulo } STRUCT_PACKED;
76f05cddf9SRui Paulo 
77f05cddf9SRui Paulo /* TDLS frame headers as per IEEE Std 802.11z-2010 */
78f05cddf9SRui Paulo struct wpa_tdls_frame {
79f05cddf9SRui Paulo 	u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */
80f05cddf9SRui Paulo 	u8 category; /* Category */
81f05cddf9SRui Paulo 	u8 action; /* Action (enum tdls_frame_type) */
82f05cddf9SRui Paulo } STRUCT_PACKED;
83f05cddf9SRui Paulo 
84f05cddf9SRui Paulo static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs);
85f05cddf9SRui Paulo static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx);
86f05cddf9SRui Paulo static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer);
875b9c547cSRui Paulo static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
885b9c547cSRui Paulo 				       struct wpa_tdls_peer *peer);
895b9c547cSRui Paulo static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
905b9c547cSRui Paulo 				  u16 reason_code);
91f05cddf9SRui Paulo 
92f05cddf9SRui Paulo 
93f05cddf9SRui Paulo #define TDLS_MAX_IE_LEN 80
94f05cddf9SRui Paulo #define IEEE80211_MAX_SUPP_RATES 32
95f05cddf9SRui Paulo 
96f05cddf9SRui Paulo struct wpa_tdls_peer {
97f05cddf9SRui Paulo 	struct wpa_tdls_peer *next;
985b9c547cSRui Paulo 	unsigned int reconfig_key:1;
99f05cddf9SRui Paulo 	int initiator; /* whether this end was initiator for TDLS setup */
100f05cddf9SRui Paulo 	u8 addr[ETH_ALEN]; /* other end MAC address */
101f05cddf9SRui Paulo 	u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
102f05cddf9SRui Paulo 	u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */
103f05cddf9SRui Paulo 	u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */
104f05cddf9SRui Paulo 	size_t rsnie_i_len;
105f05cddf9SRui Paulo 	u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */
106f05cddf9SRui Paulo 	size_t rsnie_p_len;
107f05cddf9SRui Paulo 	u32 lifetime;
108f05cddf9SRui Paulo 	int cipher; /* Selected cipher (WPA_CIPHER_*) */
109f05cddf9SRui Paulo 	u8 dtoken;
110f05cddf9SRui Paulo 
111f05cddf9SRui Paulo 	struct tpk {
112f05cddf9SRui Paulo 		u8 kck[16]; /* TPK-KCK */
113f05cddf9SRui Paulo 		u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */
114f05cddf9SRui Paulo 	} tpk;
115f05cddf9SRui Paulo 	int tpk_set;
116a2063804SGordon Tetlow 	int tk_set; /* TPK-TK configured to the driver */
117f05cddf9SRui Paulo 	int tpk_success;
1185b9c547cSRui Paulo 	int tpk_in_progress;
119f05cddf9SRui Paulo 
120f05cddf9SRui Paulo 	struct tpk_timer {
121f05cddf9SRui Paulo 		u8 dest[ETH_ALEN];
122f05cddf9SRui Paulo 		int count;      /* Retry Count */
123f05cddf9SRui Paulo 		int timer;      /* Timeout in milliseconds */
124f05cddf9SRui Paulo 		u8 action_code; /* TDLS frame type */
125f05cddf9SRui Paulo 		u8 dialog_token;
126f05cddf9SRui Paulo 		u16 status_code;
1275b9c547cSRui Paulo 		u32 peer_capab;
128f05cddf9SRui Paulo 		int buf_len;    /* length of TPK message for retransmission */
129f05cddf9SRui Paulo 		u8 *buf;        /* buffer for TPK message */
130f05cddf9SRui Paulo 	} sm_tmr;
131f05cddf9SRui Paulo 
132f05cddf9SRui Paulo 	u16 capability;
133f05cddf9SRui Paulo 
134f05cddf9SRui Paulo 	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
135f05cddf9SRui Paulo 	size_t supp_rates_len;
1365b9c547cSRui Paulo 
1375b9c547cSRui Paulo 	struct ieee80211_ht_capabilities *ht_capabilities;
1385b9c547cSRui Paulo 	struct ieee80211_vht_capabilities *vht_capabilities;
139c1d255d3SCy Schubert 	struct ieee80211_he_capabilities *he_capabilities;
140c1d255d3SCy Schubert 	size_t he_capab_len;
1414b72b91aSCy Schubert 	struct ieee80211_he_6ghz_band_cap *he_6ghz_band_capabilities;
142*a90b9d01SCy Schubert 	struct ieee80211_eht_capabilities *eht_capabilities;
143*a90b9d01SCy Schubert 	size_t eht_capab_len;
1445b9c547cSRui Paulo 
1455b9c547cSRui Paulo 	u8 qos_info;
1465b9c547cSRui Paulo 
1475b9c547cSRui Paulo 	u16 aid;
1485b9c547cSRui Paulo 
1495b9c547cSRui Paulo 	u8 *ext_capab;
1505b9c547cSRui Paulo 	size_t ext_capab_len;
1515b9c547cSRui Paulo 
1525b9c547cSRui Paulo 	u8 *supp_channels;
1535b9c547cSRui Paulo 	size_t supp_channels_len;
1545b9c547cSRui Paulo 
1555b9c547cSRui Paulo 	u8 *supp_oper_classes;
1565b9c547cSRui Paulo 	size_t supp_oper_classes_len;
1575b9c547cSRui Paulo 
1585b9c547cSRui Paulo 	u8 wmm_capable;
1595b9c547cSRui Paulo 
1605b9c547cSRui Paulo 	/* channel switch currently enabled */
1615b9c547cSRui Paulo 	int chan_switch_enabled;
162*a90b9d01SCy Schubert 
163*a90b9d01SCy Schubert 	int mld_link_id;
164*a90b9d01SCy Schubert 	bool disc_resp_rcvd;
165*a90b9d01SCy Schubert 	bool setup_req_rcvd;
166f05cddf9SRui Paulo };
167f05cddf9SRui Paulo 
168f05cddf9SRui Paulo 
wpa_tdls_get_link_bssid(struct wpa_sm * sm,int link_id)169*a90b9d01SCy Schubert static const u8 * wpa_tdls_get_link_bssid(struct wpa_sm *sm, int link_id)
170*a90b9d01SCy Schubert {
171*a90b9d01SCy Schubert 	if (link_id >= 0)
172*a90b9d01SCy Schubert 		return sm->mlo.links[link_id].bssid;
173*a90b9d01SCy Schubert 	return sm->bssid;
174*a90b9d01SCy Schubert }
175*a90b9d01SCy Schubert 
176*a90b9d01SCy Schubert 
wpa_tdls_get_privacy(struct wpa_sm * sm)177f05cddf9SRui Paulo static int wpa_tdls_get_privacy(struct wpa_sm *sm)
178f05cddf9SRui Paulo {
179f05cddf9SRui Paulo 	/*
180f05cddf9SRui Paulo 	 * Get info needed from supplicant to check if the current BSS supports
181f05cddf9SRui Paulo 	 * security. Other than OPEN mode, rest are considered secured
182f05cddf9SRui Paulo 	 * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake.
183f05cddf9SRui Paulo 	 */
184f05cddf9SRui Paulo 	return sm->pairwise_cipher != WPA_CIPHER_NONE;
185f05cddf9SRui Paulo }
186f05cddf9SRui Paulo 
187f05cddf9SRui Paulo 
wpa_add_ie(u8 * pos,const u8 * ie,size_t ie_len)188f05cddf9SRui Paulo static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
189f05cddf9SRui Paulo {
190f05cddf9SRui Paulo 	os_memcpy(pos, ie, ie_len);
191f05cddf9SRui Paulo 	return pos + ie_len;
192f05cddf9SRui Paulo }
193f05cddf9SRui Paulo 
194f05cddf9SRui Paulo 
wpa_tdls_del_key(struct wpa_sm * sm,struct wpa_tdls_peer * peer)195f05cddf9SRui Paulo static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
196f05cddf9SRui Paulo {
197*a90b9d01SCy Schubert 	if (wpa_sm_set_key(sm, -1, WPA_ALG_NONE, peer->addr,
198c1d255d3SCy Schubert 			   0, 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE) < 0) {
199f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
200f05cddf9SRui Paulo 			   "the driver");
201f05cddf9SRui Paulo 		return -1;
202f05cddf9SRui Paulo 	}
203f05cddf9SRui Paulo 
204f05cddf9SRui Paulo 	return 0;
205f05cddf9SRui Paulo }
206f05cddf9SRui Paulo 
207f05cddf9SRui Paulo 
wpa_tdls_set_key(struct wpa_sm * sm,struct wpa_tdls_peer * peer)208f05cddf9SRui Paulo static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
209f05cddf9SRui Paulo {
210f05cddf9SRui Paulo 	u8 key_len;
211f05cddf9SRui Paulo 	u8 rsc[6];
212f05cddf9SRui Paulo 	enum wpa_alg alg;
213f05cddf9SRui Paulo 
214a2063804SGordon Tetlow 	if (peer->tk_set) {
215a2063804SGordon Tetlow 		/*
216a2063804SGordon Tetlow 		 * This same TPK-TK has already been configured to the driver
217a2063804SGordon Tetlow 		 * and this new configuration attempt (likely due to an
218a2063804SGordon Tetlow 		 * unexpected retransmitted frame) would result in clearing
219a2063804SGordon Tetlow 		 * the TX/RX sequence number which can break security, so must
220a2063804SGordon Tetlow 		 * not allow that to happen.
221a2063804SGordon Tetlow 		 */
222a2063804SGordon Tetlow 		wpa_printf(MSG_INFO, "TDLS: TPK-TK for the peer " MACSTR
223a2063804SGordon Tetlow 			   " has already been configured to the driver - do not reconfigure",
224a2063804SGordon Tetlow 			   MAC2STR(peer->addr));
225a2063804SGordon Tetlow 		return -1;
226a2063804SGordon Tetlow 	}
227a2063804SGordon Tetlow 
228f05cddf9SRui Paulo 	os_memset(rsc, 0, 6);
229f05cddf9SRui Paulo 
230f05cddf9SRui Paulo 	switch (peer->cipher) {
231f05cddf9SRui Paulo 	case WPA_CIPHER_CCMP:
232f05cddf9SRui Paulo 		alg = WPA_ALG_CCMP;
233f05cddf9SRui Paulo 		key_len = 16;
234f05cddf9SRui Paulo 		break;
235f05cddf9SRui Paulo 	case WPA_CIPHER_NONE:
236f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: "
237f05cddf9SRui Paulo 			   "NONE - do not use pairwise keys");
238f05cddf9SRui Paulo 		return -1;
239f05cddf9SRui Paulo 	default:
240f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d",
241f05cddf9SRui Paulo 			   sm->pairwise_cipher);
242f05cddf9SRui Paulo 		return -1;
243f05cddf9SRui Paulo 	}
244f05cddf9SRui Paulo 
245a2063804SGordon Tetlow 	wpa_printf(MSG_DEBUG, "TDLS: Configure pairwise key for peer " MACSTR,
246a2063804SGordon Tetlow 		   MAC2STR(peer->addr));
247*a90b9d01SCy Schubert 	if (wpa_sm_set_key(sm, -1, alg, peer->addr, 0, 1, rsc, sizeof(rsc),
248c1d255d3SCy Schubert 			   peer->tpk.tk, key_len,
249c1d255d3SCy Schubert 			   KEY_FLAG_PAIRWISE_RX_TX) < 0) {
250f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
251f05cddf9SRui Paulo 			   "driver");
252f05cddf9SRui Paulo 		return -1;
253f05cddf9SRui Paulo 	}
254a2063804SGordon Tetlow 	peer->tk_set = 1;
255f05cddf9SRui Paulo 	return 0;
256f05cddf9SRui Paulo }
257f05cddf9SRui Paulo 
258f05cddf9SRui Paulo 
wpa_tdls_send_tpk_msg(struct wpa_sm * sm,const u8 * dst,u8 action_code,u8 dialog_token,u16 status_code,u32 peer_capab,int initiator,const u8 * buf,size_t len,int link_id)259f05cddf9SRui Paulo static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
260f05cddf9SRui Paulo 				 u8 action_code, u8 dialog_token,
2615b9c547cSRui Paulo 				 u16 status_code, u32 peer_capab,
262*a90b9d01SCy Schubert 				 int initiator, const u8 *buf, size_t len,
263*a90b9d01SCy Schubert 				 int link_id)
264f05cddf9SRui Paulo {
265f05cddf9SRui Paulo 	return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
2665b9c547cSRui Paulo 				     status_code, peer_capab, initiator, buf,
267*a90b9d01SCy Schubert 				     len, link_id);
268f05cddf9SRui Paulo }
269f05cddf9SRui Paulo 
270f05cddf9SRui Paulo 
wpa_tdls_tpk_send(struct wpa_sm * sm,const u8 * dest,u8 action_code,u8 dialog_token,u16 status_code,u32 peer_capab,int initiator,const u8 * msg,size_t msg_len,int link_id)271f05cddf9SRui Paulo static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
2725b9c547cSRui Paulo 			     u8 dialog_token, u16 status_code, u32 peer_capab,
273*a90b9d01SCy Schubert 			     int initiator, const u8 *msg, size_t msg_len,
274*a90b9d01SCy Schubert 			     int link_id)
275f05cddf9SRui Paulo {
276f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
277f05cddf9SRui Paulo 
278f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u "
2795b9c547cSRui Paulo 		   "dialog_token=%u status_code=%u peer_capab=%u initiator=%d "
2805b9c547cSRui Paulo 		   "msg_len=%u",
281f05cddf9SRui Paulo 		   MAC2STR(dest), action_code, dialog_token, status_code,
2825b9c547cSRui Paulo 		   peer_capab, initiator, (unsigned int) msg_len);
283f05cddf9SRui Paulo 
284f05cddf9SRui Paulo 	if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
2855b9c547cSRui Paulo 				  status_code, peer_capab, initiator, msg,
286*a90b9d01SCy Schubert 				  msg_len, link_id)) {
287f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Failed to send message "
288f05cddf9SRui Paulo 			   "(action_code=%u)", action_code);
289f05cddf9SRui Paulo 		return -1;
290f05cddf9SRui Paulo 	}
291f05cddf9SRui Paulo 
292f05cddf9SRui Paulo 	if (action_code == WLAN_TDLS_SETUP_CONFIRM ||
293f05cddf9SRui Paulo 	    action_code == WLAN_TDLS_TEARDOWN ||
294f05cddf9SRui Paulo 	    action_code == WLAN_TDLS_DISCOVERY_REQUEST ||
295f05cddf9SRui Paulo 	    action_code == WLAN_TDLS_DISCOVERY_RESPONSE)
296f05cddf9SRui Paulo 		return 0; /* No retries */
297f05cddf9SRui Paulo 
298f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
299*a90b9d01SCy Schubert 		if (ether_addr_equal(peer->addr, dest))
300f05cddf9SRui Paulo 			break;
301f05cddf9SRui Paulo 	}
302f05cddf9SRui Paulo 
303f05cddf9SRui Paulo 	if (peer == NULL) {
304f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
305f05cddf9SRui Paulo 			   "retry " MACSTR, MAC2STR(dest));
306f05cddf9SRui Paulo 		return 0;
307f05cddf9SRui Paulo 	}
308f05cddf9SRui Paulo 
309f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
310f05cddf9SRui Paulo 
3115b9c547cSRui Paulo 	if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
3125b9c547cSRui Paulo 		peer->sm_tmr.count = TPK_M2_RETRY_COUNT;
3135b9c547cSRui Paulo 		peer->sm_tmr.timer = TPK_M2_TIMEOUT;
3145b9c547cSRui Paulo 	} else {
3155b9c547cSRui Paulo 		peer->sm_tmr.count = TPK_M1_RETRY_COUNT;
3165b9c547cSRui Paulo 		peer->sm_tmr.timer = TPK_M1_TIMEOUT;
3175b9c547cSRui Paulo 	}
318f05cddf9SRui Paulo 
319f05cddf9SRui Paulo 	/* Copy message to resend on timeout */
320f05cddf9SRui Paulo 	os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN);
321f05cddf9SRui Paulo 	peer->sm_tmr.action_code = action_code;
322f05cddf9SRui Paulo 	peer->sm_tmr.dialog_token = dialog_token;
323f05cddf9SRui Paulo 	peer->sm_tmr.status_code = status_code;
3245b9c547cSRui Paulo 	peer->sm_tmr.peer_capab = peer_capab;
325f05cddf9SRui Paulo 	peer->sm_tmr.buf_len = msg_len;
326f05cddf9SRui Paulo 	os_free(peer->sm_tmr.buf);
32785732ac8SCy Schubert 	peer->sm_tmr.buf = os_memdup(msg, msg_len);
328f05cddf9SRui Paulo 	if (peer->sm_tmr.buf == NULL)
329f05cddf9SRui Paulo 		return -1;
330f05cddf9SRui Paulo 
331f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
332f05cddf9SRui Paulo 		   "(action_code=%u)", action_code);
3335b9c547cSRui Paulo 	eloop_register_timeout(peer->sm_tmr.timer / 1000,
3345b9c547cSRui Paulo 			       (peer->sm_tmr.timer % 1000) * 1000,
335f05cddf9SRui Paulo 			       wpa_tdls_tpk_retry_timeout, sm, peer);
336f05cddf9SRui Paulo 	return 0;
337f05cddf9SRui Paulo }
338f05cddf9SRui Paulo 
339f05cddf9SRui Paulo 
wpa_tdls_do_teardown(struct wpa_sm * sm,struct wpa_tdls_peer * peer,u16 reason_code)340f05cddf9SRui Paulo static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
3415b9c547cSRui Paulo 				u16 reason_code)
342f05cddf9SRui Paulo {
343f05cddf9SRui Paulo 	int ret;
344f05cddf9SRui Paulo 
345f05cddf9SRui Paulo 	ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code);
346f05cddf9SRui Paulo 	/* disable the link after teardown was sent */
3475b9c547cSRui Paulo 	wpa_tdls_disable_peer_link(sm, peer);
348f05cddf9SRui Paulo 
349f05cddf9SRui Paulo 	return ret;
350f05cddf9SRui Paulo }
351f05cddf9SRui Paulo 
352f05cddf9SRui Paulo 
wpa_tdls_tpk_retry_timeout(void * eloop_ctx,void * timeout_ctx)353f05cddf9SRui Paulo static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx)
354f05cddf9SRui Paulo {
355f05cddf9SRui Paulo 
356f05cddf9SRui Paulo 	struct wpa_sm *sm = eloop_ctx;
357f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer = timeout_ctx;
358f05cddf9SRui Paulo 
359f05cddf9SRui Paulo 	if (peer->sm_tmr.count) {
360f05cddf9SRui Paulo 		peer->sm_tmr.count--;
361f05cddf9SRui Paulo 
362f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Retrying sending of message "
363f05cddf9SRui Paulo 			   "(action_code=%u)",
364f05cddf9SRui Paulo 			   peer->sm_tmr.action_code);
365f05cddf9SRui Paulo 
366f05cddf9SRui Paulo 		if (peer->sm_tmr.buf == NULL) {
367f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: No retry buffer available "
368f05cddf9SRui Paulo 				   "for action_code=%u",
369f05cddf9SRui Paulo 				   peer->sm_tmr.action_code);
370f05cddf9SRui Paulo 			eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm,
371f05cddf9SRui Paulo 					     peer);
372f05cddf9SRui Paulo 			return;
373f05cddf9SRui Paulo 		}
374f05cddf9SRui Paulo 
375f05cddf9SRui Paulo 		/* resend TPK Handshake Message to Peer */
376f05cddf9SRui Paulo 		if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest,
377f05cddf9SRui Paulo 					  peer->sm_tmr.action_code,
378f05cddf9SRui Paulo 					  peer->sm_tmr.dialog_token,
379f05cddf9SRui Paulo 					  peer->sm_tmr.status_code,
3805b9c547cSRui Paulo 					  peer->sm_tmr.peer_capab,
3815b9c547cSRui Paulo 					  peer->initiator,
382f05cddf9SRui Paulo 					  peer->sm_tmr.buf,
383*a90b9d01SCy Schubert 					  peer->sm_tmr.buf_len, -1)) {
384f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: Failed to retry "
385f05cddf9SRui Paulo 				   "transmission");
386f05cddf9SRui Paulo 		}
387f05cddf9SRui Paulo 
388f05cddf9SRui Paulo 		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
3895b9c547cSRui Paulo 		eloop_register_timeout(peer->sm_tmr.timer / 1000,
3905b9c547cSRui Paulo 				       (peer->sm_tmr.timer % 1000) * 1000,
391f05cddf9SRui Paulo 				       wpa_tdls_tpk_retry_timeout, sm, peer);
392f05cddf9SRui Paulo 	} else {
393f05cddf9SRui Paulo 		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
394f05cddf9SRui Paulo 
395f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request");
396f05cddf9SRui Paulo 		wpa_tdls_do_teardown(sm, peer,
3975b9c547cSRui Paulo 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
398f05cddf9SRui Paulo 	}
399f05cddf9SRui Paulo }
400f05cddf9SRui Paulo 
401f05cddf9SRui Paulo 
wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm * sm,struct wpa_tdls_peer * peer,u8 action_code)402f05cddf9SRui Paulo static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm,
403f05cddf9SRui Paulo 					      struct wpa_tdls_peer *peer,
404f05cddf9SRui Paulo 					      u8 action_code)
405f05cddf9SRui Paulo {
406f05cddf9SRui Paulo 	if (action_code == peer->sm_tmr.action_code) {
407f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for "
408f05cddf9SRui Paulo 			   "action_code=%u", action_code);
409f05cddf9SRui Paulo 
410f05cddf9SRui Paulo 		/* Cancel Timeout registered */
411f05cddf9SRui Paulo 		eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
412f05cddf9SRui Paulo 
413f05cddf9SRui Paulo 		/* free all resources meant for retry */
414f05cddf9SRui Paulo 		os_free(peer->sm_tmr.buf);
415f05cddf9SRui Paulo 		peer->sm_tmr.buf = NULL;
416f05cddf9SRui Paulo 
417f05cddf9SRui Paulo 		peer->sm_tmr.count = 0;
418f05cddf9SRui Paulo 		peer->sm_tmr.timer = 0;
419f05cddf9SRui Paulo 		peer->sm_tmr.buf_len = 0;
420f05cddf9SRui Paulo 		peer->sm_tmr.action_code = 0xff;
421f05cddf9SRui Paulo 	} else {
422f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout "
423f05cddf9SRui Paulo 			   "(Unknown action_code=%u)", action_code);
424f05cddf9SRui Paulo 	}
425f05cddf9SRui Paulo }
426f05cddf9SRui Paulo 
427f05cddf9SRui Paulo 
wpa_tdls_generate_tpk(struct wpa_tdls_peer * peer,const u8 * own_addr,const u8 * bssid)428f05cddf9SRui Paulo static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer,
429f05cddf9SRui Paulo 				  const u8 *own_addr, const u8 *bssid)
430f05cddf9SRui Paulo {
431f05cddf9SRui Paulo 	u8 key_input[SHA256_MAC_LEN];
432f05cddf9SRui Paulo 	const u8 *nonce[2];
433f05cddf9SRui Paulo 	size_t len[2];
434f05cddf9SRui Paulo 	u8 data[3 * ETH_ALEN];
435f05cddf9SRui Paulo 
43685732ac8SCy Schubert 	/* IEEE Std 802.11-2016 12.7.9.2:
43785732ac8SCy Schubert 	 * TPK-Key-Input = Hash(min(SNonce, ANonce) || max(SNonce, ANonce))
43885732ac8SCy Schubert 	 * Hash = SHA-256 for TDLS
439f05cddf9SRui Paulo 	 */
440f05cddf9SRui Paulo 	len[0] = WPA_NONCE_LEN;
441f05cddf9SRui Paulo 	len[1] = WPA_NONCE_LEN;
442f05cddf9SRui Paulo 	if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) {
443f05cddf9SRui Paulo 		nonce[0] = peer->inonce;
444f05cddf9SRui Paulo 		nonce[1] = peer->rnonce;
445f05cddf9SRui Paulo 	} else {
446f05cddf9SRui Paulo 		nonce[0] = peer->rnonce;
447f05cddf9SRui Paulo 		nonce[1] = peer->inonce;
448f05cddf9SRui Paulo 	}
449f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN);
450f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN);
451f05cddf9SRui Paulo 	sha256_vector(2, nonce, len, key_input);
452f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
453f05cddf9SRui Paulo 			key_input, SHA256_MAC_LEN);
454f05cddf9SRui Paulo 
455f05cddf9SRui Paulo 	/*
45685732ac8SCy Schubert 	 * TPK = KDF-Hash-Length(TPK-Key-Input, "TDLS PMK",
45785732ac8SCy Schubert 	 *	min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID)
458f05cddf9SRui Paulo 	 */
459f05cddf9SRui Paulo 
460f05cddf9SRui Paulo 	if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
461f05cddf9SRui Paulo 		os_memcpy(data, own_addr, ETH_ALEN);
462f05cddf9SRui Paulo 		os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN);
463f05cddf9SRui Paulo 	} else {
464f05cddf9SRui Paulo 		os_memcpy(data, peer->addr, ETH_ALEN);
465f05cddf9SRui Paulo 		os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN);
466f05cddf9SRui Paulo 	}
467f05cddf9SRui Paulo 	os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
468f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
469f05cddf9SRui Paulo 
470f05cddf9SRui Paulo 	sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
471f05cddf9SRui Paulo 		   (u8 *) &peer->tpk, sizeof(peer->tpk));
472f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
473f05cddf9SRui Paulo 			peer->tpk.kck, sizeof(peer->tpk.kck));
474f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
475f05cddf9SRui Paulo 			peer->tpk.tk, sizeof(peer->tpk.tk));
476f05cddf9SRui Paulo 	peer->tpk_set = 1;
477f05cddf9SRui Paulo }
478f05cddf9SRui Paulo 
479f05cddf9SRui Paulo 
480f05cddf9SRui Paulo /**
481f05cddf9SRui Paulo  * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
482f05cddf9SRui Paulo  * @kck: TPK-KCK
483f05cddf9SRui Paulo  * @lnkid: Pointer to the beginning of Link Identifier IE
484*a90b9d01SCy Schubert  * @rsne: Pointer to the beginning of RSNE used for handshake
485*a90b9d01SCy Schubert  * @rsne_len: Length of RSNE in octets
486f05cddf9SRui Paulo  * @timeoutie: Pointer to the beginning of Timeout IE used for handshake
487*a90b9d01SCy Schubert  * @fte: Pointer to the beginning of FTE
488*a90b9d01SCy Schubert  * @fre_len: Length of FTE in octets
489f05cddf9SRui Paulo  * @mic: Pointer for writing MIC
490f05cddf9SRui Paulo  *
491f05cddf9SRui Paulo  * Calculate MIC for TDLS frame.
492f05cddf9SRui Paulo  */
wpa_tdls_ftie_mic(const u8 * kck,u8 trans_seq,const u8 * lnkid,const u8 * rsne,size_t rsne_len,const u8 * timeoutie,const u8 * fte,size_t fte_len,u8 * mic)493f05cddf9SRui Paulo static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
494*a90b9d01SCy Schubert 			     const u8 *rsne, size_t rsne_len,
495*a90b9d01SCy Schubert 			     const u8 *timeoutie,
496*a90b9d01SCy Schubert 			     const u8 *fte, size_t fte_len, u8 *mic)
497f05cddf9SRui Paulo {
498f05cddf9SRui Paulo 	u8 *buf, *pos;
499f05cddf9SRui Paulo 	struct wpa_tdls_ftie *_ftie;
500f05cddf9SRui Paulo 	const struct wpa_tdls_lnkid *_lnkid;
501f05cddf9SRui Paulo 	int ret;
502*a90b9d01SCy Schubert 	int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + rsne_len +
503*a90b9d01SCy Schubert 		2 + timeoutie[1] + fte_len;
504f05cddf9SRui Paulo 	buf = os_zalloc(len);
505f05cddf9SRui Paulo 	if (!buf) {
506f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
507f05cddf9SRui Paulo 		return -1;
508f05cddf9SRui Paulo 	}
509f05cddf9SRui Paulo 
510f05cddf9SRui Paulo 	pos = buf;
511f05cddf9SRui Paulo 	_lnkid = (const struct wpa_tdls_lnkid *) lnkid;
512f05cddf9SRui Paulo 	/* 1) TDLS initiator STA MAC address */
513f05cddf9SRui Paulo 	os_memcpy(pos, _lnkid->init_sta, ETH_ALEN);
514f05cddf9SRui Paulo 	pos += ETH_ALEN;
515f05cddf9SRui Paulo 	/* 2) TDLS responder STA MAC address */
516f05cddf9SRui Paulo 	os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN);
517f05cddf9SRui Paulo 	pos += ETH_ALEN;
518f05cddf9SRui Paulo 	/* 3) Transaction Sequence number */
519f05cddf9SRui Paulo 	*pos++ = trans_seq;
520f05cddf9SRui Paulo 	/* 4) Link Identifier IE */
521f05cddf9SRui Paulo 	os_memcpy(pos, lnkid, 2 + lnkid[1]);
522f05cddf9SRui Paulo 	pos += 2 + lnkid[1];
523f05cddf9SRui Paulo 	/* 5) RSN IE */
524*a90b9d01SCy Schubert 	os_memcpy(pos, rsne, rsne_len);
525*a90b9d01SCy Schubert 	pos += rsne_len;
526f05cddf9SRui Paulo 	/* 6) Timeout Interval IE */
527f05cddf9SRui Paulo 	os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
528f05cddf9SRui Paulo 	pos += 2 + timeoutie[1];
529f05cddf9SRui Paulo 	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
530*a90b9d01SCy Schubert 	os_memcpy(pos, fte, fte_len);
531f05cddf9SRui Paulo 	_ftie = (struct wpa_tdls_ftie *) pos;
532f05cddf9SRui Paulo 	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
533*a90b9d01SCy Schubert 	pos += fte_len;
534f05cddf9SRui Paulo 
535f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
536f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
537f05cddf9SRui Paulo 	ret = omac1_aes_128(kck, buf, pos - buf, mic);
538f05cddf9SRui Paulo 	os_free(buf);
539f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
540f05cddf9SRui Paulo 	return ret;
541f05cddf9SRui Paulo }
542f05cddf9SRui Paulo 
543f05cddf9SRui Paulo 
544f05cddf9SRui Paulo /**
545f05cddf9SRui Paulo  * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame
546f05cddf9SRui Paulo  * @kck: TPK-KCK
547f05cddf9SRui Paulo  * @trans_seq: Transaction Sequence Number (4 - Teardown)
548f05cddf9SRui Paulo  * @rcode: Reason code for Teardown
549f05cddf9SRui Paulo  * @dtoken: Dialog Token used for that particular link
550f05cddf9SRui Paulo  * @lnkid: Pointer to the beginning of Link Identifier IE
551*a90b9d01SCy Schubert  * @fte: Pointer to the beginning of FTE
552*a90b9d01SCy Schubert  * @fre_len: Length of FTE in octets
553f05cddf9SRui Paulo  * @mic: Pointer for writing MIC
554f05cddf9SRui Paulo  *
555f05cddf9SRui Paulo  * Calculate MIC for TDLS frame.
556f05cddf9SRui Paulo  */
wpa_tdls_key_mic_teardown(const u8 * kck,u8 trans_seq,u16 rcode,u8 dtoken,const u8 * lnkid,const u8 * fte,size_t fte_len,u8 * mic)557f05cddf9SRui Paulo static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
558f05cddf9SRui Paulo 				     u8 dtoken, const u8 *lnkid,
559*a90b9d01SCy Schubert 				     const u8 *fte, size_t fte_len, u8 *mic)
560f05cddf9SRui Paulo {
561f05cddf9SRui Paulo 	u8 *buf, *pos;
562f05cddf9SRui Paulo 	struct wpa_tdls_ftie *_ftie;
563f05cddf9SRui Paulo 	int ret;
564f05cddf9SRui Paulo 	int len;
565f05cddf9SRui Paulo 
566f05cddf9SRui Paulo 	if (lnkid == NULL)
567f05cddf9SRui Paulo 		return -1;
568f05cddf9SRui Paulo 
569f05cddf9SRui Paulo 	len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
570*a90b9d01SCy Schubert 		sizeof(trans_seq) + fte_len;
571f05cddf9SRui Paulo 
572f05cddf9SRui Paulo 	buf = os_zalloc(len);
573f05cddf9SRui Paulo 	if (!buf) {
574f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
575f05cddf9SRui Paulo 		return -1;
576f05cddf9SRui Paulo 	}
577f05cddf9SRui Paulo 
578f05cddf9SRui Paulo 	pos = buf;
579f05cddf9SRui Paulo 	/* 1) Link Identifier IE */
580f05cddf9SRui Paulo 	os_memcpy(pos, lnkid, 2 + lnkid[1]);
581f05cddf9SRui Paulo 	pos += 2 + lnkid[1];
582f05cddf9SRui Paulo 	/* 2) Reason Code */
583f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, rcode);
584f05cddf9SRui Paulo 	pos += sizeof(rcode);
585f05cddf9SRui Paulo 	/* 3) Dialog token */
586f05cddf9SRui Paulo 	*pos++ = dtoken;
587f05cddf9SRui Paulo 	/* 4) Transaction Sequence number */
588f05cddf9SRui Paulo 	*pos++ = trans_seq;
589f05cddf9SRui Paulo 	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
590*a90b9d01SCy Schubert 	os_memcpy(pos, fte, fte_len);
591f05cddf9SRui Paulo 	_ftie = (struct wpa_tdls_ftie *) pos;
592f05cddf9SRui Paulo 	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
593*a90b9d01SCy Schubert 	pos += fte_len;
594f05cddf9SRui Paulo 
595f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
596f05cddf9SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
597f05cddf9SRui Paulo 	ret = omac1_aes_128(kck, buf, pos - buf, mic);
598f05cddf9SRui Paulo 	os_free(buf);
599f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
600f05cddf9SRui Paulo 	return ret;
601f05cddf9SRui Paulo }
602f05cddf9SRui Paulo 
603f05cddf9SRui Paulo 
wpa_supplicant_verify_tdls_mic(u8 trans_seq,struct wpa_tdls_peer * peer,const u8 * lnkid,const u8 * timeoutie,const struct wpa_tdls_ftie * ftie,size_t fte_len)604f05cddf9SRui Paulo static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
605f05cddf9SRui Paulo 					  struct wpa_tdls_peer *peer,
606f05cddf9SRui Paulo 					  const u8 *lnkid, const u8 *timeoutie,
607*a90b9d01SCy Schubert 					  const struct wpa_tdls_ftie *ftie,
608*a90b9d01SCy Schubert 					  size_t fte_len)
609f05cddf9SRui Paulo {
610f05cddf9SRui Paulo 	u8 mic[16];
611f05cddf9SRui Paulo 
612f05cddf9SRui Paulo 	if (peer->tpk_set) {
613f05cddf9SRui Paulo 		wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
614*a90b9d01SCy Schubert 				  peer->rsnie_p, peer->rsnie_p_len, timeoutie,
615*a90b9d01SCy Schubert 				  (const u8 *) ftie, fte_len, mic);
6165b9c547cSRui Paulo 		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
617f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
618f05cddf9SRui Paulo 				   "dropping packet");
619f05cddf9SRui Paulo 			wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC",
620f05cddf9SRui Paulo 				    ftie->mic, 16);
621f05cddf9SRui Paulo 			wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC",
622f05cddf9SRui Paulo 				    mic, 16);
623f05cddf9SRui Paulo 			return -1;
624f05cddf9SRui Paulo 		}
625f05cddf9SRui Paulo 	} else {
626f05cddf9SRui Paulo 		wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, "
627f05cddf9SRui Paulo 			   "TPK not set - dropping packet");
628f05cddf9SRui Paulo 		return -1;
629f05cddf9SRui Paulo 	}
630f05cddf9SRui Paulo 	return 0;
631f05cddf9SRui Paulo }
632f05cddf9SRui Paulo 
633f05cddf9SRui Paulo 
wpa_supplicant_verify_tdls_mic_teardown(u8 trans_seq,u16 rcode,u8 dtoken,struct wpa_tdls_peer * peer,const u8 * lnkid,const struct wpa_tdls_ftie * ftie,size_t fte_len)634f05cddf9SRui Paulo static int wpa_supplicant_verify_tdls_mic_teardown(
635f05cddf9SRui Paulo 	u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
636*a90b9d01SCy Schubert 	const u8 *lnkid, const struct wpa_tdls_ftie *ftie, size_t fte_len)
637f05cddf9SRui Paulo {
638f05cddf9SRui Paulo 	u8 mic[16];
639f05cddf9SRui Paulo 
640f05cddf9SRui Paulo 	if (peer->tpk_set) {
641f05cddf9SRui Paulo 		wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
642*a90b9d01SCy Schubert 					  dtoken, lnkid, (const u8 *) ftie,
643*a90b9d01SCy Schubert 					  fte_len, mic);
6445b9c547cSRui Paulo 		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
645f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
646f05cddf9SRui Paulo 				   "dropping packet");
647f05cddf9SRui Paulo 			return -1;
648f05cddf9SRui Paulo 		}
649f05cddf9SRui Paulo 	} else {
650f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown "
651f05cddf9SRui Paulo 			   "MIC, TPK not set - dropping packet");
652f05cddf9SRui Paulo 		return -1;
653f05cddf9SRui Paulo 	}
654f05cddf9SRui Paulo 	return 0;
655f05cddf9SRui Paulo }
656f05cddf9SRui Paulo 
657f05cddf9SRui Paulo 
wpa_tdls_tpk_timeout(void * eloop_ctx,void * timeout_ctx)658f05cddf9SRui Paulo static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx)
659f05cddf9SRui Paulo {
660f05cddf9SRui Paulo 	struct wpa_sm *sm = eloop_ctx;
661f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer = timeout_ctx;
662f05cddf9SRui Paulo 
663f05cddf9SRui Paulo 	/*
664f05cddf9SRui Paulo 	 * On TPK lifetime expiration, we have an option of either tearing down
665f05cddf9SRui Paulo 	 * the direct link or trying to re-initiate it. The selection of what
666f05cddf9SRui Paulo 	 * to do is not strictly speaking controlled by our role in the expired
667f05cddf9SRui Paulo 	 * link, but for now, use that to select whether to renew or tear down
668f05cddf9SRui Paulo 	 * the link.
669f05cddf9SRui Paulo 	 */
670f05cddf9SRui Paulo 
671f05cddf9SRui Paulo 	if (peer->initiator) {
672780fb4a2SCy Schubert 		u8 addr[ETH_ALEN];
673780fb4a2SCy Schubert 
674f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
675f05cddf9SRui Paulo 			   " - try to renew", MAC2STR(peer->addr));
676780fb4a2SCy Schubert 		/* cache the peer address before do_teardown */
677780fb4a2SCy Schubert 		os_memcpy(addr, peer->addr, ETH_ALEN);
678780fb4a2SCy Schubert 		wpa_tdls_do_teardown(sm, peer,
679780fb4a2SCy Schubert 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
680780fb4a2SCy Schubert 		wpa_tdls_start(sm, addr);
681f05cddf9SRui Paulo 	} else {
682f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
683f05cddf9SRui Paulo 			   " - tear down", MAC2STR(peer->addr));
684f05cddf9SRui Paulo 		wpa_tdls_do_teardown(sm, peer,
6855b9c547cSRui Paulo 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
686f05cddf9SRui Paulo 	}
687f05cddf9SRui Paulo }
688f05cddf9SRui Paulo 
689f05cddf9SRui Paulo 
wpa_tdls_peer_remove_from_list(struct wpa_sm * sm,struct wpa_tdls_peer * peer)6905b9c547cSRui Paulo static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm,
6915b9c547cSRui Paulo 					   struct wpa_tdls_peer *peer)
6925b9c547cSRui Paulo {
6935b9c547cSRui Paulo 	struct wpa_tdls_peer *cur, *prev;
6945b9c547cSRui Paulo 
6955b9c547cSRui Paulo 	cur = sm->tdls;
6965b9c547cSRui Paulo 	prev = NULL;
6975b9c547cSRui Paulo 	while (cur && cur != peer) {
6985b9c547cSRui Paulo 		prev = cur;
6995b9c547cSRui Paulo 		cur = cur->next;
7005b9c547cSRui Paulo 	}
7015b9c547cSRui Paulo 
7025b9c547cSRui Paulo 	if (cur != peer) {
7035b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR
7045b9c547cSRui Paulo 			   " to remove it from the list",
7055b9c547cSRui Paulo 			   MAC2STR(peer->addr));
7065b9c547cSRui Paulo 		return;
7075b9c547cSRui Paulo 	}
7085b9c547cSRui Paulo 
7095b9c547cSRui Paulo 	if (prev)
7105b9c547cSRui Paulo 		prev->next = peer->next;
7115b9c547cSRui Paulo 	else
7125b9c547cSRui Paulo 		sm->tdls = peer->next;
7135b9c547cSRui Paulo }
7145b9c547cSRui Paulo 
7155b9c547cSRui Paulo 
wpa_tdls_peer_clear(struct wpa_sm * sm,struct wpa_tdls_peer * peer)7165b9c547cSRui Paulo static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
717f05cddf9SRui Paulo {
718f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
719f05cddf9SRui Paulo 		   MAC2STR(peer->addr));
720f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
721f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
7225b9c547cSRui Paulo 	peer->reconfig_key = 0;
723f05cddf9SRui Paulo 	peer->initiator = 0;
7245b9c547cSRui Paulo 	peer->tpk_in_progress = 0;
725f05cddf9SRui Paulo 	os_free(peer->sm_tmr.buf);
726f05cddf9SRui Paulo 	peer->sm_tmr.buf = NULL;
7275b9c547cSRui Paulo 	os_free(peer->ht_capabilities);
7285b9c547cSRui Paulo 	peer->ht_capabilities = NULL;
7295b9c547cSRui Paulo 	os_free(peer->vht_capabilities);
7305b9c547cSRui Paulo 	peer->vht_capabilities = NULL;
731c1d255d3SCy Schubert 	os_free(peer->he_capabilities);
732c1d255d3SCy Schubert 	peer->he_capabilities = NULL;
7334b72b91aSCy Schubert 	os_free(peer->he_6ghz_band_capabilities);
7344b72b91aSCy Schubert 	peer->he_6ghz_band_capabilities = NULL;
735*a90b9d01SCy Schubert 	os_free(peer->eht_capabilities);
736*a90b9d01SCy Schubert 	peer->eht_capabilities = NULL;
7375b9c547cSRui Paulo 	os_free(peer->ext_capab);
7385b9c547cSRui Paulo 	peer->ext_capab = NULL;
7395b9c547cSRui Paulo 	os_free(peer->supp_channels);
7405b9c547cSRui Paulo 	peer->supp_channels = NULL;
7415b9c547cSRui Paulo 	os_free(peer->supp_oper_classes);
7425b9c547cSRui Paulo 	peer->supp_oper_classes = NULL;
743f05cddf9SRui Paulo 	peer->rsnie_i_len = peer->rsnie_p_len = 0;
744f05cddf9SRui Paulo 	peer->cipher = 0;
7455b9c547cSRui Paulo 	peer->qos_info = 0;
7465b9c547cSRui Paulo 	peer->wmm_capable = 0;
747a2063804SGordon Tetlow 	peer->tk_set = peer->tpk_set = peer->tpk_success = 0;
7485b9c547cSRui Paulo 	peer->chan_switch_enabled = 0;
749f05cddf9SRui Paulo 	os_memset(&peer->tpk, 0, sizeof(peer->tpk));
750f05cddf9SRui Paulo 	os_memset(peer->inonce, 0, WPA_NONCE_LEN);
751f05cddf9SRui Paulo 	os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
752*a90b9d01SCy Schubert 	peer->mld_link_id = -1;
753f05cddf9SRui Paulo }
754f05cddf9SRui Paulo 
755f05cddf9SRui Paulo 
wpa_tdls_peer_free(struct wpa_sm * sm,struct wpa_tdls_peer * peer)7565b9c547cSRui Paulo static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
7575b9c547cSRui Paulo {
7585b9c547cSRui Paulo 	wpa_tdls_peer_clear(sm, peer);
7595b9c547cSRui Paulo 	wpa_tdls_peer_remove_from_list(sm, peer);
7605b9c547cSRui Paulo 	os_free(peer);
7615b9c547cSRui Paulo }
7625b9c547cSRui Paulo 
7635b9c547cSRui Paulo 
wpa_tdls_linkid(struct wpa_sm * sm,struct wpa_tdls_peer * peer,struct wpa_tdls_lnkid * lnkid)764f05cddf9SRui Paulo static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
765f05cddf9SRui Paulo 			    struct wpa_tdls_lnkid *lnkid)
766f05cddf9SRui Paulo {
767f05cddf9SRui Paulo 	lnkid->ie_type = WLAN_EID_LINK_ID;
768f05cddf9SRui Paulo 	lnkid->ie_len = 3 * ETH_ALEN;
769*a90b9d01SCy Schubert 	os_memcpy(lnkid->bssid, wpa_tdls_get_link_bssid(sm, peer->mld_link_id),
770*a90b9d01SCy Schubert 		  ETH_ALEN);
771f05cddf9SRui Paulo 	if (peer->initiator) {
772f05cddf9SRui Paulo 		os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN);
773f05cddf9SRui Paulo 		os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN);
774f05cddf9SRui Paulo 	} else {
775f05cddf9SRui Paulo 		os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN);
776f05cddf9SRui Paulo 		os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN);
777f05cddf9SRui Paulo 	}
778f05cddf9SRui Paulo }
779f05cddf9SRui Paulo 
780f05cddf9SRui Paulo 
wpa_tdls_send_teardown(struct wpa_sm * sm,const u8 * addr,u16 reason_code)7815b9c547cSRui Paulo static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
7825b9c547cSRui Paulo 				  u16 reason_code)
783f05cddf9SRui Paulo {
784f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
785f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
786f05cddf9SRui Paulo 	struct wpa_tdls_lnkid lnkid;
787f05cddf9SRui Paulo 	u8 dialog_token;
788f05cddf9SRui Paulo 	u8 *rbuf, *pos;
789f05cddf9SRui Paulo 	int ielen;
790f05cddf9SRui Paulo 
791f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
792f05cddf9SRui Paulo 		return -1;
793f05cddf9SRui Paulo 
794f05cddf9SRui Paulo 	/* Find the node and free from the list */
795f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
796*a90b9d01SCy Schubert 		if (ether_addr_equal(peer->addr, addr))
797f05cddf9SRui Paulo 			break;
798f05cddf9SRui Paulo 	}
799f05cddf9SRui Paulo 
800f05cddf9SRui Paulo 	if (peer == NULL) {
801f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
802f05cddf9SRui Paulo 			   "Teardown " MACSTR, MAC2STR(addr));
803f05cddf9SRui Paulo 		return 0;
804f05cddf9SRui Paulo 	}
805f05cddf9SRui Paulo 
8065b9c547cSRui Paulo 	/* Cancel active channel switch before teardown */
8075b9c547cSRui Paulo 	if (peer->chan_switch_enabled) {
8085b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR
8095b9c547cSRui Paulo 			   " to base channel", MAC2STR(addr));
8105b9c547cSRui Paulo 		wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
8115b9c547cSRui Paulo 	}
8125b9c547cSRui Paulo 
813f05cddf9SRui Paulo 	dialog_token = peer->dtoken;
814f05cddf9SRui Paulo 
815f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR,
816f05cddf9SRui Paulo 		   MAC2STR(addr));
817f05cddf9SRui Paulo 
818f05cddf9SRui Paulo 	ielen = 0;
819f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) {
820f05cddf9SRui Paulo 		/* To add FTIE for Teardown request and compute MIC */
821f05cddf9SRui Paulo 		ielen += sizeof(*ftie);
822f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
823f05cddf9SRui Paulo 		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
824f05cddf9SRui Paulo 			ielen += 170;
825f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
826f05cddf9SRui Paulo 	}
827f05cddf9SRui Paulo 
828f05cddf9SRui Paulo 	rbuf = os_zalloc(ielen + 1);
829f05cddf9SRui Paulo 	if (rbuf == NULL)
830f05cddf9SRui Paulo 		return -1;
831f05cddf9SRui Paulo 	pos = rbuf;
832f05cddf9SRui Paulo 
833f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
834f05cddf9SRui Paulo 		goto skip_ies;
835f05cddf9SRui Paulo 
836f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) pos;
837f05cddf9SRui Paulo 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
838f05cddf9SRui Paulo 	/* Using the recent nonce which should be for CONFIRM frame */
839f05cddf9SRui Paulo 	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
840f05cddf9SRui Paulo 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
841f05cddf9SRui Paulo 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
842f05cddf9SRui Paulo 	pos = (u8 *) (ftie + 1);
843f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
844f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
845f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
846f05cddf9SRui Paulo 			   "FTIE");
847f05cddf9SRui Paulo 		ftie->ie_len += 170;
848f05cddf9SRui Paulo 		*pos++ = 255; /* FTIE subelem */
849f05cddf9SRui Paulo 		*pos++ = 168; /* FTIE subelem length */
850f05cddf9SRui Paulo 		pos += 168;
851f05cddf9SRui Paulo 	}
852f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
853f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake",
854f05cddf9SRui Paulo 		    (u8 *) ftie, pos - (u8 *) ftie);
855f05cddf9SRui Paulo 
856f05cddf9SRui Paulo 	/* compute MIC before sending */
857f05cddf9SRui Paulo 	wpa_tdls_linkid(sm, peer, &lnkid);
858f05cddf9SRui Paulo 	wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
859*a90b9d01SCy Schubert 				  dialog_token, (const u8 *) &lnkid,
860*a90b9d01SCy Schubert 				  (const u8 *) ftie, 2 + ftie->ie_len,
861f05cddf9SRui Paulo 				  ftie->mic);
862f05cddf9SRui Paulo 
863f05cddf9SRui Paulo skip_ies:
864f05cddf9SRui Paulo 	/* TODO: register for a Timeout handler, if Teardown is not received at
865f05cddf9SRui Paulo 	 * the other end, then try again another time */
866f05cddf9SRui Paulo 
867f05cddf9SRui Paulo 	/* request driver to send Teardown using this FTIE */
868f05cddf9SRui Paulo 	wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
869*a90b9d01SCy Schubert 			  reason_code, 0, peer->initiator, rbuf, pos - rbuf,
870*a90b9d01SCy Schubert 			  -1);
871f05cddf9SRui Paulo 	os_free(rbuf);
872f05cddf9SRui Paulo 
873f05cddf9SRui Paulo 	return 0;
874f05cddf9SRui Paulo }
875f05cddf9SRui Paulo 
876f05cddf9SRui Paulo 
wpa_tdls_teardown_link(struct wpa_sm * sm,const u8 * addr,u16 reason_code)877f05cddf9SRui Paulo int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
878f05cddf9SRui Paulo {
879f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
880f05cddf9SRui Paulo 
881f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
882f05cddf9SRui Paulo 		return -1;
883f05cddf9SRui Paulo 
884f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
885*a90b9d01SCy Schubert 		if (ether_addr_equal(peer->addr, addr))
886f05cddf9SRui Paulo 			break;
887f05cddf9SRui Paulo 	}
888f05cddf9SRui Paulo 
889f05cddf9SRui Paulo 	if (peer == NULL) {
890f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR
891f05cddf9SRui Paulo 		   " for link Teardown", MAC2STR(addr));
892f05cddf9SRui Paulo 		return -1;
893f05cddf9SRui Paulo 	}
894f05cddf9SRui Paulo 
895f05cddf9SRui Paulo 	if (!peer->tpk_success) {
896f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
897f05cddf9SRui Paulo 		   " not connected - cannot Teardown link", MAC2STR(addr));
898f05cddf9SRui Paulo 		return -1;
899f05cddf9SRui Paulo 	}
900f05cddf9SRui Paulo 
9015b9c547cSRui Paulo 	return wpa_tdls_do_teardown(sm, peer, reason_code);
902f05cddf9SRui Paulo }
903f05cddf9SRui Paulo 
904f05cddf9SRui Paulo 
wpa_tdls_disable_peer_link(struct wpa_sm * sm,struct wpa_tdls_peer * peer)9055b9c547cSRui Paulo static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
9065b9c547cSRui Paulo 				       struct wpa_tdls_peer *peer)
9075b9c547cSRui Paulo {
9085b9c547cSRui Paulo 	wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
9095b9c547cSRui Paulo 	wpa_tdls_peer_free(sm, peer);
9105b9c547cSRui Paulo }
9115b9c547cSRui Paulo 
9125b9c547cSRui Paulo 
wpa_tdls_disable_unreachable_link(struct wpa_sm * sm,const u8 * addr)9135b9c547cSRui Paulo void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr)
914f05cddf9SRui Paulo {
915f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
916f05cddf9SRui Paulo 
917f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
918*a90b9d01SCy Schubert 		if (ether_addr_equal(peer->addr, addr))
919f05cddf9SRui Paulo 			break;
920f05cddf9SRui Paulo 	}
921f05cddf9SRui Paulo 
9225b9c547cSRui Paulo 	if (!peer || !peer->tpk_success) {
9235b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
9245b9c547cSRui Paulo 			   " not connected - cannot teardown unreachable link",
9255b9c547cSRui Paulo 			   MAC2STR(addr));
9265b9c547cSRui Paulo 		return;
927f05cddf9SRui Paulo 	}
9285b9c547cSRui Paulo 
9295b9c547cSRui Paulo 	if (wpa_tdls_is_external_setup(sm)) {
9305b9c547cSRui Paulo 		/*
9315b9c547cSRui Paulo 		 * Get us on the base channel, disable the link, send a
9325b9c547cSRui Paulo 		 * teardown packet through the AP, and then reset link data.
9335b9c547cSRui Paulo 		 */
9345b9c547cSRui Paulo 		if (peer->chan_switch_enabled)
9355b9c547cSRui Paulo 			wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
9365b9c547cSRui Paulo 		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
9375b9c547cSRui Paulo 		wpa_tdls_send_teardown(sm, addr,
9385b9c547cSRui Paulo 				       WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE);
9395b9c547cSRui Paulo 		wpa_tdls_peer_free(sm, peer);
9405b9c547cSRui Paulo 	} else {
9415b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
9425b9c547cSRui Paulo 	}
9435b9c547cSRui Paulo }
9445b9c547cSRui Paulo 
9455b9c547cSRui Paulo 
wpa_tdls_get_link_status(struct wpa_sm * sm,const u8 * addr)9465b9c547cSRui Paulo const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr)
9475b9c547cSRui Paulo {
9485b9c547cSRui Paulo 	struct wpa_tdls_peer *peer;
9495b9c547cSRui Paulo 
9505b9c547cSRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
9515b9c547cSRui Paulo 		return "disabled";
9525b9c547cSRui Paulo 
9535b9c547cSRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
954*a90b9d01SCy Schubert 		if (ether_addr_equal(peer->addr, addr))
9555b9c547cSRui Paulo 			break;
9565b9c547cSRui Paulo 	}
9575b9c547cSRui Paulo 
9585b9c547cSRui Paulo 	if (peer == NULL)
9595b9c547cSRui Paulo 		return "peer does not exist";
9605b9c547cSRui Paulo 
9615b9c547cSRui Paulo 	if (!peer->tpk_success)
9625b9c547cSRui Paulo 		return "peer not connected";
9635b9c547cSRui Paulo 
9645b9c547cSRui Paulo 	return "connected";
965f05cddf9SRui Paulo }
966f05cddf9SRui Paulo 
967f05cddf9SRui Paulo 
wpa_tdls_recv_teardown(struct wpa_sm * sm,const u8 * src_addr,const u8 * buf,size_t len)968f05cddf9SRui Paulo static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr,
969f05cddf9SRui Paulo 				  const u8 *buf, size_t len)
970f05cddf9SRui Paulo {
971f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer = NULL;
972f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
973f05cddf9SRui Paulo 	struct wpa_tdls_lnkid *lnkid;
974f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
975f05cddf9SRui Paulo 	u16 reason_code;
976f05cddf9SRui Paulo 	const u8 *pos;
977f05cddf9SRui Paulo 	int ielen;
978f05cddf9SRui Paulo 
979f05cddf9SRui Paulo 	/* Find the node and free from the list */
980f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
981*a90b9d01SCy Schubert 		if (ether_addr_equal(peer->addr, src_addr))
982f05cddf9SRui Paulo 			break;
983f05cddf9SRui Paulo 	}
984f05cddf9SRui Paulo 
985f05cddf9SRui Paulo 	if (peer == NULL) {
986f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
987f05cddf9SRui Paulo 			   "Teardown " MACSTR, MAC2STR(src_addr));
988f05cddf9SRui Paulo 		return 0;
989f05cddf9SRui Paulo 	}
990f05cddf9SRui Paulo 
991f05cddf9SRui Paulo 	pos = buf;
992f05cddf9SRui Paulo 	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
993f05cddf9SRui Paulo 
994f05cddf9SRui Paulo 	reason_code = WPA_GET_LE16(pos);
995f05cddf9SRui Paulo 	pos += 2;
996f05cddf9SRui Paulo 
997f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR
998f05cddf9SRui Paulo 		   " (reason code %u)", MAC2STR(src_addr), reason_code);
999f05cddf9SRui Paulo 
1000f05cddf9SRui Paulo 	ielen = len - (pos - buf); /* start of IE in buf */
10015b9c547cSRui Paulo 
10025b9c547cSRui Paulo 	/*
10035b9c547cSRui Paulo 	 * Don't reject the message if failing to parse IEs. The IEs we need are
10045b9c547cSRui Paulo 	 * explicitly checked below. Some APs may add arbitrary padding to the
10055b9c547cSRui Paulo 	 * end of short TDLS frames and that would look like invalid IEs.
10065b9c547cSRui Paulo 	 */
10075b9c547cSRui Paulo 	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0)
10085b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
10095b9c547cSRui Paulo 			   "TDLS: Failed to parse IEs in Teardown - ignore as an interop workaround");
1010f05cddf9SRui Paulo 
1011f05cddf9SRui Paulo 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
1012f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS "
1013f05cddf9SRui Paulo 			   "Teardown");
1014f05cddf9SRui Paulo 		return -1;
1015f05cddf9SRui Paulo 	}
1016f05cddf9SRui Paulo 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
1017f05cddf9SRui Paulo 
1018f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
1019f05cddf9SRui Paulo 		goto skip_ftie;
1020f05cddf9SRui Paulo 
1021f05cddf9SRui Paulo 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
1022f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown");
1023f05cddf9SRui Paulo 		return -1;
1024f05cddf9SRui Paulo 	}
1025f05cddf9SRui Paulo 
1026f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
1027f05cddf9SRui Paulo 
1028f05cddf9SRui Paulo 	/* Process MIC check to see if TDLS Teardown is right */
1029f05cddf9SRui Paulo 	if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
1030f05cddf9SRui Paulo 						    peer->dtoken, peer,
1031*a90b9d01SCy Schubert 						    (const u8 *) lnkid,
1032*a90b9d01SCy Schubert 						    ftie, kde.ftie_len) < 0) {
1033f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
1034f05cddf9SRui Paulo 			   "Teardown Request from " MACSTR, MAC2STR(src_addr));
1035f05cddf9SRui Paulo 		return -1;
1036f05cddf9SRui Paulo 	}
1037f05cddf9SRui Paulo 
1038f05cddf9SRui Paulo skip_ftie:
1039f05cddf9SRui Paulo 	/*
1040f05cddf9SRui Paulo 	 * Request the driver to disable the direct link and clear associated
1041f05cddf9SRui Paulo 	 * keys.
1042f05cddf9SRui Paulo 	 */
10435b9c547cSRui Paulo 	wpa_tdls_disable_peer_link(sm, peer);
1044f05cddf9SRui Paulo 	return 0;
1045f05cddf9SRui Paulo }
1046f05cddf9SRui Paulo 
1047f05cddf9SRui Paulo 
1048f05cddf9SRui Paulo /**
1049f05cddf9SRui Paulo  * wpa_tdls_send_error - To send suitable TDLS status response with
1050f05cddf9SRui Paulo  *	appropriate status code mentioning reason for error/failure.
1051f05cddf9SRui Paulo  * @dst 	- MAC addr of Peer station
1052f05cddf9SRui Paulo  * @tdls_action - TDLS frame type for which error code is sent
10535b9c547cSRui Paulo  * @initiator   - was this end the initiator of the connection
1054f05cddf9SRui Paulo  * @status 	- status code mentioning reason
1055f05cddf9SRui Paulo  */
1056f05cddf9SRui Paulo 
wpa_tdls_send_error(struct wpa_sm * sm,const u8 * dst,u8 tdls_action,u8 dialog_token,int initiator,u16 status)1057f05cddf9SRui Paulo static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
10585b9c547cSRui Paulo 			       u8 tdls_action, u8 dialog_token, int initiator,
10595b9c547cSRui Paulo 			       u16 status)
1060f05cddf9SRui Paulo {
1061f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR
1062f05cddf9SRui Paulo 		   " (action=%u status=%u)",
1063f05cddf9SRui Paulo 		   MAC2STR(dst), tdls_action, status);
1064f05cddf9SRui Paulo 	return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
1065*a90b9d01SCy Schubert 				 0, initiator, NULL, 0, -1);
1066f05cddf9SRui Paulo }
1067f05cddf9SRui Paulo 
1068f05cddf9SRui Paulo 
1069f05cddf9SRui Paulo static struct wpa_tdls_peer *
wpa_tdls_add_peer(struct wpa_sm * sm,const u8 * addr,int * existing)10705b9c547cSRui Paulo wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing)
1071f05cddf9SRui Paulo {
1072f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
1073f05cddf9SRui Paulo 
10745b9c547cSRui Paulo 	if (existing)
10755b9c547cSRui Paulo 		*existing = 0;
10765b9c547cSRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
1077*a90b9d01SCy Schubert 		if (ether_addr_equal(peer->addr, addr)) {
10785b9c547cSRui Paulo 			if (existing)
10795b9c547cSRui Paulo 				*existing = 1;
10805b9c547cSRui Paulo 			return peer; /* re-use existing entry */
10815b9c547cSRui Paulo 		}
10825b9c547cSRui Paulo 	}
10835b9c547cSRui Paulo 
1084f05cddf9SRui Paulo 	wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR,
1085f05cddf9SRui Paulo 		   MAC2STR(addr));
1086f05cddf9SRui Paulo 
1087f05cddf9SRui Paulo 	peer = os_zalloc(sizeof(*peer));
1088f05cddf9SRui Paulo 	if (peer == NULL)
1089f05cddf9SRui Paulo 		return NULL;
1090f05cddf9SRui Paulo 
1091f05cddf9SRui Paulo 	os_memcpy(peer->addr, addr, ETH_ALEN);
1092*a90b9d01SCy Schubert 	peer->mld_link_id = -1;
1093f05cddf9SRui Paulo 	peer->next = sm->tdls;
1094f05cddf9SRui Paulo 	sm->tdls = peer;
1095f05cddf9SRui Paulo 
1096f05cddf9SRui Paulo 	return peer;
1097f05cddf9SRui Paulo }
1098f05cddf9SRui Paulo 
1099f05cddf9SRui Paulo 
wpa_tdls_send_tpk_m1(struct wpa_sm * sm,struct wpa_tdls_peer * peer)1100f05cddf9SRui Paulo static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm,
1101f05cddf9SRui Paulo 				struct wpa_tdls_peer *peer)
1102f05cddf9SRui Paulo {
1103f05cddf9SRui Paulo 	size_t buf_len;
1104f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie timeoutie;
1105f05cddf9SRui Paulo 	u16 rsn_capab;
1106f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
1107f05cddf9SRui Paulo 	u8 *rbuf, *pos, *count_pos;
1108f05cddf9SRui Paulo 	u16 count;
1109f05cddf9SRui Paulo 	struct rsn_ie_hdr *hdr;
11105b9c547cSRui Paulo 	int status;
1111f05cddf9SRui Paulo 
1112f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm)) {
1113f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No security used on the link");
1114f05cddf9SRui Paulo 		peer->rsnie_i_len = 0;
1115f05cddf9SRui Paulo 		goto skip_rsnie;
1116f05cddf9SRui Paulo 	}
1117f05cddf9SRui Paulo 
1118f05cddf9SRui Paulo 	/*
1119f05cddf9SRui Paulo 	 * TPK Handshake Message 1:
1120f05cddf9SRui Paulo 	 * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I,
1121f05cddf9SRui Paulo 	 * Timeout Interval IE))
1122f05cddf9SRui Paulo 	 */
1123f05cddf9SRui Paulo 
1124f05cddf9SRui Paulo 	/* Filling RSN IE */
1125f05cddf9SRui Paulo 	hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
1126f05cddf9SRui Paulo 	hdr->elem_id = WLAN_EID_RSN;
1127f05cddf9SRui Paulo 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
1128f05cddf9SRui Paulo 
1129f05cddf9SRui Paulo 	pos = (u8 *) (hdr + 1);
1130f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
1131f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
1132f05cddf9SRui Paulo 	count_pos = pos;
1133f05cddf9SRui Paulo 	pos += 2;
1134f05cddf9SRui Paulo 
1135f05cddf9SRui Paulo 	count = 0;
1136f05cddf9SRui Paulo 
1137f05cddf9SRui Paulo 	/*
1138f05cddf9SRui Paulo 	 * AES-CCMP is the default Encryption preferred for TDLS, so
1139f05cddf9SRui Paulo 	 * RSN IE is filled only with CCMP CIPHER
1140f05cddf9SRui Paulo 	 * Note: TKIP is not used to encrypt TDLS link.
1141f05cddf9SRui Paulo 	 *
1142f05cddf9SRui Paulo 	 * Regardless of the cipher used on the AP connection, select CCMP
1143f05cddf9SRui Paulo 	 * here.
1144f05cddf9SRui Paulo 	 */
1145f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
1146f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
1147f05cddf9SRui Paulo 	count++;
1148f05cddf9SRui Paulo 
1149f05cddf9SRui Paulo 	WPA_PUT_LE16(count_pos, count);
1150f05cddf9SRui Paulo 
1151f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, 1);
1152f05cddf9SRui Paulo 	pos += 2;
1153f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
1154f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
1155f05cddf9SRui Paulo 
1156f05cddf9SRui Paulo 	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
1157f05cddf9SRui Paulo 	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
1158f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1159f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
1160f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for "
1161f05cddf9SRui Paulo 			   "testing");
1162f05cddf9SRui Paulo 		rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
1163f05cddf9SRui Paulo 	}
1164f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1165f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, rsn_capab);
1166f05cddf9SRui Paulo 	pos += 2;
1167f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1168f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
1169f05cddf9SRui Paulo 		/* Number of PMKIDs */
1170f05cddf9SRui Paulo 		*pos++ = 0x00;
1171f05cddf9SRui Paulo 		*pos++ = 0x00;
1172f05cddf9SRui Paulo 	}
1173f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1174f05cddf9SRui Paulo 
1175f05cddf9SRui Paulo 	hdr->len = (pos - peer->rsnie_i) - 2;
1176f05cddf9SRui Paulo 	peer->rsnie_i_len = pos - peer->rsnie_i;
1177f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
1178f05cddf9SRui Paulo 		    peer->rsnie_i, peer->rsnie_i_len);
1179f05cddf9SRui Paulo 
1180f05cddf9SRui Paulo skip_rsnie:
1181f05cddf9SRui Paulo 	buf_len = 0;
1182f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm))
1183f05cddf9SRui Paulo 		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
1184f05cddf9SRui Paulo 			sizeof(struct wpa_tdls_timeoutie);
1185f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1186f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm) &&
1187f05cddf9SRui Paulo 	    (tdls_testing & TDLS_TESTING_LONG_FRAME))
1188f05cddf9SRui Paulo 		buf_len += 170;
1189f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_DIFF_BSSID)
1190f05cddf9SRui Paulo 		buf_len += sizeof(struct wpa_tdls_lnkid);
1191f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1192f05cddf9SRui Paulo 	rbuf = os_zalloc(buf_len + 1);
1193f05cddf9SRui Paulo 	if (rbuf == NULL) {
1194f05cddf9SRui Paulo 		wpa_tdls_peer_free(sm, peer);
1195c1d255d3SCy Schubert 		return -2;
1196f05cddf9SRui Paulo 	}
1197f05cddf9SRui Paulo 	pos = rbuf;
1198f05cddf9SRui Paulo 
1199f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm))
1200f05cddf9SRui Paulo 		goto skip_ies;
1201f05cddf9SRui Paulo 
1202f05cddf9SRui Paulo 	/* Initiator RSN IE */
1203f05cddf9SRui Paulo 	pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
1204f05cddf9SRui Paulo 
1205f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) pos;
1206f05cddf9SRui Paulo 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
1207f05cddf9SRui Paulo 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
1208f05cddf9SRui Paulo 
1209f05cddf9SRui Paulo 	if (os_get_random(peer->inonce, WPA_NONCE_LEN)) {
1210f05cddf9SRui Paulo 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
1211f05cddf9SRui Paulo 			"TDLS: Failed to get random data for initiator Nonce");
1212f05cddf9SRui Paulo 		os_free(rbuf);
1213f05cddf9SRui Paulo 		wpa_tdls_peer_free(sm, peer);
1214c1d255d3SCy Schubert 		return -2;
1215f05cddf9SRui Paulo 	}
1216a2063804SGordon Tetlow 	peer->tk_set = 0; /* A new nonce results in a new TK */
1217f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake",
1218f05cddf9SRui Paulo 		    peer->inonce, WPA_NONCE_LEN);
1219f05cddf9SRui Paulo 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
1220f05cddf9SRui Paulo 
1221f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1",
1222f05cddf9SRui Paulo 		    (u8 *) ftie, sizeof(struct wpa_tdls_ftie));
1223f05cddf9SRui Paulo 
1224f05cddf9SRui Paulo 	pos = (u8 *) (ftie + 1);
1225f05cddf9SRui Paulo 
1226f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1227f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
1228f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
1229f05cddf9SRui Paulo 			   "FTIE");
1230f05cddf9SRui Paulo 		ftie->ie_len += 170;
1231f05cddf9SRui Paulo 		*pos++ = 255; /* FTIE subelem */
1232f05cddf9SRui Paulo 		*pos++ = 168; /* FTIE subelem length */
1233f05cddf9SRui Paulo 		pos += 168;
1234f05cddf9SRui Paulo 	}
1235f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1236f05cddf9SRui Paulo 
1237f05cddf9SRui Paulo 	/* Lifetime */
1238f05cddf9SRui Paulo 	peer->lifetime = TPK_LIFETIME;
1239f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1240f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) {
1241f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK "
1242f05cddf9SRui Paulo 			   "lifetime");
1243f05cddf9SRui Paulo 		peer->lifetime = 301;
1244f05cddf9SRui Paulo 	}
1245f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) {
1246f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK "
1247f05cddf9SRui Paulo 			   "lifetime");
1248f05cddf9SRui Paulo 		peer->lifetime = 0xffffffff;
1249f05cddf9SRui Paulo 	}
1250f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1251f05cddf9SRui Paulo 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
1252f05cddf9SRui Paulo 				     sizeof(timeoutie), peer->lifetime);
1253f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
1254f05cddf9SRui Paulo 
1255f05cddf9SRui Paulo skip_ies:
1256f05cddf9SRui Paulo 
1257f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1258f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_DIFF_BSSID) {
125985732ac8SCy Schubert 		struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
126085732ac8SCy Schubert 
1261f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in "
1262f05cddf9SRui Paulo 			   "Link Identifier");
1263f05cddf9SRui Paulo 		wpa_tdls_linkid(sm, peer, l);
1264f05cddf9SRui Paulo 		l->bssid[5] ^= 0x01;
1265f05cddf9SRui Paulo 		pos += sizeof(*l);
1266f05cddf9SRui Paulo 	}
1267f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1268f05cddf9SRui Paulo 
1269f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK "
1270f05cddf9SRui Paulo 		   "Handshake Message 1 (peer " MACSTR ")",
1271f05cddf9SRui Paulo 		   MAC2STR(peer->addr));
1272f05cddf9SRui Paulo 
12735b9c547cSRui Paulo 	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST,
1274*a90b9d01SCy Schubert 				   1, 0, 0, peer->initiator, rbuf, pos - rbuf,
1275*a90b9d01SCy Schubert 				   -1);
1276f05cddf9SRui Paulo 	os_free(rbuf);
1277f05cddf9SRui Paulo 
12785b9c547cSRui Paulo 	return status;
1279f05cddf9SRui Paulo }
1280f05cddf9SRui Paulo 
1281f05cddf9SRui Paulo 
wpa_tdls_send_tpk_m2(struct wpa_sm * sm,const unsigned char * src_addr,u8 dtoken,struct wpa_tdls_lnkid * lnkid,const struct wpa_tdls_peer * peer)1282f05cddf9SRui Paulo static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm,
1283f05cddf9SRui Paulo 				const unsigned char *src_addr, u8 dtoken,
1284f05cddf9SRui Paulo 				struct wpa_tdls_lnkid *lnkid,
1285f05cddf9SRui Paulo 				const struct wpa_tdls_peer *peer)
1286f05cddf9SRui Paulo {
1287f05cddf9SRui Paulo 	u8 *rbuf, *pos;
1288f05cddf9SRui Paulo 	size_t buf_len;
1289f05cddf9SRui Paulo 	u32 lifetime;
1290f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie timeoutie;
1291f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
12925b9c547cSRui Paulo 	int status;
1293f05cddf9SRui Paulo 
1294f05cddf9SRui Paulo 	buf_len = 0;
1295f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm)) {
1296f05cddf9SRui Paulo 		/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
1297f05cddf9SRui Paulo 		 * Lifetime */
1298f05cddf9SRui Paulo 		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
1299f05cddf9SRui Paulo 			sizeof(struct wpa_tdls_timeoutie);
1300f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1301f05cddf9SRui Paulo 		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
1302f05cddf9SRui Paulo 			buf_len += 170;
1303f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1304f05cddf9SRui Paulo 	}
1305f05cddf9SRui Paulo 
1306f05cddf9SRui Paulo 	rbuf = os_zalloc(buf_len + 1);
1307f05cddf9SRui Paulo 	if (rbuf == NULL)
1308f05cddf9SRui Paulo 		return -1;
1309f05cddf9SRui Paulo 	pos = rbuf;
1310f05cddf9SRui Paulo 
1311f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm))
1312f05cddf9SRui Paulo 		goto skip_ies;
1313f05cddf9SRui Paulo 
1314f05cddf9SRui Paulo 	/* Peer RSN IE */
1315f05cddf9SRui Paulo 	pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
1316f05cddf9SRui Paulo 
1317f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) pos;
1318f05cddf9SRui Paulo 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
1319f05cddf9SRui Paulo 	/* TODO: ftie->mic_control to set 2-RESPONSE */
1320f05cddf9SRui Paulo 	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
1321f05cddf9SRui Paulo 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
1322f05cddf9SRui Paulo 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
1323f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2",
1324f05cddf9SRui Paulo 		    (u8 *) ftie, sizeof(*ftie));
1325f05cddf9SRui Paulo 
1326f05cddf9SRui Paulo 	pos = (u8 *) (ftie + 1);
1327f05cddf9SRui Paulo 
1328f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1329f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
1330f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
1331f05cddf9SRui Paulo 			   "FTIE");
1332f05cddf9SRui Paulo 		ftie->ie_len += 170;
1333f05cddf9SRui Paulo 		*pos++ = 255; /* FTIE subelem */
1334f05cddf9SRui Paulo 		*pos++ = 168; /* FTIE subelem length */
1335f05cddf9SRui Paulo 		pos += 168;
1336f05cddf9SRui Paulo 	}
1337f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1338f05cddf9SRui Paulo 
1339f05cddf9SRui Paulo 	/* Lifetime */
1340f05cddf9SRui Paulo 	lifetime = peer->lifetime;
1341f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1342f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) {
1343f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
1344f05cddf9SRui Paulo 			   "lifetime in response");
1345f05cddf9SRui Paulo 		lifetime++;
1346f05cddf9SRui Paulo 	}
1347f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1348f05cddf9SRui Paulo 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
1349f05cddf9SRui Paulo 				     sizeof(timeoutie), lifetime);
1350f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator",
1351f05cddf9SRui Paulo 		   lifetime);
1352f05cddf9SRui Paulo 
1353f05cddf9SRui Paulo 	/* compute MIC before sending */
1354*a90b9d01SCy Schubert 	wpa_tdls_ftie_mic(peer->tpk.kck, 2, (const u8 *) lnkid, peer->rsnie_p,
1355*a90b9d01SCy Schubert 			  peer->rsnie_p_len, (const u8 *) &timeoutie,
1356*a90b9d01SCy Schubert 			  (const u8 *) ftie, 2 + ftie->ie_len, ftie->mic);
13575b9c547cSRui Paulo #ifdef CONFIG_TDLS_TESTING
13585b9c547cSRui Paulo 	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
13595b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
13605b9c547cSRui Paulo 		ftie->mic[0] ^= 0x01;
13615b9c547cSRui Paulo 	}
13625b9c547cSRui Paulo #endif /* CONFIG_TDLS_TESTING */
1363f05cddf9SRui Paulo 
1364f05cddf9SRui Paulo skip_ies:
13655b9c547cSRui Paulo 	status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
13665b9c547cSRui Paulo 				   dtoken, 0, 0, peer->initiator, rbuf,
1367*a90b9d01SCy Schubert 				   pos - rbuf, -1);
1368f05cddf9SRui Paulo 	os_free(rbuf);
1369f05cddf9SRui Paulo 
13705b9c547cSRui Paulo 	return status;
1371f05cddf9SRui Paulo }
1372f05cddf9SRui Paulo 
1373f05cddf9SRui Paulo 
wpa_tdls_send_tpk_m3(struct wpa_sm * sm,const unsigned char * src_addr,u8 dtoken,struct wpa_tdls_lnkid * lnkid,const struct wpa_tdls_peer * peer)1374f05cddf9SRui Paulo static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm,
1375f05cddf9SRui Paulo 				const unsigned char *src_addr, u8 dtoken,
1376f05cddf9SRui Paulo 				struct wpa_tdls_lnkid *lnkid,
1377f05cddf9SRui Paulo 				const struct wpa_tdls_peer *peer)
1378f05cddf9SRui Paulo {
1379f05cddf9SRui Paulo 	u8 *rbuf, *pos;
1380f05cddf9SRui Paulo 	size_t buf_len;
1381f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
1382f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie timeoutie;
1383f05cddf9SRui Paulo 	u32 lifetime;
13845b9c547cSRui Paulo 	int status;
13855b9c547cSRui Paulo 	u32 peer_capab = 0;
1386f05cddf9SRui Paulo 
1387f05cddf9SRui Paulo 	buf_len = 0;
1388f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm)) {
1389f05cddf9SRui Paulo 		/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
1390f05cddf9SRui Paulo 		 * Lifetime */
1391f05cddf9SRui Paulo 		buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
1392f05cddf9SRui Paulo 			sizeof(struct wpa_tdls_timeoutie);
1393f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1394f05cddf9SRui Paulo 		if (tdls_testing & TDLS_TESTING_LONG_FRAME)
1395f05cddf9SRui Paulo 			buf_len += 170;
1396f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1397f05cddf9SRui Paulo 	}
1398f05cddf9SRui Paulo 
1399f05cddf9SRui Paulo 	rbuf = os_zalloc(buf_len + 1);
1400f05cddf9SRui Paulo 	if (rbuf == NULL)
1401f05cddf9SRui Paulo 		return -1;
1402f05cddf9SRui Paulo 	pos = rbuf;
1403f05cddf9SRui Paulo 
1404f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm))
1405f05cddf9SRui Paulo 		goto skip_ies;
1406f05cddf9SRui Paulo 
1407f05cddf9SRui Paulo 	/* Peer RSN IE */
1408f05cddf9SRui Paulo 	pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
1409f05cddf9SRui Paulo 
1410f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) pos;
1411f05cddf9SRui Paulo 	ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
1412f05cddf9SRui Paulo 	/*TODO: ftie->mic_control to set 3-CONFIRM */
1413f05cddf9SRui Paulo 	os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
1414f05cddf9SRui Paulo 	os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
1415f05cddf9SRui Paulo 	ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
1416f05cddf9SRui Paulo 
1417f05cddf9SRui Paulo 	pos = (u8 *) (ftie + 1);
1418f05cddf9SRui Paulo 
1419f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1420f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
1421f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
1422f05cddf9SRui Paulo 			   "FTIE");
1423f05cddf9SRui Paulo 		ftie->ie_len += 170;
1424f05cddf9SRui Paulo 		*pos++ = 255; /* FTIE subelem */
1425f05cddf9SRui Paulo 		*pos++ = 168; /* FTIE subelem length */
1426f05cddf9SRui Paulo 		pos += 168;
1427f05cddf9SRui Paulo 	}
1428f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1429f05cddf9SRui Paulo 
1430f05cddf9SRui Paulo 	/* Lifetime */
1431f05cddf9SRui Paulo 	lifetime = peer->lifetime;
1432f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
1433f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) {
1434f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
1435f05cddf9SRui Paulo 			   "lifetime in confirm");
1436f05cddf9SRui Paulo 		lifetime++;
1437f05cddf9SRui Paulo 	}
1438f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
1439f05cddf9SRui Paulo 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
1440f05cddf9SRui Paulo 				     sizeof(timeoutie), lifetime);
1441f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds",
1442f05cddf9SRui Paulo 		   lifetime);
1443f05cddf9SRui Paulo 
1444f05cddf9SRui Paulo 	/* compute MIC before sending */
1445*a90b9d01SCy Schubert 	wpa_tdls_ftie_mic(peer->tpk.kck, 3, (const u8 *) lnkid, peer->rsnie_p,
1446*a90b9d01SCy Schubert 			  peer->rsnie_p_len, (const u8 *) &timeoutie,
1447*a90b9d01SCy Schubert 			  (const u8 *) ftie, 2 + ftie->ie_len, ftie->mic);
14485b9c547cSRui Paulo #ifdef CONFIG_TDLS_TESTING
14495b9c547cSRui Paulo 	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
14505b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
14515b9c547cSRui Paulo 		ftie->mic[0] ^= 0x01;
14525b9c547cSRui Paulo 	}
14535b9c547cSRui Paulo #endif /* CONFIG_TDLS_TESTING */
1454f05cddf9SRui Paulo 
1455f05cddf9SRui Paulo skip_ies:
14565b9c547cSRui Paulo 
1457c1d255d3SCy Schubert 	if (peer->he_capabilities)
1458c1d255d3SCy Schubert 		peer_capab |= TDLS_PEER_HE;
14595b9c547cSRui Paulo 	if (peer->vht_capabilities)
14605b9c547cSRui Paulo 		peer_capab |= TDLS_PEER_VHT;
14615b9c547cSRui Paulo 	if (peer->ht_capabilities)
14625b9c547cSRui Paulo 		peer_capab |= TDLS_PEER_HT;
14635b9c547cSRui Paulo 	if (peer->wmm_capable)
14645b9c547cSRui Paulo 		peer_capab |= TDLS_PEER_WMM;
14655b9c547cSRui Paulo 
14665b9c547cSRui Paulo 	status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
14675b9c547cSRui Paulo 				   dtoken, 0, peer_capab, peer->initiator,
1468*a90b9d01SCy Schubert 				   rbuf, pos - rbuf, -1);
1469f05cddf9SRui Paulo 	os_free(rbuf);
1470f05cddf9SRui Paulo 
14715b9c547cSRui Paulo 	return status;
1472f05cddf9SRui Paulo }
1473f05cddf9SRui Paulo 
1474f05cddf9SRui Paulo 
wpa_tdls_send_discovery_response(struct wpa_sm * sm,struct wpa_tdls_peer * peer,u8 dialog_token,int link_id)1475f05cddf9SRui Paulo static int wpa_tdls_send_discovery_response(struct wpa_sm *sm,
1476f05cddf9SRui Paulo 					    struct wpa_tdls_peer *peer,
1477*a90b9d01SCy Schubert 					    u8 dialog_token, int link_id)
1478f05cddf9SRui Paulo {
14795b9c547cSRui Paulo 	size_t buf_len = 0;
14805b9c547cSRui Paulo 	struct wpa_tdls_timeoutie timeoutie;
14815b9c547cSRui Paulo 	u16 rsn_capab;
14825b9c547cSRui Paulo 	u8 *rbuf, *pos, *count_pos;
14835b9c547cSRui Paulo 	u16 count;
14845b9c547cSRui Paulo 	struct rsn_ie_hdr *hdr;
14855b9c547cSRui Paulo 	int status;
14865b9c547cSRui Paulo 
1487f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response "
1488f05cddf9SRui Paulo 		   "(peer " MACSTR ")", MAC2STR(peer->addr));
14895b9c547cSRui Paulo 	if (!wpa_tdls_get_privacy(sm))
14905b9c547cSRui Paulo 		goto skip_rsn_ies;
1491f05cddf9SRui Paulo 
14925b9c547cSRui Paulo 	/* Filling RSN IE */
14935b9c547cSRui Paulo 	hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
14945b9c547cSRui Paulo 	hdr->elem_id = WLAN_EID_RSN;
14955b9c547cSRui Paulo 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
14965b9c547cSRui Paulo 	pos = (u8 *) (hdr + 1);
14975b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
14985b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
14995b9c547cSRui Paulo 	count_pos = pos;
15005b9c547cSRui Paulo 	pos += 2;
15015b9c547cSRui Paulo 	count = 0;
15025b9c547cSRui Paulo 
15035b9c547cSRui Paulo 	/*
15045b9c547cSRui Paulo 	* AES-CCMP is the default encryption preferred for TDLS, so
15055b9c547cSRui Paulo 	* RSN IE is filled only with CCMP cipher suite.
15065b9c547cSRui Paulo 	* Note: TKIP is not used to encrypt TDLS link.
15075b9c547cSRui Paulo 	*
15085b9c547cSRui Paulo 	* Regardless of the cipher used on the AP connection, select CCMP
15095b9c547cSRui Paulo 	* here.
15105b9c547cSRui Paulo 	*/
15115b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
15125b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
15135b9c547cSRui Paulo 	count++;
15145b9c547cSRui Paulo 	WPA_PUT_LE16(count_pos, count);
15155b9c547cSRui Paulo 	WPA_PUT_LE16(pos, 1);
15165b9c547cSRui Paulo 	pos += 2;
15175b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
15185b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
15195b9c547cSRui Paulo 
15205b9c547cSRui Paulo 	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
15215b9c547cSRui Paulo 	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
15225b9c547cSRui Paulo 	WPA_PUT_LE16(pos, rsn_capab);
15235b9c547cSRui Paulo 	pos += 2;
15245b9c547cSRui Paulo 	hdr->len = (pos - (u8 *) hdr) - 2;
15255b9c547cSRui Paulo 	peer->rsnie_i_len = pos - peer->rsnie_i;
15265b9c547cSRui Paulo 
15275b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response",
15285b9c547cSRui Paulo 		    (u8 *) hdr, hdr->len + 2);
15295b9c547cSRui Paulo skip_rsn_ies:
15305b9c547cSRui Paulo 	buf_len = 0;
15315b9c547cSRui Paulo 	if (wpa_tdls_get_privacy(sm)) {
15325b9c547cSRui Paulo 		/* Peer RSN IE, Lifetime */
15335b9c547cSRui Paulo 		buf_len += peer->rsnie_i_len +
15345b9c547cSRui Paulo 			sizeof(struct wpa_tdls_timeoutie);
15355b9c547cSRui Paulo 	}
15365b9c547cSRui Paulo 	rbuf = os_zalloc(buf_len + 1);
15375b9c547cSRui Paulo 	if (rbuf == NULL) {
15385b9c547cSRui Paulo 		wpa_tdls_peer_free(sm, peer);
15395b9c547cSRui Paulo 		return -1;
15405b9c547cSRui Paulo 	}
15415b9c547cSRui Paulo 	pos = rbuf;
15425b9c547cSRui Paulo 
15435b9c547cSRui Paulo 	if (!wpa_tdls_get_privacy(sm))
15445b9c547cSRui Paulo 		goto skip_ies;
15455b9c547cSRui Paulo 	/* Initiator RSN IE */
15465b9c547cSRui Paulo 	pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
15475b9c547cSRui Paulo 	/* Lifetime */
15485b9c547cSRui Paulo 	peer->lifetime = TPK_LIFETIME;
15495b9c547cSRui Paulo 	pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
15505b9c547cSRui Paulo 				     sizeof(timeoutie), peer->lifetime);
15515b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
15525b9c547cSRui Paulo skip_ies:
15535b9c547cSRui Paulo 	status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
1554*a90b9d01SCy Schubert 				   dialog_token, 0, 0, 0, rbuf, pos - rbuf,
1555*a90b9d01SCy Schubert 				   link_id);
15565b9c547cSRui Paulo 	os_free(rbuf);
15575b9c547cSRui Paulo 
15585b9c547cSRui Paulo 	return status;
1559f05cddf9SRui Paulo }
1560f05cddf9SRui Paulo 
1561f05cddf9SRui Paulo 
wpa_tdls_is_lnkid_bss_valid(struct wpa_sm * sm,const struct wpa_tdls_lnkid * lnkid,int * link_id)1562*a90b9d01SCy Schubert static bool wpa_tdls_is_lnkid_bss_valid(struct wpa_sm *sm,
1563*a90b9d01SCy Schubert 					const struct wpa_tdls_lnkid *lnkid,
1564*a90b9d01SCy Schubert 					int *link_id)
1565*a90b9d01SCy Schubert {
1566*a90b9d01SCy Schubert 	*link_id = -1;
1567*a90b9d01SCy Schubert 
1568*a90b9d01SCy Schubert 	if (!sm->mlo.valid_links) {
1569*a90b9d01SCy Schubert 		if (!ether_addr_equal(sm->bssid, lnkid->bssid))
1570*a90b9d01SCy Schubert 			return false;
1571*a90b9d01SCy Schubert 	} else {
1572*a90b9d01SCy Schubert 		int i;
1573*a90b9d01SCy Schubert 
1574*a90b9d01SCy Schubert 		for_each_link(sm->mlo.valid_links, i) {
1575*a90b9d01SCy Schubert 			if (ether_addr_equal(lnkid->bssid,
1576*a90b9d01SCy Schubert 					     sm->mlo.links[i].bssid)) {
1577*a90b9d01SCy Schubert 				*link_id = i;
1578*a90b9d01SCy Schubert 				break;
1579*a90b9d01SCy Schubert 			}
1580*a90b9d01SCy Schubert 		}
1581*a90b9d01SCy Schubert 		if (*link_id < 0) {
1582*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
1583*a90b9d01SCy Schubert 				   "TDLS: MLD link not found for linkid BSS "
1584*a90b9d01SCy Schubert 				   MACSTR, MAC2STR(lnkid->bssid));
1585*a90b9d01SCy Schubert 			return false;
1586*a90b9d01SCy Schubert 		}
1587*a90b9d01SCy Schubert 	}
1588*a90b9d01SCy Schubert 
1589*a90b9d01SCy Schubert 	return true;
1590*a90b9d01SCy Schubert }
1591*a90b9d01SCy Schubert 
1592*a90b9d01SCy Schubert 
1593f05cddf9SRui Paulo static int
wpa_tdls_process_discovery_request(struct wpa_sm * sm,const u8 * addr,const u8 * buf,size_t len)1594f05cddf9SRui Paulo wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
1595f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
1596f05cddf9SRui Paulo {
1597f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
1598f05cddf9SRui Paulo 	const struct wpa_tdls_lnkid *lnkid;
1599f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
1600f05cddf9SRui Paulo 	size_t min_req_len = sizeof(struct wpa_tdls_frame) +
1601f05cddf9SRui Paulo 		1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid);
1602f05cddf9SRui Paulo 	u8 dialog_token;
1603*a90b9d01SCy Schubert 	int link_id = -1;
1604f05cddf9SRui Paulo 
1605f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR,
1606f05cddf9SRui Paulo 		   MAC2STR(addr));
1607f05cddf9SRui Paulo 
1608f05cddf9SRui Paulo 	if (len < min_req_len) {
1609f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: "
1610f05cddf9SRui Paulo 			   "%d", (int) len);
1611f05cddf9SRui Paulo 		return -1;
1612f05cddf9SRui Paulo 	}
1613f05cddf9SRui Paulo 
1614f05cddf9SRui Paulo 	dialog_token = buf[sizeof(struct wpa_tdls_frame)];
1615f05cddf9SRui Paulo 
16165b9c547cSRui Paulo 	/*
16175b9c547cSRui Paulo 	 * Some APs will tack on a weird IE to the end of a TDLS
16185b9c547cSRui Paulo 	 * discovery request packet. This needn't fail the response,
16195b9c547cSRui Paulo 	 * since the required IE are verified separately.
16205b9c547cSRui Paulo 	 */
1621f05cddf9SRui Paulo 	if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
1622f05cddf9SRui Paulo 				     len - (sizeof(struct wpa_tdls_frame) + 1),
16235b9c547cSRui Paulo 				     &kde) < 0) {
16245b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
16255b9c547cSRui Paulo 			   "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround");
16265b9c547cSRui Paulo 	}
1627f05cddf9SRui Paulo 
1628f05cddf9SRui Paulo 	if (!kde.lnkid) {
1629f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
1630f05cddf9SRui Paulo 			   "Request");
1631f05cddf9SRui Paulo 		return -1;
1632f05cddf9SRui Paulo 	}
1633f05cddf9SRui Paulo 
1634f05cddf9SRui Paulo 	lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid;
1635f05cddf9SRui Paulo 
1636*a90b9d01SCy Schubert 	if (!wpa_tdls_is_lnkid_bss_valid(sm, lnkid, &link_id)) {
1637*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
1638*a90b9d01SCy Schubert 			   "TDLS: Discovery Request from different BSS "
1639*a90b9d01SCy Schubert 			   MACSTR, MAC2STR(lnkid->bssid));
1640f05cddf9SRui Paulo 			return -1;
1641f05cddf9SRui Paulo 	}
1642f05cddf9SRui Paulo 
16435b9c547cSRui Paulo 	peer = wpa_tdls_add_peer(sm, addr, NULL);
1644f05cddf9SRui Paulo 	if (peer == NULL)
1645f05cddf9SRui Paulo 		return -1;
1646f05cddf9SRui Paulo 
1647*a90b9d01SCy Schubert 	return wpa_tdls_send_discovery_response(sm, peer, dialog_token,
1648*a90b9d01SCy Schubert 						link_id);
1649f05cddf9SRui Paulo }
1650f05cddf9SRui Paulo 
1651f05cddf9SRui Paulo 
wpa_tdls_send_discovery_request(struct wpa_sm * sm,const u8 * addr)1652f05cddf9SRui Paulo int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr)
1653f05cddf9SRui Paulo {
1654f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
1655f05cddf9SRui Paulo 		return -1;
1656f05cddf9SRui Paulo 
1657f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer "
1658f05cddf9SRui Paulo 		   MACSTR, MAC2STR(addr));
1659f05cddf9SRui Paulo 	return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST,
1660*a90b9d01SCy Schubert 				 1, 0, 0, 1, NULL, 0, -1);
1661f05cddf9SRui Paulo }
1662f05cddf9SRui Paulo 
1663f05cddf9SRui Paulo 
copy_supp_rates(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)1664f05cddf9SRui Paulo static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde,
1665f05cddf9SRui Paulo 			   struct wpa_tdls_peer *peer)
1666f05cddf9SRui Paulo {
1667f05cddf9SRui Paulo 	if (!kde->supp_rates) {
1668f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
1669f05cddf9SRui Paulo 		return -1;
1670f05cddf9SRui Paulo 	}
16715b9c547cSRui Paulo 	peer->supp_rates_len = merge_byte_arrays(
16725b9c547cSRui Paulo 		peer->supp_rates, sizeof(peer->supp_rates),
16735b9c547cSRui Paulo 		kde->supp_rates + 2, kde->supp_rates_len - 2,
16745b9c547cSRui Paulo 		kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL,
16754bc52338SCy Schubert 		kde->ext_supp_rates ? kde->ext_supp_rates_len - 2 : 0);
16765b9c547cSRui Paulo 	return 0;
1677f05cddf9SRui Paulo }
1678f05cddf9SRui Paulo 
16795b9c547cSRui Paulo 
copy_peer_ht_capab(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)16805b9c547cSRui Paulo static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde,
16815b9c547cSRui Paulo 			      struct wpa_tdls_peer *peer)
16825b9c547cSRui Paulo {
1683325151a3SRui Paulo 	if (!kde->ht_capabilities) {
16845b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities "
16855b9c547cSRui Paulo 			   "received");
1686f05cddf9SRui Paulo 		return 0;
1687f05cddf9SRui Paulo 	}
1688f05cddf9SRui Paulo 
16895b9c547cSRui Paulo 	if (!peer->ht_capabilities) {
16905b9c547cSRui Paulo 		peer->ht_capabilities =
16915b9c547cSRui Paulo                         os_zalloc(sizeof(struct ieee80211_ht_capabilities));
16925b9c547cSRui Paulo 		if (peer->ht_capabilities == NULL)
16935b9c547cSRui Paulo                         return -1;
16945b9c547cSRui Paulo 	}
16955b9c547cSRui Paulo 
16965b9c547cSRui Paulo 	os_memcpy(peer->ht_capabilities, kde->ht_capabilities,
16975b9c547cSRui Paulo                   sizeof(struct ieee80211_ht_capabilities));
16985b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities",
16995b9c547cSRui Paulo 		    (u8 *) peer->ht_capabilities,
17005b9c547cSRui Paulo 		    sizeof(struct ieee80211_ht_capabilities));
17015b9c547cSRui Paulo 
17025b9c547cSRui Paulo 	return 0;
17035b9c547cSRui Paulo }
17045b9c547cSRui Paulo 
17055b9c547cSRui Paulo 
copy_peer_vht_capab(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)17065b9c547cSRui Paulo static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde,
17075b9c547cSRui Paulo 			      struct wpa_tdls_peer *peer)
17085b9c547cSRui Paulo {
1709325151a3SRui Paulo 	if (!kde->vht_capabilities) {
17105b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities "
17115b9c547cSRui Paulo 			   "received");
17125b9c547cSRui Paulo 		return 0;
17135b9c547cSRui Paulo 	}
17145b9c547cSRui Paulo 
17155b9c547cSRui Paulo 	if (!peer->vht_capabilities) {
17165b9c547cSRui Paulo 		peer->vht_capabilities =
17175b9c547cSRui Paulo                         os_zalloc(sizeof(struct ieee80211_vht_capabilities));
17185b9c547cSRui Paulo 		if (peer->vht_capabilities == NULL)
17195b9c547cSRui Paulo                         return -1;
17205b9c547cSRui Paulo 	}
17215b9c547cSRui Paulo 
17225b9c547cSRui Paulo 	os_memcpy(peer->vht_capabilities, kde->vht_capabilities,
17235b9c547cSRui Paulo                   sizeof(struct ieee80211_vht_capabilities));
17245b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities",
17255b9c547cSRui Paulo 		    (u8 *) peer->vht_capabilities,
17265b9c547cSRui Paulo 		    sizeof(struct ieee80211_vht_capabilities));
17275b9c547cSRui Paulo 
17285b9c547cSRui Paulo 	return 0;
17295b9c547cSRui Paulo }
17305b9c547cSRui Paulo 
17315b9c547cSRui Paulo 
copy_peer_he_capab(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)1732c1d255d3SCy Schubert static int copy_peer_he_capab(const struct wpa_eapol_ie_parse *kde,
1733c1d255d3SCy Schubert 			      struct wpa_tdls_peer *peer)
1734c1d255d3SCy Schubert {
1735c1d255d3SCy Schubert 	if (!kde->he_capabilities) {
1736c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG, "TDLS: No HE capabilities received");
1737c1d255d3SCy Schubert 		return 0;
1738c1d255d3SCy Schubert 	}
1739c1d255d3SCy Schubert 
1740c1d255d3SCy Schubert 	os_free(peer->he_capabilities);
1741c1d255d3SCy Schubert 	peer->he_capab_len = 0;
1742c1d255d3SCy Schubert 	peer->he_capabilities = os_memdup(kde->he_capabilities,
1743c1d255d3SCy Schubert 					  kde->he_capab_len);
1744c1d255d3SCy Schubert 	if (!peer->he_capabilities)
1745c1d255d3SCy Schubert 		return -1;
1746c1d255d3SCy Schubert 
1747c1d255d3SCy Schubert 	peer->he_capab_len = kde->he_capab_len;
1748c1d255d3SCy Schubert 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer HE capabilities",
1749c1d255d3SCy Schubert 		    peer->he_capabilities, peer->he_capab_len);
1750c1d255d3SCy Schubert 
1751c1d255d3SCy Schubert 	return 0;
1752c1d255d3SCy Schubert }
1753c1d255d3SCy Schubert 
1754c1d255d3SCy Schubert 
copy_peer_he_6ghz_band_capab(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)17554b72b91aSCy Schubert static int copy_peer_he_6ghz_band_capab(const struct wpa_eapol_ie_parse *kde,
17564b72b91aSCy Schubert 					struct wpa_tdls_peer *peer)
17574b72b91aSCy Schubert {
17584b72b91aSCy Schubert 	if (!kde->he_6ghz_capabilities) {
17594b72b91aSCy Schubert 		wpa_printf(MSG_DEBUG,
17604b72b91aSCy Schubert 			   "TDLS: No HE 6 GHz band capabilities received");
17614b72b91aSCy Schubert 		return 0;
17624b72b91aSCy Schubert 	}
17634b72b91aSCy Schubert 
17644b72b91aSCy Schubert 	if (!peer->he_6ghz_band_capabilities) {
17654b72b91aSCy Schubert 		peer->he_6ghz_band_capabilities =
17664b72b91aSCy Schubert 			os_zalloc(sizeof(struct ieee80211_he_6ghz_band_cap));
17674b72b91aSCy Schubert 		if (peer->he_6ghz_band_capabilities == NULL)
17684b72b91aSCy Schubert 			return -1;
17694b72b91aSCy Schubert 	}
17704b72b91aSCy Schubert 
17714b72b91aSCy Schubert 	os_memcpy(peer->he_6ghz_band_capabilities, kde->he_6ghz_capabilities,
17724b72b91aSCy Schubert 		  sizeof(struct ieee80211_he_6ghz_band_cap));
17734b72b91aSCy Schubert 
17744b72b91aSCy Schubert 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer 6 GHz band HE capabilities",
17754b72b91aSCy Schubert 		    peer->he_6ghz_band_capabilities,
17764b72b91aSCy Schubert 		    sizeof(struct ieee80211_he_6ghz_band_cap));
17774b72b91aSCy Schubert 
17784b72b91aSCy Schubert 	return 0;
17794b72b91aSCy Schubert }
17804b72b91aSCy Schubert 
17814b72b91aSCy Schubert 
copy_peer_ext_capab(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)17825b9c547cSRui Paulo static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde,
17835b9c547cSRui Paulo 			       struct wpa_tdls_peer *peer)
17845b9c547cSRui Paulo {
17855b9c547cSRui Paulo 	if (!kde->ext_capab) {
17865b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities "
17875b9c547cSRui Paulo 			   "received");
17885b9c547cSRui Paulo 		return 0;
17895b9c547cSRui Paulo 	}
17905b9c547cSRui Paulo 
17915b9c547cSRui Paulo 	if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) {
17925b9c547cSRui Paulo 		/* Need to allocate buffer to fit the new information */
17935b9c547cSRui Paulo 		os_free(peer->ext_capab);
17945b9c547cSRui Paulo 		peer->ext_capab = os_zalloc(kde->ext_capab_len - 2);
17955b9c547cSRui Paulo 		if (peer->ext_capab == NULL)
17965b9c547cSRui Paulo 			return -1;
17975b9c547cSRui Paulo 	}
17985b9c547cSRui Paulo 
17995b9c547cSRui Paulo 	peer->ext_capab_len = kde->ext_capab_len - 2;
18005b9c547cSRui Paulo 	os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len);
18015b9c547cSRui Paulo 
18025b9c547cSRui Paulo 	return 0;
18035b9c547cSRui Paulo }
18045b9c547cSRui Paulo 
18055b9c547cSRui Paulo 
copy_peer_eht_capab(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)1806*a90b9d01SCy Schubert static int copy_peer_eht_capab(const struct wpa_eapol_ie_parse *kde,
1807*a90b9d01SCy Schubert 			       struct wpa_tdls_peer *peer)
1808*a90b9d01SCy Schubert {
1809*a90b9d01SCy Schubert 	if (!kde->eht_capabilities) {
1810*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "TDLS: No EHT capabilities received");
1811*a90b9d01SCy Schubert 		return 0;
1812*a90b9d01SCy Schubert 	}
1813*a90b9d01SCy Schubert 
1814*a90b9d01SCy Schubert 	os_free(peer->eht_capabilities);
1815*a90b9d01SCy Schubert 	peer->eht_capab_len = 0;
1816*a90b9d01SCy Schubert 	peer->eht_capabilities = os_memdup(kde->eht_capabilities,
1817*a90b9d01SCy Schubert 					   kde->eht_capab_len);
1818*a90b9d01SCy Schubert 	if (!peer->eht_capabilities)
1819*a90b9d01SCy Schubert 		return -1;
1820*a90b9d01SCy Schubert 
1821*a90b9d01SCy Schubert 	peer->eht_capab_len = kde->eht_capab_len;
1822*a90b9d01SCy Schubert 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer EHT capabilities",
1823*a90b9d01SCy Schubert 		    peer->eht_capabilities, peer->eht_capab_len);
1824*a90b9d01SCy Schubert 
1825*a90b9d01SCy Schubert 	return 0;
1826*a90b9d01SCy Schubert }
1827*a90b9d01SCy Schubert 
1828*a90b9d01SCy Schubert 
copy_peer_wmm_capab(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)18295b9c547cSRui Paulo static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde,
18305b9c547cSRui Paulo 			       struct wpa_tdls_peer *peer)
18315b9c547cSRui Paulo {
18325b9c547cSRui Paulo 	struct wmm_information_element *wmm;
18335b9c547cSRui Paulo 
18345b9c547cSRui Paulo 	if (!kde->wmm) {
18355b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported WMM capabilities received");
18365b9c547cSRui Paulo 		return 0;
18375b9c547cSRui Paulo 	}
18385b9c547cSRui Paulo 
18395b9c547cSRui Paulo 	if (kde->wmm_len < sizeof(struct wmm_information_element)) {
18405b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Invalid supported WMM capabilities received");
18415b9c547cSRui Paulo 		return -1;
18425b9c547cSRui Paulo 	}
18435b9c547cSRui Paulo 
18445b9c547cSRui Paulo 	wmm = (struct wmm_information_element *) kde->wmm;
18455b9c547cSRui Paulo 	peer->qos_info = wmm->qos_info;
18465b9c547cSRui Paulo 
18475b9c547cSRui Paulo 	peer->wmm_capable = 1;
18485b9c547cSRui Paulo 
18495b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Peer WMM QOS Info 0x%x", peer->qos_info);
18505b9c547cSRui Paulo 	return 0;
18515b9c547cSRui Paulo }
18525b9c547cSRui Paulo 
18535b9c547cSRui Paulo 
copy_peer_supp_channels(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)18545b9c547cSRui Paulo static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde,
18555b9c547cSRui Paulo 				   struct wpa_tdls_peer *peer)
18565b9c547cSRui Paulo {
18575b9c547cSRui Paulo 	if (!kde->supp_channels) {
18585b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported channels received");
18595b9c547cSRui Paulo 		return 0;
18605b9c547cSRui Paulo 	}
18615b9c547cSRui Paulo 
18625b9c547cSRui Paulo 	if (!peer->supp_channels ||
18635b9c547cSRui Paulo 	    peer->supp_channels_len < kde->supp_channels_len) {
18645b9c547cSRui Paulo 		os_free(peer->supp_channels);
18655b9c547cSRui Paulo 		peer->supp_channels = os_zalloc(kde->supp_channels_len);
18665b9c547cSRui Paulo 		if (peer->supp_channels == NULL)
18675b9c547cSRui Paulo 			return -1;
18685b9c547cSRui Paulo 	}
18695b9c547cSRui Paulo 
18705b9c547cSRui Paulo 	peer->supp_channels_len = kde->supp_channels_len;
18715b9c547cSRui Paulo 
18725b9c547cSRui Paulo 	os_memcpy(peer->supp_channels, kde->supp_channels,
18735b9c547cSRui Paulo 		  peer->supp_channels_len);
18745b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels",
18755b9c547cSRui Paulo 		    (u8 *) peer->supp_channels, peer->supp_channels_len);
18765b9c547cSRui Paulo 	return 0;
18775b9c547cSRui Paulo }
18785b9c547cSRui Paulo 
18795b9c547cSRui Paulo 
copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse * kde,struct wpa_tdls_peer * peer)18805b9c547cSRui Paulo static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde,
18815b9c547cSRui Paulo 				       struct wpa_tdls_peer *peer)
18825b9c547cSRui Paulo {
18835b9c547cSRui Paulo 	if (!kde->supp_oper_classes) {
18845b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received");
18855b9c547cSRui Paulo 		return 0;
18865b9c547cSRui Paulo 	}
18875b9c547cSRui Paulo 
18885b9c547cSRui Paulo 	if (!peer->supp_oper_classes ||
18895b9c547cSRui Paulo 	    peer->supp_oper_classes_len < kde->supp_oper_classes_len) {
18905b9c547cSRui Paulo 		os_free(peer->supp_oper_classes);
18915b9c547cSRui Paulo 		peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len);
18925b9c547cSRui Paulo 		if (peer->supp_oper_classes == NULL)
18935b9c547cSRui Paulo 			return -1;
18945b9c547cSRui Paulo 	}
18955b9c547cSRui Paulo 
18965b9c547cSRui Paulo 	peer->supp_oper_classes_len = kde->supp_oper_classes_len;
18975b9c547cSRui Paulo 	os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes,
18985b9c547cSRui Paulo 		  peer->supp_oper_classes_len);
18995b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes",
19005b9c547cSRui Paulo 		    (u8 *) peer->supp_oper_classes,
19015b9c547cSRui Paulo 		    peer->supp_oper_classes_len);
19025b9c547cSRui Paulo 	return 0;
19035b9c547cSRui Paulo }
19045b9c547cSRui Paulo 
19055b9c547cSRui Paulo 
wpa_tdls_addset_peer(struct wpa_sm * sm,struct wpa_tdls_peer * peer,int add)19065b9c547cSRui Paulo static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
19075b9c547cSRui Paulo 				int add)
19085b9c547cSRui Paulo {
19095b9c547cSRui Paulo 	return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid,
19105b9c547cSRui Paulo 				       peer->capability,
19115b9c547cSRui Paulo 				       peer->supp_rates, peer->supp_rates_len,
19125b9c547cSRui Paulo 				       peer->ht_capabilities,
19135b9c547cSRui Paulo 				       peer->vht_capabilities,
1914c1d255d3SCy Schubert 				       peer->he_capabilities,
1915c1d255d3SCy Schubert 				       peer->he_capab_len,
19164b72b91aSCy Schubert 				       peer->he_6ghz_band_capabilities,
19175b9c547cSRui Paulo 				       peer->qos_info, peer->wmm_capable,
19185b9c547cSRui Paulo 				       peer->ext_capab, peer->ext_capab_len,
19195b9c547cSRui Paulo 				       peer->supp_channels,
19205b9c547cSRui Paulo 				       peer->supp_channels_len,
19215b9c547cSRui Paulo 				       peer->supp_oper_classes,
1922*a90b9d01SCy Schubert 				       peer->supp_oper_classes_len,
1923*a90b9d01SCy Schubert 				       peer->eht_capabilities,
1924*a90b9d01SCy Schubert 				       peer->eht_capab_len,
1925*a90b9d01SCy Schubert 				       peer->mld_link_id);
19265b9c547cSRui Paulo }
19275b9c547cSRui Paulo 
1928f05cddf9SRui Paulo 
tdls_nonce_set(const u8 * nonce)1929a2063804SGordon Tetlow static int tdls_nonce_set(const u8 *nonce)
1930a2063804SGordon Tetlow {
1931a2063804SGordon Tetlow 	int i;
1932a2063804SGordon Tetlow 
1933a2063804SGordon Tetlow 	for (i = 0; i < WPA_NONCE_LEN; i++) {
1934a2063804SGordon Tetlow 		if (nonce[i])
1935a2063804SGordon Tetlow 			return 1;
1936a2063804SGordon Tetlow 	}
1937a2063804SGordon Tetlow 
1938a2063804SGordon Tetlow 	return 0;
1939a2063804SGordon Tetlow }
1940a2063804SGordon Tetlow 
1941a2063804SGordon Tetlow 
wpa_tdls_process_tpk_m1(struct wpa_sm * sm,const u8 * src_addr,const u8 * buf,size_t len)1942f05cddf9SRui Paulo static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
1943f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
1944f05cddf9SRui Paulo {
1945f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
1946f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
1947f05cddf9SRui Paulo 	struct wpa_ie_data ie;
1948f05cddf9SRui Paulo 	int cipher;
1949f05cddf9SRui Paulo 	const u8 *cpos;
1950f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie = NULL;
1951f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie *timeoutie;
1952f05cddf9SRui Paulo 	struct wpa_tdls_lnkid *lnkid;
1953f05cddf9SRui Paulo 	u32 lifetime = 0;
1954f05cddf9SRui Paulo #if 0
1955f05cddf9SRui Paulo 	struct rsn_ie_hdr *hdr;
1956f05cddf9SRui Paulo 	u8 *pos;
1957f05cddf9SRui Paulo 	u16 rsn_capab;
1958f05cddf9SRui Paulo 	u16 rsn_ver;
1959f05cddf9SRui Paulo #endif
1960f05cddf9SRui Paulo 	u8 dtoken;
1961f05cddf9SRui Paulo 	u16 ielen;
1962f05cddf9SRui Paulo 	u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
1963f05cddf9SRui Paulo 	int tdls_prohibited = sm->tdls_prohibited;
1964f05cddf9SRui Paulo 	int existing_peer = 0;
1965*a90b9d01SCy Schubert 	int link_id = -1;
1966f05cddf9SRui Paulo 
1967f05cddf9SRui Paulo 	if (len < 3 + 3)
1968f05cddf9SRui Paulo 		return -1;
1969f05cddf9SRui Paulo 
1970f05cddf9SRui Paulo 	cpos = buf;
1971f05cddf9SRui Paulo 	cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
1972f05cddf9SRui Paulo 
1973f05cddf9SRui Paulo 	/* driver had already verified the frame format */
1974f05cddf9SRui Paulo 	dtoken = *cpos++; /* dialog token */
1975f05cddf9SRui Paulo 
1976f05cddf9SRui Paulo 	wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken);
1977f05cddf9SRui Paulo 
19785b9c547cSRui Paulo 	peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer);
1979f05cddf9SRui Paulo 	if (peer == NULL)
1980f05cddf9SRui Paulo 		goto error;
19815b9c547cSRui Paulo 
19825b9c547cSRui Paulo 	/* If found, use existing entry instead of adding a new one;
19835b9c547cSRui Paulo 	 * how to handle the case where both ends initiate at the
19845b9c547cSRui Paulo 	 * same time? */
19855b9c547cSRui Paulo 	if (existing_peer) {
19865b9c547cSRui Paulo 		if (peer->tpk_success) {
19875b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
19885b9c547cSRui Paulo 				   "direct link is enabled - tear down the "
19895b9c547cSRui Paulo 				   "old link first");
19905b9c547cSRui Paulo 			wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
19915b9c547cSRui Paulo 			wpa_tdls_peer_clear(sm, peer);
19925b9c547cSRui Paulo 		} else if (peer->initiator) {
19935b9c547cSRui Paulo 			/*
19945b9c547cSRui Paulo 			 * An entry is already present, so check if we already
19955b9c547cSRui Paulo 			 * sent a TDLS Setup Request. If so, compare MAC
19965b9c547cSRui Paulo 			 * addresses and let the STA with the lower MAC address
19975b9c547cSRui Paulo 			 * continue as the initiator. The other negotiation is
19985b9c547cSRui Paulo 			 * terminated.
19995b9c547cSRui Paulo 			 */
20005b9c547cSRui Paulo 			if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
20015b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "TDLS: Discard request "
20025b9c547cSRui Paulo 					   "from peer with higher address "
20035b9c547cSRui Paulo 					   MACSTR, MAC2STR(src_addr));
20045b9c547cSRui Paulo 				return -1;
20055b9c547cSRui Paulo 			} else {
20065b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "TDLS: Accept request "
20075b9c547cSRui Paulo 					   "from peer with lower address "
20085b9c547cSRui Paulo 					   MACSTR " (terminate previously "
20095b9c547cSRui Paulo 					   "initiated negotiation",
20105b9c547cSRui Paulo 					   MAC2STR(src_addr));
20115b9c547cSRui Paulo 				wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
20125b9c547cSRui Paulo 						 peer->addr);
20135b9c547cSRui Paulo 				wpa_tdls_peer_clear(sm, peer);
20145b9c547cSRui Paulo 			}
20155b9c547cSRui Paulo 		}
2016f05cddf9SRui Paulo 	}
2017f05cddf9SRui Paulo 
2018f05cddf9SRui Paulo 	/* capability information */
2019f05cddf9SRui Paulo 	peer->capability = WPA_GET_LE16(cpos);
2020f05cddf9SRui Paulo 	cpos += 2;
2021f05cddf9SRui Paulo 
2022f05cddf9SRui Paulo 	ielen = len - (cpos - buf); /* start of IE in buf */
20235b9c547cSRui Paulo 
20245b9c547cSRui Paulo 	/*
20255b9c547cSRui Paulo 	 * Don't reject the message if failing to parse IEs. The IEs we need are
20265b9c547cSRui Paulo 	 * explicitly checked below. Some APs may add arbitrary padding to the
20275b9c547cSRui Paulo 	 * end of short TDLS frames and that would look like invalid IEs.
20285b9c547cSRui Paulo 	 */
20295b9c547cSRui Paulo 	if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0)
20305b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
20315b9c547cSRui Paulo 			   "TDLS: Failed to parse IEs in TPK M1 - ignore as an interop workaround");
2032f05cddf9SRui Paulo 
2033f05cddf9SRui Paulo 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
2034f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
2035f05cddf9SRui Paulo 			   "TPK M1");
2036f05cddf9SRui Paulo 		goto error;
2037f05cddf9SRui Paulo 	}
2038f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1",
2039f05cddf9SRui Paulo 		    kde.lnkid, kde.lnkid_len);
2040f05cddf9SRui Paulo 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
2041*a90b9d01SCy Schubert 
2042*a90b9d01SCy Schubert 	if (!wpa_tdls_is_lnkid_bss_valid(sm, lnkid, &link_id)) {
2043*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS "
2044*a90b9d01SCy Schubert 				MACSTR, MAC2STR(lnkid->bssid));
20455b9c547cSRui Paulo 		status = WLAN_STATUS_REQUEST_DECLINED;
2046f05cddf9SRui Paulo 		goto error;
2047f05cddf9SRui Paulo 	}
2048f05cddf9SRui Paulo 
2049*a90b9d01SCy Schubert 	peer->mld_link_id = link_id;
2050f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
2051f05cddf9SRui Paulo 		   MAC2STR(src_addr));
2052f05cddf9SRui Paulo 
2053f05cddf9SRui Paulo 	if (copy_supp_rates(&kde, peer) < 0)
2054f05cddf9SRui Paulo 		goto error;
2055f05cddf9SRui Paulo 
20565b9c547cSRui Paulo 	if (copy_peer_ht_capab(&kde, peer) < 0)
20575b9c547cSRui Paulo 		goto error;
20585b9c547cSRui Paulo 
2059c1d255d3SCy Schubert 	if (copy_peer_vht_capab(&kde, peer) < 0 ||
20604b72b91aSCy Schubert 	    copy_peer_he_capab(&kde, peer) < 0 ||
20614b72b91aSCy Schubert 	    copy_peer_he_6ghz_band_capab(&kde, peer) < 0)
20625b9c547cSRui Paulo 		goto error;
20635b9c547cSRui Paulo 
2064*a90b9d01SCy Schubert 	if (copy_peer_eht_capab(&kde, peer) < 0)
2065*a90b9d01SCy Schubert 		goto error;
2066*a90b9d01SCy Schubert 
20675b9c547cSRui Paulo 	if (copy_peer_ext_capab(&kde, peer) < 0)
20685b9c547cSRui Paulo 		goto error;
20695b9c547cSRui Paulo 
20705b9c547cSRui Paulo 	if (copy_peer_supp_channels(&kde, peer) < 0)
20715b9c547cSRui Paulo 		goto error;
20725b9c547cSRui Paulo 
20735b9c547cSRui Paulo 	if (copy_peer_supp_oper_classes(&kde, peer) < 0)
20745b9c547cSRui Paulo 		goto error;
20755b9c547cSRui Paulo 
20765b9c547cSRui Paulo 	peer->qos_info = kde.qosinfo;
20775b9c547cSRui Paulo 
20785b9c547cSRui Paulo 	/* Overwrite with the qos_info obtained in WMM IE */
20795b9c547cSRui Paulo 	if (copy_peer_wmm_capab(&kde, peer) < 0)
20805b9c547cSRui Paulo 		goto error;
20815b9c547cSRui Paulo 
20825b9c547cSRui Paulo 	peer->aid = kde.aid;
20835b9c547cSRui Paulo 
2084f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
2085f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
20865b9c547cSRui Paulo 		peer = wpa_tdls_add_peer(sm, src_addr, NULL);
2087f05cddf9SRui Paulo 		if (peer == NULL)
2088f05cddf9SRui Paulo 			goto error;
2089f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of "
2090f05cddf9SRui Paulo 			   "TDLS setup - send own request");
2091f05cddf9SRui Paulo 		peer->initiator = 1;
20925b9c547cSRui Paulo 		wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
20934b72b91aSCy Schubert 					NULL, NULL, 0, NULL, 0, 0, NULL, 0,
2094*a90b9d01SCy Schubert 					NULL, 0, NULL, 0, NULL, 0, link_id);
2095c1d255d3SCy Schubert 		if (wpa_tdls_send_tpk_m1(sm, peer) == -2) {
2096c1d255d3SCy Schubert 			peer = NULL;
2097c1d255d3SCy Schubert 			goto error;
2098c1d255d3SCy Schubert 		}
2099f05cddf9SRui Paulo 	}
2100f05cddf9SRui Paulo 
2101f05cddf9SRui Paulo 	if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
2102f05cddf9SRui Paulo 	    tdls_prohibited) {
2103f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
2104f05cddf9SRui Paulo 			   "on TDLS");
2105f05cddf9SRui Paulo 		tdls_prohibited = 0;
2106f05cddf9SRui Paulo 	}
2107f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
2108f05cddf9SRui Paulo 
2109f05cddf9SRui Paulo 	if (tdls_prohibited) {
2110f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS");
2111f05cddf9SRui Paulo 		status = WLAN_STATUS_REQUEST_DECLINED;
2112f05cddf9SRui Paulo 		goto error;
2113f05cddf9SRui Paulo 	}
2114f05cddf9SRui Paulo 
2115f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm)) {
2116f05cddf9SRui Paulo 		if (kde.rsn_ie) {
2117f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while "
2118f05cddf9SRui Paulo 				   "security is disabled");
2119f05cddf9SRui Paulo 			status = WLAN_STATUS_SECURITY_DISABLED;
2120f05cddf9SRui Paulo 			goto error;
2121f05cddf9SRui Paulo 		}
2122f05cddf9SRui Paulo 		goto skip_rsn;
2123f05cddf9SRui Paulo 	}
2124f05cddf9SRui Paulo 
2125f05cddf9SRui Paulo 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
2126f05cddf9SRui Paulo 	    kde.rsn_ie == NULL) {
2127f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1");
2128f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_PARAMETERS;
2129f05cddf9SRui Paulo 		goto error;
2130f05cddf9SRui Paulo 	}
2131f05cddf9SRui Paulo 
2132f05cddf9SRui Paulo 	if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
2133f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in "
2134f05cddf9SRui Paulo 			   "TPK M1");
2135f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
2136f05cddf9SRui Paulo 		goto error;
2137f05cddf9SRui Paulo 	}
2138f05cddf9SRui Paulo 
2139f05cddf9SRui Paulo 	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
2140f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1");
2141f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
2142f05cddf9SRui Paulo 		goto error;
2143f05cddf9SRui Paulo 	}
2144f05cddf9SRui Paulo 
2145f05cddf9SRui Paulo 	cipher = ie.pairwise_cipher;
2146f05cddf9SRui Paulo 	if (cipher & WPA_CIPHER_CCMP) {
2147f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
2148f05cddf9SRui Paulo 		cipher = WPA_CIPHER_CCMP;
2149f05cddf9SRui Paulo 	} else {
2150f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1");
2151f05cddf9SRui Paulo 		status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
2152f05cddf9SRui Paulo 		goto error;
2153f05cddf9SRui Paulo 	}
2154f05cddf9SRui Paulo 
2155f05cddf9SRui Paulo 	if ((ie.capabilities &
2156f05cddf9SRui Paulo 	     (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) !=
2157f05cddf9SRui Paulo 	    WPA_CAPABILITY_PEERKEY_ENABLED) {
2158f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in "
2159f05cddf9SRui Paulo 			   "TPK M1");
2160f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSN_IE_CAPAB;
2161f05cddf9SRui Paulo 		goto error;
2162f05cddf9SRui Paulo 	}
2163f05cddf9SRui Paulo 
2164f05cddf9SRui Paulo 	/* Lifetime */
2165f05cddf9SRui Paulo 	if (kde.key_lifetime == NULL) {
2166f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1");
2167f05cddf9SRui Paulo 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
2168f05cddf9SRui Paulo 		goto error;
2169f05cddf9SRui Paulo 	}
2170f05cddf9SRui Paulo 	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
2171f05cddf9SRui Paulo 	lifetime = WPA_GET_LE32(timeoutie->value);
2172f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime);
2173f05cddf9SRui Paulo 	if (lifetime < 300) {
2174f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime");
2175f05cddf9SRui Paulo 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
2176f05cddf9SRui Paulo 		goto error;
2177f05cddf9SRui Paulo 	}
2178f05cddf9SRui Paulo 
2179f05cddf9SRui Paulo skip_rsn:
2180f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
2181f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
2182f05cddf9SRui Paulo 		if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) {
2183f05cddf9SRui Paulo 			/*
2184f05cddf9SRui Paulo 			 * The request frame from us is going to win, so do not
2185f05cddf9SRui Paulo 			 * replace information based on this request frame from
2186f05cddf9SRui Paulo 			 * the peer.
2187f05cddf9SRui Paulo 			 */
2188f05cddf9SRui Paulo 			goto skip_rsn_check;
2189f05cddf9SRui Paulo 		}
2190f05cddf9SRui Paulo 	}
2191f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
2192f05cddf9SRui Paulo 
2193f05cddf9SRui Paulo 	peer->initiator = 0; /* Need to check */
2194f05cddf9SRui Paulo 	peer->dtoken = dtoken;
2195f05cddf9SRui Paulo 
2196f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm)) {
2197f05cddf9SRui Paulo 		peer->rsnie_i_len = 0;
2198f05cddf9SRui Paulo 		peer->rsnie_p_len = 0;
2199f05cddf9SRui Paulo 		peer->cipher = WPA_CIPHER_NONE;
2200f05cddf9SRui Paulo 		goto skip_rsn_check;
2201f05cddf9SRui Paulo 	}
2202f05cddf9SRui Paulo 
2203f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
2204f05cddf9SRui Paulo 	os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
2205f05cddf9SRui Paulo 	peer->rsnie_i_len = kde.rsn_ie_len;
2206f05cddf9SRui Paulo 	peer->cipher = cipher;
2207f05cddf9SRui Paulo 
2208a2063804SGordon Tetlow 	if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0 ||
2209a2063804SGordon Tetlow 	    !tdls_nonce_set(peer->inonce)) {
22105b9c547cSRui Paulo 		/*
22115b9c547cSRui Paulo 		 * There is no point in updating the RNonce for every obtained
22125b9c547cSRui Paulo 		 * TPK M1 frame (e.g., retransmission due to timeout) with the
22135b9c547cSRui Paulo 		 * same INonce (SNonce in FTIE). However, if the TPK M1 is
22145b9c547cSRui Paulo 		 * retransmitted with a different INonce, update the RNonce
22155b9c547cSRui Paulo 		 * since this is for a new TDLS session.
22165b9c547cSRui Paulo 		 */
22175b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
22185b9c547cSRui Paulo 			   "TDLS: New TPK M1 INonce - generate new RNonce");
22195b9c547cSRui Paulo 		os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN);
2220f05cddf9SRui Paulo 		if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
2221f05cddf9SRui Paulo 			wpa_msg(sm->ctx->ctx, MSG_WARNING,
2222f05cddf9SRui Paulo 				"TDLS: Failed to get random data for responder nonce");
2223f05cddf9SRui Paulo 			goto error;
2224f05cddf9SRui Paulo 		}
2225a2063804SGordon Tetlow 		peer->tk_set = 0; /* A new nonce results in a new TK */
22265b9c547cSRui Paulo 	}
2227f05cddf9SRui Paulo 
2228f05cddf9SRui Paulo #if 0
2229f05cddf9SRui Paulo 	/* get version info from RSNIE received from Peer */
2230f05cddf9SRui Paulo 	hdr = (struct rsn_ie_hdr *) kde.rsn_ie;
2231f05cddf9SRui Paulo 	rsn_ver = WPA_GET_LE16(hdr->version);
2232f05cddf9SRui Paulo 
2233f05cddf9SRui Paulo 	/* use min(peer's version, out version) */
2234f05cddf9SRui Paulo 	if (rsn_ver > RSN_VERSION)
2235f05cddf9SRui Paulo 		rsn_ver = RSN_VERSION;
2236f05cddf9SRui Paulo 
2237f05cddf9SRui Paulo 	hdr = (struct rsn_ie_hdr *) peer->rsnie_p;
2238f05cddf9SRui Paulo 
2239f05cddf9SRui Paulo 	hdr->elem_id = WLAN_EID_RSN;
2240f05cddf9SRui Paulo 	WPA_PUT_LE16(hdr->version, rsn_ver);
2241f05cddf9SRui Paulo 	pos = (u8 *) (hdr + 1);
2242f05cddf9SRui Paulo 
2243f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
2244f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
2245f05cddf9SRui Paulo 	/* Include only the selected cipher in pairwise cipher suite */
2246f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, 1);
2247f05cddf9SRui Paulo 	pos += 2;
2248f05cddf9SRui Paulo 	if (cipher == WPA_CIPHER_CCMP)
2249f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
2250f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
2251f05cddf9SRui Paulo 
2252f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, 1);
2253f05cddf9SRui Paulo 	pos += 2;
2254f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
2255f05cddf9SRui Paulo 	pos += RSN_SELECTOR_LEN;
2256f05cddf9SRui Paulo 
2257f05cddf9SRui Paulo 	rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
2258f05cddf9SRui Paulo 	rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
2259f05cddf9SRui Paulo 	WPA_PUT_LE16(pos, rsn_capab);
2260f05cddf9SRui Paulo 	pos += 2;
2261f05cddf9SRui Paulo 
2262f05cddf9SRui Paulo 	hdr->len = (pos - peer->rsnie_p) - 2;
2263f05cddf9SRui Paulo 	peer->rsnie_p_len = pos - peer->rsnie_p;
2264f05cddf9SRui Paulo #endif
2265f05cddf9SRui Paulo 
2266f05cddf9SRui Paulo 	/* temp fix: validation of RSNIE later */
2267f05cddf9SRui Paulo 	os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len);
2268f05cddf9SRui Paulo 	peer->rsnie_p_len = peer->rsnie_i_len;
2269f05cddf9SRui Paulo 
2270f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
2271f05cddf9SRui Paulo 		    peer->rsnie_p, peer->rsnie_p_len);
2272f05cddf9SRui Paulo 
2273f05cddf9SRui Paulo 	peer->lifetime = lifetime;
2274f05cddf9SRui Paulo 
2275*a90b9d01SCy Schubert 	if (peer->mld_link_id >= 0)
2276*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "TDLS: Use link ID %u for TPK derivation",
2277*a90b9d01SCy Schubert 			   peer->mld_link_id);
2278*a90b9d01SCy Schubert 	wpa_tdls_generate_tpk(peer, sm->own_addr,
2279*a90b9d01SCy Schubert 			      wpa_tdls_get_link_bssid(sm, peer->mld_link_id));
2280f05cddf9SRui Paulo 
2281f05cddf9SRui Paulo skip_rsn_check:
22825b9c547cSRui Paulo #ifdef CONFIG_TDLS_TESTING
22835b9c547cSRui Paulo 	if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT)
22845b9c547cSRui Paulo 		goto skip_add_peer;
22855b9c547cSRui Paulo #endif /* CONFIG_TDLS_TESTING */
22865b9c547cSRui Paulo 
22875b9c547cSRui Paulo 	/* add supported rates, capabilities, and qos_info to the TDLS peer */
22885b9c547cSRui Paulo 	if (wpa_tdls_addset_peer(sm, peer, 1) < 0)
22895b9c547cSRui Paulo 		goto error;
22905b9c547cSRui Paulo 
22915b9c547cSRui Paulo #ifdef CONFIG_TDLS_TESTING
22925b9c547cSRui Paulo skip_add_peer:
22935b9c547cSRui Paulo #endif /* CONFIG_TDLS_TESTING */
22945b9c547cSRui Paulo 	peer->tpk_in_progress = 1;
2295f05cddf9SRui Paulo 
2296f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
2297f05cddf9SRui Paulo 	if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
22985b9c547cSRui Paulo 		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
2299f05cddf9SRui Paulo 		goto error;
2300f05cddf9SRui Paulo 	}
2301f05cddf9SRui Paulo 
230285732ac8SCy Schubert #ifdef CONFIG_TDLS_TESTING
230385732ac8SCy Schubert 	if (tdls_testing & TDLS_TESTING_DOUBLE_TPK_M2) {
230485732ac8SCy Schubert 		wpa_printf(MSG_INFO, "TDLS: Testing - Send another TPK M2");
230585732ac8SCy Schubert 		wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer);
230685732ac8SCy Schubert 	}
230785732ac8SCy Schubert #endif /* CONFIG_TDLS_TESTING */
230885732ac8SCy Schubert 
2309f05cddf9SRui Paulo 	return 0;
2310f05cddf9SRui Paulo 
2311f05cddf9SRui Paulo error:
23125b9c547cSRui Paulo 	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
2313f05cddf9SRui Paulo 			    status);
23145b9c547cSRui Paulo 	if (peer)
23155b9c547cSRui Paulo 		wpa_tdls_peer_free(sm, peer);
2316f05cddf9SRui Paulo 	return -1;
2317f05cddf9SRui Paulo }
2318f05cddf9SRui Paulo 
2319f05cddf9SRui Paulo 
wpa_tdls_enable_link(struct wpa_sm * sm,struct wpa_tdls_peer * peer)23205b9c547cSRui Paulo static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
2321f05cddf9SRui Paulo {
2322f05cddf9SRui Paulo 	peer->tpk_success = 1;
23235b9c547cSRui Paulo 	peer->tpk_in_progress = 0;
2324f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
2325f05cddf9SRui Paulo 	if (wpa_tdls_get_privacy(sm)) {
2326f05cddf9SRui Paulo 		u32 lifetime = peer->lifetime;
2327f05cddf9SRui Paulo 		/*
2328f05cddf9SRui Paulo 		 * Start the initiator process a bit earlier to avoid race
2329f05cddf9SRui Paulo 		 * condition with the responder sending teardown request.
2330f05cddf9SRui Paulo 		 */
2331f05cddf9SRui Paulo 		if (lifetime > 3 && peer->initiator)
2332f05cddf9SRui Paulo 			lifetime -= 3;
2333f05cddf9SRui Paulo 		eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout,
2334f05cddf9SRui Paulo 				       sm, peer);
2335f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
2336f05cddf9SRui Paulo 		if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) {
233785732ac8SCy Schubert 			wpa_printf(MSG_DEBUG,
233885732ac8SCy Schubert 				   "TDLS: Testing - disable TPK expiration");
2339f05cddf9SRui Paulo 			eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
2340f05cddf9SRui Paulo 		}
2341f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
2342f05cddf9SRui Paulo 	}
2343f05cddf9SRui Paulo 
23445b9c547cSRui Paulo 	if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) {
23455b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Could not configure key to the "
23465b9c547cSRui Paulo 			   "driver");
23475b9c547cSRui Paulo 		return -1;
23485b9c547cSRui Paulo 	}
23495b9c547cSRui Paulo 	peer->reconfig_key = 0;
2350f05cddf9SRui Paulo 
23515b9c547cSRui Paulo 	return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr);
2352f05cddf9SRui Paulo }
2353f05cddf9SRui Paulo 
2354f05cddf9SRui Paulo 
wpa_tdls_process_tpk_m2(struct wpa_sm * sm,const u8 * src_addr,const u8 * buf,size_t len)2355f05cddf9SRui Paulo static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
2356f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
2357f05cddf9SRui Paulo {
2358f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
2359f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
2360f05cddf9SRui Paulo 	struct wpa_ie_data ie;
2361f05cddf9SRui Paulo 	int cipher;
2362f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
2363f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie *timeoutie;
2364f05cddf9SRui Paulo 	struct wpa_tdls_lnkid *lnkid;
2365f05cddf9SRui Paulo 	u32 lifetime;
2366f05cddf9SRui Paulo 	u8 dtoken;
2367f05cddf9SRui Paulo 	int ielen;
2368f05cddf9SRui Paulo 	u16 status;
2369f05cddf9SRui Paulo 	const u8 *pos;
23705b9c547cSRui Paulo 	int ret = 0;
2371f05cddf9SRui Paulo 
2372f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
2373f05cddf9SRui Paulo 		   "(Peer " MACSTR ")", MAC2STR(src_addr));
2374f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
2375*a90b9d01SCy Schubert 		if (ether_addr_equal(peer->addr, src_addr))
2376f05cddf9SRui Paulo 			break;
2377f05cddf9SRui Paulo 	}
2378f05cddf9SRui Paulo 	if (peer == NULL) {
2379f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
2380f05cddf9SRui Paulo 			   "TPK M2: " MACSTR, MAC2STR(src_addr));
2381f05cddf9SRui Paulo 		return -1;
2382f05cddf9SRui Paulo 	}
23835b9c547cSRui Paulo 	if (!peer->initiator) {
23845b9c547cSRui Paulo 		/*
23855b9c547cSRui Paulo 		 * This may happen if both devices try to initiate TDLS at the
23865b9c547cSRui Paulo 		 * same time and we accept the TPK M1 from the peer in
23875b9c547cSRui Paulo 		 * wpa_tdls_process_tpk_m1() and clear our previous state.
23885b9c547cSRui Paulo 		 */
23895b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so "
23905b9c547cSRui Paulo 			   "ignore TPK M2 from " MACSTR, MAC2STR(src_addr));
23915b9c547cSRui Paulo 		return -1;
23925b9c547cSRui Paulo 	}
2393a2063804SGordon Tetlow 
2394a2063804SGordon Tetlow 	if (peer->tpk_success) {
2395a2063804SGordon Tetlow 		wpa_printf(MSG_INFO, "TDLS: Ignore incoming TPK M2 retry, from "
2396a2063804SGordon Tetlow 			   MACSTR " as TPK M3 was already sent",
2397a2063804SGordon Tetlow 			   MAC2STR(src_addr));
2398a2063804SGordon Tetlow 		return 0;
2399a2063804SGordon Tetlow 	}
2400a2063804SGordon Tetlow 
2401f05cddf9SRui Paulo 	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
2402f05cddf9SRui Paulo 
24035b9c547cSRui Paulo 	if (len < 3 + 2 + 1) {
24045b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2405f05cddf9SRui Paulo 		return -1;
24065b9c547cSRui Paulo 	}
24075b9c547cSRui Paulo 
2408f05cddf9SRui Paulo 	pos = buf;
2409f05cddf9SRui Paulo 	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
2410f05cddf9SRui Paulo 	status = WPA_GET_LE16(pos);
2411f05cddf9SRui Paulo 	pos += 2 /* status code */;
2412f05cddf9SRui Paulo 
2413f05cddf9SRui Paulo 	if (status != WLAN_STATUS_SUCCESS) {
2414f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u",
2415f05cddf9SRui Paulo 			   status);
24165b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2417f05cddf9SRui Paulo 		return -1;
2418f05cddf9SRui Paulo 	}
2419f05cddf9SRui Paulo 
2420f05cddf9SRui Paulo 	status = WLAN_STATUS_UNSPECIFIED_FAILURE;
2421f05cddf9SRui Paulo 
2422f05cddf9SRui Paulo 	/* TODO: need to verify dialog token matches here or in kernel */
2423f05cddf9SRui Paulo 	dtoken = *pos++; /* dialog token */
2424f05cddf9SRui Paulo 
2425f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken);
2426f05cddf9SRui Paulo 
24275b9c547cSRui Paulo 	if (len < 3 + 2 + 1 + 2) {
24285b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2429f05cddf9SRui Paulo 		return -1;
24305b9c547cSRui Paulo 	}
2431f05cddf9SRui Paulo 
2432f05cddf9SRui Paulo 	/* capability information */
2433f05cddf9SRui Paulo 	peer->capability = WPA_GET_LE16(pos);
2434f05cddf9SRui Paulo 	pos += 2;
2435f05cddf9SRui Paulo 
2436f05cddf9SRui Paulo 	ielen = len - (pos - buf); /* start of IE in buf */
24375b9c547cSRui Paulo 
24385b9c547cSRui Paulo 	/*
24395b9c547cSRui Paulo 	 * Don't reject the message if failing to parse IEs. The IEs we need are
24405b9c547cSRui Paulo 	 * explicitly checked below. Some APs may add arbitrary padding to the
24415b9c547cSRui Paulo 	 * end of short TDLS frames and that would look like invalid IEs.
24425b9c547cSRui Paulo 	 */
24435b9c547cSRui Paulo 	if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0)
24445b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
24455b9c547cSRui Paulo 			   "TDLS: Failed to parse IEs in TPK M2 - ignore as an interop workaround");
2446f05cddf9SRui Paulo 
2447f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
2448f05cddf9SRui Paulo 	if (tdls_testing & TDLS_TESTING_DECLINE_RESP) {
2449f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response");
2450f05cddf9SRui Paulo 		status = WLAN_STATUS_REQUEST_DECLINED;
2451f05cddf9SRui Paulo 		goto error;
2452f05cddf9SRui Paulo 	}
2453f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
2454f05cddf9SRui Paulo 
2455f05cddf9SRui Paulo 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
2456f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
2457f05cddf9SRui Paulo 			   "TPK M2");
2458f05cddf9SRui Paulo 		goto error;
2459f05cddf9SRui Paulo 	}
2460f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2",
2461f05cddf9SRui Paulo 		    kde.lnkid, kde.lnkid_len);
2462f05cddf9SRui Paulo 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
2463f05cddf9SRui Paulo 
2464*a90b9d01SCy Schubert 	if (!ether_addr_equal(sm->bssid,
2465*a90b9d01SCy Schubert 			      wpa_tdls_get_link_bssid(sm, peer->mld_link_id))) {
2466f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
2467f05cddf9SRui Paulo 		status = WLAN_STATUS_NOT_IN_SAME_BSS;
2468f05cddf9SRui Paulo 		goto error;
2469f05cddf9SRui Paulo 	}
2470f05cddf9SRui Paulo 
2471f05cddf9SRui Paulo 	if (copy_supp_rates(&kde, peer) < 0)
2472f05cddf9SRui Paulo 		goto error;
2473f05cddf9SRui Paulo 
24745b9c547cSRui Paulo 	if (copy_peer_ht_capab(&kde, peer) < 0)
24755b9c547cSRui Paulo 		goto error;
24765b9c547cSRui Paulo 
2477c1d255d3SCy Schubert 	if (copy_peer_vht_capab(&kde, peer) < 0 ||
24784b72b91aSCy Schubert 	    copy_peer_he_capab(&kde, peer) < 0 ||
24794b72b91aSCy Schubert 	    copy_peer_he_6ghz_band_capab(&kde, peer) < 0)
24805b9c547cSRui Paulo 		goto error;
24815b9c547cSRui Paulo 
2482*a90b9d01SCy Schubert 	if (copy_peer_eht_capab(&kde, peer) < 0)
2483*a90b9d01SCy Schubert 		goto error;
2484*a90b9d01SCy Schubert 
24855b9c547cSRui Paulo 	if (copy_peer_ext_capab(&kde, peer) < 0)
24865b9c547cSRui Paulo 		goto error;
24875b9c547cSRui Paulo 
24885b9c547cSRui Paulo 	if (copy_peer_supp_channels(&kde, peer) < 0)
24895b9c547cSRui Paulo 		goto error;
24905b9c547cSRui Paulo 
24915b9c547cSRui Paulo 	if (copy_peer_supp_oper_classes(&kde, peer) < 0)
24925b9c547cSRui Paulo 		goto error;
24935b9c547cSRui Paulo 
24945b9c547cSRui Paulo 	peer->qos_info = kde.qosinfo;
24955b9c547cSRui Paulo 
24965b9c547cSRui Paulo 	/* Overwrite with the qos_info obtained in WMM IE */
24975b9c547cSRui Paulo 	if (copy_peer_wmm_capab(&kde, peer) < 0)
24985b9c547cSRui Paulo 		goto error;
24995b9c547cSRui Paulo 
25005b9c547cSRui Paulo 	peer->aid = kde.aid;
25015b9c547cSRui Paulo 
2502f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm)) {
2503f05cddf9SRui Paulo 		peer->rsnie_p_len = 0;
2504f05cddf9SRui Paulo 		peer->cipher = WPA_CIPHER_NONE;
2505f05cddf9SRui Paulo 		goto skip_rsn;
2506f05cddf9SRui Paulo 	}
2507f05cddf9SRui Paulo 
2508f05cddf9SRui Paulo 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
2509f05cddf9SRui Paulo 	    kde.rsn_ie == NULL) {
2510f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2");
2511f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_PARAMETERS;
2512f05cddf9SRui Paulo 		goto error;
2513f05cddf9SRui Paulo 	}
2514f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
2515f05cddf9SRui Paulo 		    kde.rsn_ie, kde.rsn_ie_len);
2516f05cddf9SRui Paulo 
25175b9c547cSRui Paulo 	if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
25185b9c547cSRui Paulo 		wpa_printf(MSG_INFO,
25195b9c547cSRui Paulo 			   "TDLS: Too long Responder RSN IE in TPK M2");
25205b9c547cSRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
25215b9c547cSRui Paulo 		goto error;
25225b9c547cSRui Paulo 	}
25235b9c547cSRui Paulo 
2524f05cddf9SRui Paulo 	/*
2525f05cddf9SRui Paulo 	 * FIX: bitwise comparison of RSN IE is not the correct way of
2526f05cddf9SRui Paulo 	 * validation this. It can be different, but certain fields must
2527f05cddf9SRui Paulo 	 * match. Since we list only a single pairwise cipher in TPK M1, the
2528f05cddf9SRui Paulo 	 * memcmp is likely to work in most cases, though.
2529f05cddf9SRui Paulo 	 */
2530f05cddf9SRui Paulo 	if (kde.rsn_ie_len != peer->rsnie_i_len ||
2531f05cddf9SRui Paulo 	    os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) {
2532f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does "
2533f05cddf9SRui Paulo 			   "not match with RSN IE used in TPK M1");
2534f05cddf9SRui Paulo 		wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1",
2535f05cddf9SRui Paulo 			    peer->rsnie_i, peer->rsnie_i_len);
2536f05cddf9SRui Paulo 		wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
2537f05cddf9SRui Paulo 			    kde.rsn_ie, kde.rsn_ie_len);
2538f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
2539f05cddf9SRui Paulo 		goto error;
2540f05cddf9SRui Paulo 	}
2541f05cddf9SRui Paulo 
2542f05cddf9SRui Paulo 	if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
2543f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2");
2544f05cddf9SRui Paulo 		status = WLAN_STATUS_INVALID_RSNIE;
2545f05cddf9SRui Paulo 		goto error;
2546f05cddf9SRui Paulo 	}
2547f05cddf9SRui Paulo 
2548f05cddf9SRui Paulo 	cipher = ie.pairwise_cipher;
2549f05cddf9SRui Paulo 	if (cipher == WPA_CIPHER_CCMP) {
2550f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
2551f05cddf9SRui Paulo 		cipher = WPA_CIPHER_CCMP;
2552f05cddf9SRui Paulo 	} else {
2553f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2");
2554f05cddf9SRui Paulo 		status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
2555f05cddf9SRui Paulo 		goto error;
2556f05cddf9SRui Paulo 	}
2557f05cddf9SRui Paulo 
2558f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2",
2559f05cddf9SRui Paulo 		    kde.ftie, sizeof(*ftie));
2560f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
2561f05cddf9SRui Paulo 
2562780fb4a2SCy Schubert 	if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) {
2563f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does "
2564f05cddf9SRui Paulo 			   "not match with FTIE SNonce used in TPK M1");
2565f05cddf9SRui Paulo 		/* Silently discard the frame */
2566f05cddf9SRui Paulo 		return -1;
2567f05cddf9SRui Paulo 	}
2568f05cddf9SRui Paulo 
2569f05cddf9SRui Paulo 	/* Responder Nonce and RSN IE */
2570f05cddf9SRui Paulo 	os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN);
2571f05cddf9SRui Paulo 	os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len);
2572f05cddf9SRui Paulo 	peer->rsnie_p_len = kde.rsn_ie_len;
2573f05cddf9SRui Paulo 	peer->cipher = cipher;
2574f05cddf9SRui Paulo 
2575f05cddf9SRui Paulo 	/* Lifetime */
2576f05cddf9SRui Paulo 	if (kde.key_lifetime == NULL) {
2577f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2");
2578f05cddf9SRui Paulo 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
2579f05cddf9SRui Paulo 		goto error;
2580f05cddf9SRui Paulo 	}
2581f05cddf9SRui Paulo 	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
2582f05cddf9SRui Paulo 	lifetime = WPA_GET_LE32(timeoutie->value);
2583f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2",
2584f05cddf9SRui Paulo 		   lifetime);
2585f05cddf9SRui Paulo 	if (lifetime != peer->lifetime) {
2586f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
2587f05cddf9SRui Paulo 			   "TPK M2 (expected %u)", lifetime, peer->lifetime);
2588f05cddf9SRui Paulo 		status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
2589f05cddf9SRui Paulo 		goto error;
2590f05cddf9SRui Paulo 	}
2591f05cddf9SRui Paulo 
2592*a90b9d01SCy Schubert 	if (peer->mld_link_id >= 0)
2593*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "TDLS: Use link ID %u for TPK derivation",
2594*a90b9d01SCy Schubert 			   peer->mld_link_id);
2595*a90b9d01SCy Schubert 	wpa_tdls_generate_tpk(peer, sm->own_addr,
2596*a90b9d01SCy Schubert 			      wpa_tdls_get_link_bssid(sm, peer->mld_link_id));
2597f05cddf9SRui Paulo 
2598f05cddf9SRui Paulo 	/* Process MIC check to see if TPK M2 is right */
2599*a90b9d01SCy Schubert 	if (wpa_supplicant_verify_tdls_mic(2, peer, (const u8 *) lnkid,
2600*a90b9d01SCy Schubert 					   (const u8 *) timeoutie, ftie,
2601*a90b9d01SCy Schubert 					   kde.ftie_len) < 0) {
2602f05cddf9SRui Paulo 		/* Discard the frame */
2603f05cddf9SRui Paulo 		wpa_tdls_del_key(sm, peer);
26045b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
2605f05cddf9SRui Paulo 		return -1;
2606f05cddf9SRui Paulo 	}
2607f05cddf9SRui Paulo 
26085b9c547cSRui Paulo 	if (wpa_tdls_set_key(sm, peer) < 0) {
26095b9c547cSRui Paulo 		/*
26105b9c547cSRui Paulo 		 * Some drivers may not be able to config the key prior to full
26115b9c547cSRui Paulo 		 * STA entry having been configured.
26125b9c547cSRui Paulo 		 */
26135b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
26145b9c547cSRui Paulo 			   "STA entry is complete");
26155b9c547cSRui Paulo 		peer->reconfig_key = 1;
26165b9c547cSRui Paulo 	}
2617f05cddf9SRui Paulo 
2618f05cddf9SRui Paulo skip_rsn:
2619f05cddf9SRui Paulo 	peer->dtoken = dtoken;
2620f05cddf9SRui Paulo 
26215b9c547cSRui Paulo 	/* add supported rates, capabilities, and qos_info to the TDLS peer */
26225b9c547cSRui Paulo 	if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
26235b9c547cSRui Paulo 		goto error;
26245b9c547cSRui Paulo 
2625f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
2626f05cddf9SRui Paulo 		   "TPK Handshake Message 3");
26275b9c547cSRui Paulo 	if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
2628780fb4a2SCy Schubert 		goto error_no_msg;
2629f05cddf9SRui Paulo 
26305b9c547cSRui Paulo 	if (!peer->tpk_success) {
26315b9c547cSRui Paulo 		/*
26325b9c547cSRui Paulo 		 * Enable Link only when tpk_success is 0, signifying that this
26335b9c547cSRui Paulo 		 * processing of TPK M2 frame is not because of a retransmission
26345b9c547cSRui Paulo 		 * during TDLS setup handshake.
26355b9c547cSRui Paulo 		 */
26365b9c547cSRui Paulo 		ret = wpa_tdls_enable_link(sm, peer);
26375b9c547cSRui Paulo 		if (ret < 0) {
26385b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
26395b9c547cSRui Paulo 			wpa_tdls_do_teardown(
26405b9c547cSRui Paulo 				sm, peer,
26415b9c547cSRui Paulo 				WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
26425b9c547cSRui Paulo 		}
26435b9c547cSRui Paulo 	}
26445b9c547cSRui Paulo 	return ret;
2645f05cddf9SRui Paulo 
2646f05cddf9SRui Paulo error:
26475b9c547cSRui Paulo 	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1,
2648f05cddf9SRui Paulo 			    status);
2649780fb4a2SCy Schubert error_no_msg:
26505b9c547cSRui Paulo 	wpa_tdls_disable_peer_link(sm, peer);
2651f05cddf9SRui Paulo 	return -1;
2652f05cddf9SRui Paulo }
2653f05cddf9SRui Paulo 
2654f05cddf9SRui Paulo 
wpa_tdls_process_tpk_m3(struct wpa_sm * sm,const u8 * src_addr,const u8 * buf,size_t len)2655f05cddf9SRui Paulo static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
2656f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
2657f05cddf9SRui Paulo {
2658f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
2659f05cddf9SRui Paulo 	struct wpa_eapol_ie_parse kde;
2660f05cddf9SRui Paulo 	struct wpa_tdls_ftie *ftie;
2661f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie *timeoutie;
2662f05cddf9SRui Paulo 	struct wpa_tdls_lnkid *lnkid;
2663f05cddf9SRui Paulo 	int ielen;
2664f05cddf9SRui Paulo 	u16 status;
2665f05cddf9SRui Paulo 	const u8 *pos;
2666f05cddf9SRui Paulo 	u32 lifetime;
26675b9c547cSRui Paulo 	int ret = 0;
2668f05cddf9SRui Paulo 
2669f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
2670f05cddf9SRui Paulo 		   "(Peer " MACSTR ")", MAC2STR(src_addr));
2671f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
2672*a90b9d01SCy Schubert 		if (ether_addr_equal(peer->addr, src_addr))
2673f05cddf9SRui Paulo 			break;
2674f05cddf9SRui Paulo 	}
2675f05cddf9SRui Paulo 	if (peer == NULL) {
2676f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
2677f05cddf9SRui Paulo 			   "TPK M3: " MACSTR, MAC2STR(src_addr));
2678f05cddf9SRui Paulo 		return -1;
2679f05cddf9SRui Paulo 	}
2680f05cddf9SRui Paulo 	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE);
2681f05cddf9SRui Paulo 
2682f05cddf9SRui Paulo 	if (len < 3 + 3)
26835b9c547cSRui Paulo 		goto error;
2684f05cddf9SRui Paulo 	pos = buf;
2685f05cddf9SRui Paulo 	pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
2686f05cddf9SRui Paulo 
2687f05cddf9SRui Paulo 	status = WPA_GET_LE16(pos);
2688f05cddf9SRui Paulo 
2689f05cddf9SRui Paulo 	if (status != 0) {
2690f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u",
2691f05cddf9SRui Paulo 			   status);
26925b9c547cSRui Paulo 		goto error;
2693f05cddf9SRui Paulo 	}
2694f05cddf9SRui Paulo 	pos += 2 /* status code */ + 1 /* dialog token */;
2695f05cddf9SRui Paulo 
2696f05cddf9SRui Paulo 	ielen = len - (pos - buf); /* start of IE in buf */
26975b9c547cSRui Paulo 
26985b9c547cSRui Paulo 	/*
26995b9c547cSRui Paulo 	 * Don't reject the message if failing to parse IEs. The IEs we need are
27005b9c547cSRui Paulo 	 * explicitly checked below. Some APs piggy-back broken IEs to the end
27015b9c547cSRui Paulo 	 * of a TDLS Confirm packet, which will fail the link if we don't ignore
27025b9c547cSRui Paulo 	 * this error.
27035b9c547cSRui Paulo 	 */
2704f05cddf9SRui Paulo 	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
27055b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
27065b9c547cSRui Paulo 			   "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
2707f05cddf9SRui Paulo 	}
2708f05cddf9SRui Paulo 
2709f05cddf9SRui Paulo 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
2710f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3");
27115b9c547cSRui Paulo 		goto error;
2712f05cddf9SRui Paulo 	}
2713f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3",
2714f05cddf9SRui Paulo 		    (u8 *) kde.lnkid, kde.lnkid_len);
2715f05cddf9SRui Paulo 	lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
2716f05cddf9SRui Paulo 
2717*a90b9d01SCy Schubert 	if (!ether_addr_equal(wpa_tdls_get_link_bssid(sm, peer->mld_link_id),
2718*a90b9d01SCy Schubert 			      lnkid->bssid)) {
2719f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
27205b9c547cSRui Paulo 		goto error;
2721f05cddf9SRui Paulo 	}
2722f05cddf9SRui Paulo 
2723f05cddf9SRui Paulo 	if (!wpa_tdls_get_privacy(sm))
2724f05cddf9SRui Paulo 		goto skip_rsn;
2725f05cddf9SRui Paulo 
2726f05cddf9SRui Paulo 	if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
2727f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3");
27285b9c547cSRui Paulo 		goto error;
2729f05cddf9SRui Paulo 	}
2730f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3",
2731f05cddf9SRui Paulo 		    kde.ftie, sizeof(*ftie));
2732f05cddf9SRui Paulo 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
2733f05cddf9SRui Paulo 
2734f05cddf9SRui Paulo 	if (kde.rsn_ie == NULL) {
2735f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3");
27365b9c547cSRui Paulo 		goto error;
2737f05cddf9SRui Paulo 	}
2738f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3",
2739f05cddf9SRui Paulo 		    kde.rsn_ie, kde.rsn_ie_len);
2740f05cddf9SRui Paulo 	if (kde.rsn_ie_len != peer->rsnie_p_len ||
2741f05cddf9SRui Paulo 	    os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) {
2742f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match "
2743f05cddf9SRui Paulo 			   "with the one sent in TPK M2");
27445b9c547cSRui Paulo 		goto error;
2745f05cddf9SRui Paulo 	}
2746f05cddf9SRui Paulo 
2747780fb4a2SCy Schubert 	if (os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) != 0) {
2748f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does "
2749f05cddf9SRui Paulo 			   "not match with FTIE ANonce used in TPK M2");
27505b9c547cSRui Paulo 		goto error;
2751f05cddf9SRui Paulo 	}
2752f05cddf9SRui Paulo 
2753780fb4a2SCy Schubert 	if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) {
2754f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not "
2755f05cddf9SRui Paulo 			   "match with FTIE SNonce used in TPK M1");
27565b9c547cSRui Paulo 		goto error;
2757f05cddf9SRui Paulo 	}
2758f05cddf9SRui Paulo 
2759f05cddf9SRui Paulo 	if (kde.key_lifetime == NULL) {
2760f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
27615b9c547cSRui Paulo 		goto error;
2762f05cddf9SRui Paulo 	}
2763f05cddf9SRui Paulo 	timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
2764f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3",
2765f05cddf9SRui Paulo 		    (u8 *) timeoutie, sizeof(*timeoutie));
2766f05cddf9SRui Paulo 	lifetime = WPA_GET_LE32(timeoutie->value);
2767f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3",
2768f05cddf9SRui Paulo 		   lifetime);
2769f05cddf9SRui Paulo 	if (lifetime != peer->lifetime) {
2770f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
2771f05cddf9SRui Paulo 			   "TPK M3 (expected %u)", lifetime, peer->lifetime);
27725b9c547cSRui Paulo 		goto error;
2773f05cddf9SRui Paulo 	}
2774f05cddf9SRui Paulo 
2775*a90b9d01SCy Schubert 	if (wpa_supplicant_verify_tdls_mic(3, peer, (const u8 *) lnkid,
2776*a90b9d01SCy Schubert 					   (const u8 *) timeoutie, ftie,
2777*a90b9d01SCy Schubert 					   kde.ftie_len) < 0) {
2778f05cddf9SRui Paulo 		wpa_tdls_del_key(sm, peer);
27795b9c547cSRui Paulo 		goto error;
2780f05cddf9SRui Paulo 	}
2781f05cddf9SRui Paulo 
27825b9c547cSRui Paulo 	if (wpa_tdls_set_key(sm, peer) < 0) {
27835b9c547cSRui Paulo 		/*
27845b9c547cSRui Paulo 		 * Some drivers may not be able to config the key prior to full
27855b9c547cSRui Paulo 		 * STA entry having been configured.
27865b9c547cSRui Paulo 		 */
27875b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
27885b9c547cSRui Paulo 			   "STA entry is complete");
27895b9c547cSRui Paulo 		peer->reconfig_key = 1;
27905b9c547cSRui Paulo 	}
2791f05cddf9SRui Paulo 
2792f05cddf9SRui Paulo skip_rsn:
27935b9c547cSRui Paulo 	/* add supported rates, capabilities, and qos_info to the TDLS peer */
27945b9c547cSRui Paulo 	if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
27955b9c547cSRui Paulo 		goto error;
2796f05cddf9SRui Paulo 
27975b9c547cSRui Paulo 	if (!peer->tpk_success) {
27985b9c547cSRui Paulo 		/*
27995b9c547cSRui Paulo 		 * Enable Link only when tpk_success is 0, signifying that this
28005b9c547cSRui Paulo 		 * processing of TPK M3 frame is not because of a retransmission
28015b9c547cSRui Paulo 		 * during TDLS setup handshake.
28025b9c547cSRui Paulo 		 */
28035b9c547cSRui Paulo 		ret = wpa_tdls_enable_link(sm, peer);
28045b9c547cSRui Paulo 		if (ret < 0) {
28055b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
28065b9c547cSRui Paulo 			goto error;
28075b9c547cSRui Paulo 		}
28085b9c547cSRui Paulo 	}
28095b9c547cSRui Paulo 	return ret;
28105b9c547cSRui Paulo error:
28115b9c547cSRui Paulo 	wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
28125b9c547cSRui Paulo 	return -1;
2813f05cddf9SRui Paulo }
2814f05cddf9SRui Paulo 
2815f05cddf9SRui Paulo 
wpa_add_tdls_timeoutie(u8 * pos,u8 * ie,size_t ie_len,u32 tsecs)2816f05cddf9SRui Paulo static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs)
2817f05cddf9SRui Paulo {
2818f05cddf9SRui Paulo 	struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie;
2819f05cddf9SRui Paulo 
2820f05cddf9SRui Paulo 	os_memset(lifetime, 0, ie_len);
2821f05cddf9SRui Paulo 	lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL;
2822f05cddf9SRui Paulo 	lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2;
2823f05cddf9SRui Paulo 	lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME;
2824f05cddf9SRui Paulo 	WPA_PUT_LE32(lifetime->value, tsecs);
2825f05cddf9SRui Paulo 	os_memcpy(pos, ie, ie_len);
2826f05cddf9SRui Paulo 	return pos + ie_len;
2827f05cddf9SRui Paulo }
2828f05cddf9SRui Paulo 
2829f05cddf9SRui Paulo 
2830f05cddf9SRui Paulo /**
2831f05cddf9SRui Paulo  * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1)
2832f05cddf9SRui Paulo  * @sm: Pointer to WPA state machine data from wpa_sm_init()
2833f05cddf9SRui Paulo  * @peer: MAC address of the peer STA
2834f05cddf9SRui Paulo  * Returns: 0 on success, or -1 on failure
2835f05cddf9SRui Paulo  *
2836f05cddf9SRui Paulo  * Send TPK Handshake Message 1 info to driver to start TDLS
2837f05cddf9SRui Paulo  * handshake with the peer.
2838f05cddf9SRui Paulo  */
wpa_tdls_start(struct wpa_sm * sm,const u8 * addr)2839f05cddf9SRui Paulo int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
2840f05cddf9SRui Paulo {
2841f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
2842f05cddf9SRui Paulo 	int tdls_prohibited = sm->tdls_prohibited;
2843c1d255d3SCy Schubert 	int res;
2844f05cddf9SRui Paulo 
2845f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
2846f05cddf9SRui Paulo 		return -1;
2847f05cddf9SRui Paulo 
2848f05cddf9SRui Paulo #ifdef CONFIG_TDLS_TESTING
2849f05cddf9SRui Paulo 	if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
2850f05cddf9SRui Paulo 	    tdls_prohibited) {
2851f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
2852f05cddf9SRui Paulo 			   "on TDLS");
2853f05cddf9SRui Paulo 		tdls_prohibited = 0;
2854f05cddf9SRui Paulo 	}
2855f05cddf9SRui Paulo #endif /* CONFIG_TDLS_TESTING */
2856f05cddf9SRui Paulo 
2857f05cddf9SRui Paulo 	if (tdls_prohibited) {
2858f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - "
2859f05cddf9SRui Paulo 			   "reject request to start setup");
2860f05cddf9SRui Paulo 		return -1;
2861f05cddf9SRui Paulo 	}
2862f05cddf9SRui Paulo 
28635b9c547cSRui Paulo 	peer = wpa_tdls_add_peer(sm, addr, NULL);
2864f05cddf9SRui Paulo 	if (peer == NULL)
2865f05cddf9SRui Paulo 		return -1;
28665b9c547cSRui Paulo 
28675b9c547cSRui Paulo 	if (peer->tpk_in_progress) {
28685b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer");
28695b9c547cSRui Paulo 		return 0;
2870f05cddf9SRui Paulo 	}
2871f05cddf9SRui Paulo 
2872*a90b9d01SCy Schubert 	if (sm->mlo.valid_links && !peer->disc_resp_rcvd) {
2873*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
2874*a90b9d01SCy Schubert 			   "TDLS: MLO STA connection - defer the setup request since Discovery Resp not yet received");
2875*a90b9d01SCy Schubert 		peer->setup_req_rcvd = true;
2876*a90b9d01SCy Schubert 		return 0;
2877*a90b9d01SCy Schubert 	}
2878f05cddf9SRui Paulo 	peer->initiator = 1;
2879f05cddf9SRui Paulo 
2880f05cddf9SRui Paulo 	/* add the peer to the driver as a "setup in progress" peer */
28815b9c547cSRui Paulo 	if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
28824b72b91aSCy Schubert 				    NULL, NULL, 0, NULL, 0, 0, NULL, 0, NULL, 0,
2883*a90b9d01SCy Schubert 				    NULL, 0, NULL, 0, peer->mld_link_id)) {
28845b9c547cSRui Paulo 		wpa_tdls_disable_peer_link(sm, peer);
28855b9c547cSRui Paulo 		return -1;
28865b9c547cSRui Paulo 	}
28875b9c547cSRui Paulo 
28885b9c547cSRui Paulo 	peer->tpk_in_progress = 1;
2889f05cddf9SRui Paulo 
2890c1d255d3SCy Schubert 	res = wpa_tdls_send_tpk_m1(sm, peer);
2891c1d255d3SCy Schubert 	if (res < 0) {
2892c1d255d3SCy Schubert 		if (res != -2)
28935b9c547cSRui Paulo 			wpa_tdls_disable_peer_link(sm, peer);
2894f05cddf9SRui Paulo 		return -1;
2895f05cddf9SRui Paulo 	}
2896f05cddf9SRui Paulo 
2897f05cddf9SRui Paulo 	return 0;
2898f05cddf9SRui Paulo }
2899f05cddf9SRui Paulo 
2900f05cddf9SRui Paulo 
wpa_tdls_remove(struct wpa_sm * sm,const u8 * addr)29015b9c547cSRui Paulo void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr)
2902f05cddf9SRui Paulo {
2903f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer;
2904f05cddf9SRui Paulo 
2905f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
29065b9c547cSRui Paulo 		return;
2907f05cddf9SRui Paulo 
2908f05cddf9SRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
2909*a90b9d01SCy Schubert 		if (ether_addr_equal(peer->addr, addr))
2910f05cddf9SRui Paulo 			break;
2911f05cddf9SRui Paulo 	}
2912f05cddf9SRui Paulo 
2913f05cddf9SRui Paulo 	if (peer == NULL || !peer->tpk_success)
29145b9c547cSRui Paulo 		return;
2915f05cddf9SRui Paulo 
2916f05cddf9SRui Paulo 	if (sm->tdls_external_setup) {
2917f05cddf9SRui Paulo 		/*
2918f05cddf9SRui Paulo 		 * Disable previous link to allow renegotiation to be completed
2919f05cddf9SRui Paulo 		 * on AP path.
2920f05cddf9SRui Paulo 		 */
29215b9c547cSRui Paulo 		wpa_tdls_do_teardown(sm, peer,
29225b9c547cSRui Paulo 				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
2923f05cddf9SRui Paulo 	}
2924f05cddf9SRui Paulo }
2925f05cddf9SRui Paulo 
2926f05cddf9SRui Paulo 
2927f05cddf9SRui Paulo /**
2928f05cddf9SRui Paulo  * wpa_supplicant_rx_tdls - Receive TDLS data frame
2929f05cddf9SRui Paulo  *
2930f05cddf9SRui Paulo  * This function is called to receive TDLS (ethertype = 0x890d) data frames.
2931f05cddf9SRui Paulo  */
wpa_supplicant_rx_tdls(void * ctx,const u8 * src_addr,const u8 * buf,size_t len)2932f05cddf9SRui Paulo static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr,
2933f05cddf9SRui Paulo 				   const u8 *buf, size_t len)
2934f05cddf9SRui Paulo {
2935f05cddf9SRui Paulo 	struct wpa_sm *sm = ctx;
2936f05cddf9SRui Paulo 	struct wpa_tdls_frame *tf;
2937f05cddf9SRui Paulo 
2938f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation",
2939f05cddf9SRui Paulo 		    buf, len);
2940f05cddf9SRui Paulo 
2941f05cddf9SRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported) {
2942f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled "
2943f05cddf9SRui Paulo 			   "or unsupported by driver");
2944f05cddf9SRui Paulo 		return;
2945f05cddf9SRui Paulo 	}
2946f05cddf9SRui Paulo 
2947*a90b9d01SCy Schubert 	if (ether_addr_equal(src_addr, sm->own_addr)) {
2948f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message");
2949f05cddf9SRui Paulo 		return;
2950f05cddf9SRui Paulo 	}
2951f05cddf9SRui Paulo 
2952f05cddf9SRui Paulo 	if (len < sizeof(*tf)) {
2953f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Drop too short frame");
2954f05cddf9SRui Paulo 		return;
2955f05cddf9SRui Paulo 	}
2956f05cddf9SRui Paulo 
2957f05cddf9SRui Paulo 	/* Check to make sure its a valid encapsulated TDLS frame */
2958f05cddf9SRui Paulo 	tf = (struct wpa_tdls_frame *) buf;
2959f05cddf9SRui Paulo 	if (tf->payloadtype != 2 /* TDLS_RFTYPE */ ||
2960f05cddf9SRui Paulo 	    tf->category != WLAN_ACTION_TDLS) {
2961f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u "
2962f05cddf9SRui Paulo 			   "category=%u action=%u",
2963f05cddf9SRui Paulo 			   tf->payloadtype, tf->category, tf->action);
2964f05cddf9SRui Paulo 		return;
2965f05cddf9SRui Paulo 	}
2966f05cddf9SRui Paulo 
2967f05cddf9SRui Paulo 	switch (tf->action) {
2968f05cddf9SRui Paulo 	case WLAN_TDLS_SETUP_REQUEST:
2969f05cddf9SRui Paulo 		wpa_tdls_process_tpk_m1(sm, src_addr, buf, len);
2970f05cddf9SRui Paulo 		break;
2971f05cddf9SRui Paulo 	case WLAN_TDLS_SETUP_RESPONSE:
2972f05cddf9SRui Paulo 		wpa_tdls_process_tpk_m2(sm, src_addr, buf, len);
2973f05cddf9SRui Paulo 		break;
2974f05cddf9SRui Paulo 	case WLAN_TDLS_SETUP_CONFIRM:
2975f05cddf9SRui Paulo 		wpa_tdls_process_tpk_m3(sm, src_addr, buf, len);
2976f05cddf9SRui Paulo 		break;
2977f05cddf9SRui Paulo 	case WLAN_TDLS_TEARDOWN:
2978f05cddf9SRui Paulo 		wpa_tdls_recv_teardown(sm, src_addr, buf, len);
2979f05cddf9SRui Paulo 		break;
2980f05cddf9SRui Paulo 	case WLAN_TDLS_DISCOVERY_REQUEST:
2981f05cddf9SRui Paulo 		wpa_tdls_process_discovery_request(sm, src_addr, buf, len);
2982f05cddf9SRui Paulo 		break;
2983f05cddf9SRui Paulo 	default:
2984f05cddf9SRui Paulo 		/* Kernel code will process remaining frames */
2985f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u",
2986f05cddf9SRui Paulo 			   tf->action);
2987f05cddf9SRui Paulo 		break;
2988f05cddf9SRui Paulo 	}
2989f05cddf9SRui Paulo }
2990f05cddf9SRui Paulo 
2991f05cddf9SRui Paulo 
2992f05cddf9SRui Paulo /**
2993f05cddf9SRui Paulo  * wpa_tdls_init - Initialize driver interface parameters for TDLS
2994f05cddf9SRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
2995f05cddf9SRui Paulo  * Returns: 0 on success, -1 on failure
2996f05cddf9SRui Paulo  *
2997f05cddf9SRui Paulo  * This function is called to initialize driver interface parameters for TDLS.
2998f05cddf9SRui Paulo  * wpa_drv_init() must have been called before this function to initialize the
2999f05cddf9SRui Paulo  * driver interface.
3000f05cddf9SRui Paulo  */
wpa_tdls_init(struct wpa_sm * sm)3001f05cddf9SRui Paulo int wpa_tdls_init(struct wpa_sm *sm)
3002f05cddf9SRui Paulo {
3003f05cddf9SRui Paulo 	if (sm == NULL)
3004f05cddf9SRui Paulo 		return -1;
3005f05cddf9SRui Paulo 
3006c1d255d3SCy Schubert 	if (sm->l2_tdls) {
3007c1d255d3SCy Schubert 		l2_packet_deinit(sm->l2_tdls);
3008c1d255d3SCy Schubert 		sm->l2_tdls = NULL;
3009c1d255d3SCy Schubert 	}
3010c1d255d3SCy Schubert 
3011f05cddf9SRui Paulo 	sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
3012f05cddf9SRui Paulo 				     sm->ifname,
3013f05cddf9SRui Paulo 				     sm->own_addr,
3014f05cddf9SRui Paulo 				     ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
3015f05cddf9SRui Paulo 				     sm, 0);
3016f05cddf9SRui Paulo 	if (sm->l2_tdls == NULL) {
3017f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet "
3018f05cddf9SRui Paulo 			   "connection");
3019f05cddf9SRui Paulo 		return -1;
3020f05cddf9SRui Paulo 	}
3021f05cddf9SRui Paulo 
3022f05cddf9SRui Paulo 	/*
3023f05cddf9SRui Paulo 	 * Drivers that support TDLS but don't implement the get_capa callback
3024f05cddf9SRui Paulo 	 * are assumed to perform everything internally
3025f05cddf9SRui Paulo 	 */
3026f05cddf9SRui Paulo 	if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported,
30275b9c547cSRui Paulo 				 &sm->tdls_external_setup,
30285b9c547cSRui Paulo 				 &sm->tdls_chan_switch) < 0) {
3029f05cddf9SRui Paulo 		sm->tdls_supported = 1;
3030f05cddf9SRui Paulo 		sm->tdls_external_setup = 0;
3031f05cddf9SRui Paulo 	}
3032f05cddf9SRui Paulo 
3033f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by "
3034f05cddf9SRui Paulo 		   "driver", sm->tdls_supported ? "" : " not");
3035f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup",
3036f05cddf9SRui Paulo 		   sm->tdls_external_setup ? "external" : "internal");
30375b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching",
30385b9c547cSRui Paulo 		   sm->tdls_chan_switch ? "supports" : "does not support");
3039f05cddf9SRui Paulo 
3040f05cddf9SRui Paulo 	return 0;
3041f05cddf9SRui Paulo }
3042f05cddf9SRui Paulo 
3043f05cddf9SRui Paulo 
wpa_tdls_teardown_peers(struct wpa_sm * sm)30445b9c547cSRui Paulo void wpa_tdls_teardown_peers(struct wpa_sm *sm)
30455b9c547cSRui Paulo {
30465b9c547cSRui Paulo 	struct wpa_tdls_peer *peer, *tmp;
30475b9c547cSRui Paulo 
30485b9c547cSRui Paulo 	if (!sm)
30495b9c547cSRui Paulo 		return;
30505b9c547cSRui Paulo 	peer = sm->tdls;
30515b9c547cSRui Paulo 
30525b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
30535b9c547cSRui Paulo 
30545b9c547cSRui Paulo 	while (peer) {
30555b9c547cSRui Paulo 		tmp = peer->next;
30565b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR,
30575b9c547cSRui Paulo 			   MAC2STR(peer->addr));
30585b9c547cSRui Paulo 		if (sm->tdls_external_setup)
30595b9c547cSRui Paulo 			wpa_tdls_do_teardown(sm, peer,
30605b9c547cSRui Paulo 					     WLAN_REASON_DEAUTH_LEAVING);
30615b9c547cSRui Paulo 		else
30625b9c547cSRui Paulo 			wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
30635b9c547cSRui Paulo 
30645b9c547cSRui Paulo 		peer = tmp;
30655b9c547cSRui Paulo 	}
30665b9c547cSRui Paulo }
30675b9c547cSRui Paulo 
30685b9c547cSRui Paulo 
wpa_tdls_remove_peers(struct wpa_sm * sm)3069f05cddf9SRui Paulo static void wpa_tdls_remove_peers(struct wpa_sm *sm)
3070f05cddf9SRui Paulo {
3071f05cddf9SRui Paulo 	struct wpa_tdls_peer *peer, *tmp;
3072f05cddf9SRui Paulo 
3073f05cddf9SRui Paulo 	peer = sm->tdls;
3074f05cddf9SRui Paulo 
3075f05cddf9SRui Paulo 	while (peer) {
3076f05cddf9SRui Paulo 		int res;
3077f05cddf9SRui Paulo 		tmp = peer->next;
3078f05cddf9SRui Paulo 		res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
3079f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
3080f05cddf9SRui Paulo 			   MAC2STR(peer->addr), res);
3081f05cddf9SRui Paulo 		wpa_tdls_peer_free(sm, peer);
3082f05cddf9SRui Paulo 		peer = tmp;
3083f05cddf9SRui Paulo 	}
3084f05cddf9SRui Paulo }
3085f05cddf9SRui Paulo 
3086f05cddf9SRui Paulo 
3087f05cddf9SRui Paulo /**
3088f05cddf9SRui Paulo  * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS
3089f05cddf9SRui Paulo  *
3090f05cddf9SRui Paulo  * This function is called to recover driver interface parameters for TDLS
3091f05cddf9SRui Paulo  * and frees resources allocated for it.
3092f05cddf9SRui Paulo  */
wpa_tdls_deinit(struct wpa_sm * sm)3093f05cddf9SRui Paulo void wpa_tdls_deinit(struct wpa_sm *sm)
3094f05cddf9SRui Paulo {
3095f05cddf9SRui Paulo 	if (sm == NULL)
3096f05cddf9SRui Paulo 		return;
3097f05cddf9SRui Paulo 
3098f05cddf9SRui Paulo 	if (sm->l2_tdls)
3099f05cddf9SRui Paulo 		l2_packet_deinit(sm->l2_tdls);
3100f05cddf9SRui Paulo 	sm->l2_tdls = NULL;
3101f05cddf9SRui Paulo 
3102f05cddf9SRui Paulo 	wpa_tdls_remove_peers(sm);
3103f05cddf9SRui Paulo }
3104f05cddf9SRui Paulo 
3105f05cddf9SRui Paulo 
wpa_tdls_assoc(struct wpa_sm * sm)3106f05cddf9SRui Paulo void wpa_tdls_assoc(struct wpa_sm *sm)
3107f05cddf9SRui Paulo {
3108f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association");
3109f05cddf9SRui Paulo 	wpa_tdls_remove_peers(sm);
3110f05cddf9SRui Paulo }
3111f05cddf9SRui Paulo 
3112f05cddf9SRui Paulo 
wpa_tdls_disassoc(struct wpa_sm * sm)3113f05cddf9SRui Paulo void wpa_tdls_disassoc(struct wpa_sm *sm)
3114f05cddf9SRui Paulo {
3115f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation");
3116f05cddf9SRui Paulo 	wpa_tdls_remove_peers(sm);
3117f05cddf9SRui Paulo }
3118f05cddf9SRui Paulo 
3119f05cddf9SRui Paulo 
wpa_tdls_prohibited(struct ieee802_11_elems * elems)3120325151a3SRui Paulo static int wpa_tdls_prohibited(struct ieee802_11_elems *elems)
3121f05cddf9SRui Paulo {
3122f05cddf9SRui Paulo 	/* bit 38 - TDLS Prohibited */
312385732ac8SCy Schubert 	return !!(elems->ext_capab[4] & 0x40);
31245b9c547cSRui Paulo }
31255b9c547cSRui Paulo 
31265b9c547cSRui Paulo 
wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems * elems)3127325151a3SRui Paulo static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems)
31285b9c547cSRui Paulo {
31295b9c547cSRui Paulo 	/* bit 39 - TDLS Channel Switch Prohibited */
313085732ac8SCy Schubert 	return !!(elems->ext_capab[4] & 0x80);
3131f05cddf9SRui Paulo }
3132f05cddf9SRui Paulo 
3133f05cddf9SRui Paulo 
wpa_tdls_ap_ies(struct wpa_sm * sm,const u8 * ies,size_t len)3134f05cddf9SRui Paulo void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
3135f05cddf9SRui Paulo {
3136325151a3SRui Paulo 	struct ieee802_11_elems elems;
31375b9c547cSRui Paulo 
31385b9c547cSRui Paulo 	sm->tdls_prohibited = 0;
31395b9c547cSRui Paulo 	sm->tdls_chan_switch_prohibited = 0;
31405b9c547cSRui Paulo 
3141325151a3SRui Paulo 	if (ies == NULL ||
3142325151a3SRui Paulo 	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
314385732ac8SCy Schubert 	    elems.ext_capab == NULL || elems.ext_capab_len < 5)
31445b9c547cSRui Paulo 		return;
31455b9c547cSRui Paulo 
31465b9c547cSRui Paulo 	sm->tdls_prohibited = wpa_tdls_prohibited(&elems);
3147f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS",
3148f05cddf9SRui Paulo 		   sm->tdls_prohibited ? "prohibited" : "allowed");
31495b9c547cSRui Paulo 	sm->tdls_chan_switch_prohibited =
31505b9c547cSRui Paulo 		wpa_tdls_chan_switch_prohibited(&elems);
31515b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS",
31525b9c547cSRui Paulo 		   sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed");
3153f05cddf9SRui Paulo }
3154f05cddf9SRui Paulo 
3155f05cddf9SRui Paulo 
wpa_tdls_assoc_resp_ies(struct wpa_sm * sm,const u8 * ies,size_t len)3156f05cddf9SRui Paulo void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
3157f05cddf9SRui Paulo {
3158325151a3SRui Paulo 	struct ieee802_11_elems elems;
31595b9c547cSRui Paulo 
3160325151a3SRui Paulo 	if (ies == NULL ||
3161325151a3SRui Paulo 	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
316285732ac8SCy Schubert 	    elems.ext_capab == NULL || elems.ext_capab_len < 5)
31635b9c547cSRui Paulo 		return;
31645b9c547cSRui Paulo 
31655b9c547cSRui Paulo 	if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) {
3166f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on "
3167f05cddf9SRui Paulo 			   "(Re)Association Response IEs");
3168f05cddf9SRui Paulo 		sm->tdls_prohibited = 1;
3169f05cddf9SRui Paulo 	}
31705b9c547cSRui Paulo 
31715b9c547cSRui Paulo 	if (!sm->tdls_chan_switch_prohibited &&
31725b9c547cSRui Paulo 	    wpa_tdls_chan_switch_prohibited(&elems)) {
31735b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
31745b9c547cSRui Paulo 			   "TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs");
31755b9c547cSRui Paulo 		sm->tdls_chan_switch_prohibited = 1;
31765b9c547cSRui Paulo 	}
3177f05cddf9SRui Paulo }
3178f05cddf9SRui Paulo 
3179f05cddf9SRui Paulo 
wpa_tdls_enable(struct wpa_sm * sm,int enabled)3180f05cddf9SRui Paulo void wpa_tdls_enable(struct wpa_sm *sm, int enabled)
3181f05cddf9SRui Paulo {
3182f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled");
3183f05cddf9SRui Paulo 	sm->tdls_disabled = !enabled;
3184f05cddf9SRui Paulo }
3185f05cddf9SRui Paulo 
3186f05cddf9SRui Paulo 
wpa_tdls_is_external_setup(struct wpa_sm * sm)3187f05cddf9SRui Paulo int wpa_tdls_is_external_setup(struct wpa_sm *sm)
3188f05cddf9SRui Paulo {
3189f05cddf9SRui Paulo 	return sm->tdls_external_setup;
3190f05cddf9SRui Paulo }
31915b9c547cSRui Paulo 
31925b9c547cSRui Paulo 
wpa_tdls_process_discovery_response(struct wpa_sm * sm,const u8 * addr,const u8 * buf,size_t len)3193*a90b9d01SCy Schubert int wpa_tdls_process_discovery_response(struct wpa_sm *sm, const u8 *addr,
3194*a90b9d01SCy Schubert 					const u8 *buf, size_t len)
3195*a90b9d01SCy Schubert {
3196*a90b9d01SCy Schubert 	struct ieee802_11_elems elems;
3197*a90b9d01SCy Schubert 	const struct wpa_tdls_lnkid *lnkid;
3198*a90b9d01SCy Schubert 	struct wpa_tdls_peer *peer;
3199*a90b9d01SCy Schubert 	size_t min_req_len = 1 /* Dialog Token */ + 2 /* Capability */ +
3200*a90b9d01SCy Schubert 		sizeof(struct wpa_tdls_lnkid);
3201*a90b9d01SCy Schubert 	int link_id = -1;
3202*a90b9d01SCy Schubert 
3203*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG, "TDLS: Process Discovery Response from " MACSTR,
3204*a90b9d01SCy Schubert 		   MAC2STR(addr));
3205*a90b9d01SCy Schubert 
3206*a90b9d01SCy Schubert 	if (len < min_req_len) {
3207*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "TDLS Discovery Resp is too short: %zu",
3208*a90b9d01SCy Schubert 			   len);
3209*a90b9d01SCy Schubert 		return -1;
3210*a90b9d01SCy Schubert 	}
3211*a90b9d01SCy Schubert 
3212*a90b9d01SCy Schubert 	/* Elements start after the three octets of fixed field (one octet for
3213*a90b9d01SCy Schubert 	 * the Dialog Token field and two octets for the Capability field. */
3214*a90b9d01SCy Schubert 	if (ieee802_11_parse_elems(buf + 3, len - 3, &elems, 1) ==
3215*a90b9d01SCy Schubert 	    ParseFailed) {
3216*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
3217*a90b9d01SCy Schubert 			   "TDLS: Failed to parse IEs in Discovery Response");
3218*a90b9d01SCy Schubert 		return -1;
3219*a90b9d01SCy Schubert 	}
3220*a90b9d01SCy Schubert 
3221*a90b9d01SCy Schubert 	if (!elems.link_id) {
3222*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
3223*a90b9d01SCy Schubert 			   "TDLS: Link Identifier element not found in Discovery Response");
3224*a90b9d01SCy Schubert 		return -1;
3225*a90b9d01SCy Schubert 	}
3226*a90b9d01SCy Schubert 
3227*a90b9d01SCy Schubert 	lnkid = (const struct wpa_tdls_lnkid *) (elems.link_id - 2);
3228*a90b9d01SCy Schubert 
3229*a90b9d01SCy Schubert 	if (!wpa_tdls_is_lnkid_bss_valid(sm, lnkid, &link_id)) {
3230*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
3231*a90b9d01SCy Schubert 			   "TDLS: Discovery Response from different BSS "
3232*a90b9d01SCy Schubert 			   MACSTR, MAC2STR(lnkid->bssid));
3233*a90b9d01SCy Schubert 		return -1;
3234*a90b9d01SCy Schubert 	}
3235*a90b9d01SCy Schubert 
3236*a90b9d01SCy Schubert 	peer = wpa_tdls_add_peer(sm, addr, NULL);
3237*a90b9d01SCy Schubert 	if (!peer) {
3238*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "TDLS: Could not add peer entry");
3239*a90b9d01SCy Schubert 		return -1;
3240*a90b9d01SCy Schubert 	}
3241*a90b9d01SCy Schubert 
3242*a90b9d01SCy Schubert 	peer->mld_link_id = link_id;
3243*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG, "TDLS: Link identifier BSS: " MACSTR
3244*a90b9d01SCy Schubert 		   " , link id: %u", MAC2STR(lnkid->bssid), link_id);
3245*a90b9d01SCy Schubert 
3246*a90b9d01SCy Schubert 	peer->disc_resp_rcvd = true;
3247*a90b9d01SCy Schubert 	if (peer->setup_req_rcvd) {
3248*a90b9d01SCy Schubert 		peer->setup_req_rcvd = false;
3249*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "TDLS: Process the deferred TDLS start");
3250*a90b9d01SCy Schubert 		return wpa_tdls_start(sm, addr);
3251*a90b9d01SCy Schubert 	}
3252*a90b9d01SCy Schubert 
3253*a90b9d01SCy Schubert 	return 0;
3254*a90b9d01SCy Schubert }
3255*a90b9d01SCy Schubert 
3256*a90b9d01SCy Schubert 
wpa_tdls_enable_chan_switch(struct wpa_sm * sm,const u8 * addr,u8 oper_class,struct hostapd_freq_params * freq_params)32575b9c547cSRui Paulo int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
32585b9c547cSRui Paulo 				u8 oper_class,
32595b9c547cSRui Paulo 				struct hostapd_freq_params *freq_params)
32605b9c547cSRui Paulo {
32615b9c547cSRui Paulo 	struct wpa_tdls_peer *peer;
32625b9c547cSRui Paulo 	int ret;
32635b9c547cSRui Paulo 
32645b9c547cSRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
32655b9c547cSRui Paulo 		return -1;
32665b9c547cSRui Paulo 
32675b9c547cSRui Paulo 	if (!sm->tdls_chan_switch) {
32685b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
32695b9c547cSRui Paulo 			   "TDLS: Channel switching not supported by the driver");
32705b9c547cSRui Paulo 		return -1;
32715b9c547cSRui Paulo 	}
32725b9c547cSRui Paulo 
32735b9c547cSRui Paulo 	if (sm->tdls_chan_switch_prohibited) {
32745b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
32755b9c547cSRui Paulo 			   "TDLS: Channel switching is prohibited in this BSS - reject request to switch channel");
32765b9c547cSRui Paulo 		return -1;
32775b9c547cSRui Paulo 	}
32785b9c547cSRui Paulo 
32795b9c547cSRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
3280*a90b9d01SCy Schubert 		if (ether_addr_equal(peer->addr, addr))
32815b9c547cSRui Paulo 			break;
32825b9c547cSRui Paulo 	}
32835b9c547cSRui Paulo 
32845b9c547cSRui Paulo 	if (peer == NULL || !peer->tpk_success) {
32855b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR
32865b9c547cSRui Paulo 			   " not found for channel switching", MAC2STR(addr));
32875b9c547cSRui Paulo 		return -1;
32885b9c547cSRui Paulo 	}
32895b9c547cSRui Paulo 
32905b9c547cSRui Paulo 	if (peer->chan_switch_enabled) {
32915b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
32925b9c547cSRui Paulo 			   " already has channel switching enabled",
32935b9c547cSRui Paulo 			   MAC2STR(addr));
32945b9c547cSRui Paulo 		return 0;
32955b9c547cSRui Paulo 	}
32965b9c547cSRui Paulo 
32975b9c547cSRui Paulo 	ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr,
32985b9c547cSRui Paulo 						oper_class, freq_params);
32995b9c547cSRui Paulo 	if (!ret)
33005b9c547cSRui Paulo 		peer->chan_switch_enabled = 1;
33015b9c547cSRui Paulo 
33025b9c547cSRui Paulo 	return ret;
33035b9c547cSRui Paulo }
33045b9c547cSRui Paulo 
33055b9c547cSRui Paulo 
wpa_tdls_disable_chan_switch(struct wpa_sm * sm,const u8 * addr)33065b9c547cSRui Paulo int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr)
33075b9c547cSRui Paulo {
33085b9c547cSRui Paulo 	struct wpa_tdls_peer *peer;
33095b9c547cSRui Paulo 
33105b9c547cSRui Paulo 	if (sm->tdls_disabled || !sm->tdls_supported)
33115b9c547cSRui Paulo 		return -1;
33125b9c547cSRui Paulo 
33135b9c547cSRui Paulo 	for (peer = sm->tdls; peer; peer = peer->next) {
3314*a90b9d01SCy Schubert 		if (ether_addr_equal(peer->addr, addr))
33155b9c547cSRui Paulo 			break;
33165b9c547cSRui Paulo 	}
33175b9c547cSRui Paulo 
33185b9c547cSRui Paulo 	if (!peer || !peer->chan_switch_enabled) {
33195b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for "
33205b9c547cSRui Paulo 			   MACSTR, MAC2STR(addr));
33215b9c547cSRui Paulo 		return -1;
33225b9c547cSRui Paulo 	}
33235b9c547cSRui Paulo 
33245b9c547cSRui Paulo 	/* ignore the return value */
33255b9c547cSRui Paulo 	wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
33265b9c547cSRui Paulo 
33275b9c547cSRui Paulo 	peer->chan_switch_enabled = 0;
33285b9c547cSRui Paulo 	return 0;
33295b9c547cSRui Paulo }
3330