2 * Received Data frame processing for TDLS packets
3 * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
9 #include "utils/includes.h"
11 #include "utils/common.h"
12 #include "crypto/sha256.h"
13 #include "crypto/crypto.h"
14 #include "crypto/aes_wrap.h"
15 #include "common/ieee802_11_defs.h"
16 #include "common/ieee802_11_common.h"
20 static struct wlantest_tdls
* get_tdls(struct wlantest
*wt
, const u8
*linkid
,
21 int create_new
, const u8
*bssid
)
23 struct wlantest_bss
*bss
;
24 struct wlantest_sta
*init
, *resp
;
25 struct wlantest_tdls
*tdls
;
27 bss
= bss_find(wt
, linkid
);
28 if (bss
== NULL
&& bssid
) {
29 bss
= bss_find(wt
, bssid
);
31 wpa_printf(MSG_INFO
, "TDLS: Incorrect BSSID " MACSTR
32 " in LinkId?! (init=" MACSTR
" resp="
34 MAC2STR(linkid
), MAC2STR(linkid
+ ETH_ALEN
),
35 MAC2STR(linkid
+ 2 * ETH_ALEN
));
40 init
= sta_find(bss
, linkid
+ ETH_ALEN
);
44 resp
= sta_find(bss
, linkid
+ 2 * ETH_ALEN
);
48 dl_list_for_each(tdls
, &bss
->tdls
, struct wlantest_tdls
, list
) {
49 if (tdls
->init
== init
&& tdls
->resp
== resp
)
56 tdls
= os_zalloc(sizeof(*tdls
));
61 dl_list_add(&bss
->tdls
, &tdls
->list
);
66 static int tdls_derive_tpk(struct wlantest_tdls
*tdls
, const u8
*bssid
,
67 const u8
*ftie
, u8 ftie_len
)
69 const struct rsn_ftie
*f
;
70 u8 key_input
[SHA256_MAC_LEN
];
73 u8 data
[3 * ETH_ALEN
];
75 if (ftie
== NULL
|| ftie_len
< sizeof(struct rsn_ftie
))
78 f
= (const struct rsn_ftie
*) ftie
;
79 wpa_hexdump(MSG_DEBUG
, "TDLS ANonce", f
->anonce
, WPA_NONCE_LEN
);
80 wpa_hexdump(MSG_DEBUG
, "TDLS SNonce", f
->snonce
, WPA_NONCE_LEN
);
83 * IEEE Std 802.11z-2010 8.5.9.1:
84 * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
86 len
[0] = WPA_NONCE_LEN
;
87 len
[1] = WPA_NONCE_LEN
;
88 if (os_memcmp(f
->anonce
, f
->snonce
, WPA_NONCE_LEN
) < 0) {
95 sha256_vector(2, nonce
, len
, key_input
);
96 wpa_hexdump_key(MSG_DEBUG
, "TDLS: TPK-Key-Input",
97 key_input
, SHA256_MAC_LEN
);
100 * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
101 * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
102 * TODO: is N_KEY really included in KDF Context and if so, in which
103 * presentation format (little endian 16-bit?) is it used? It gets
104 * added by the KDF anyway..
107 if (os_memcmp(tdls
->init
->addr
, tdls
->resp
->addr
, ETH_ALEN
) < 0) {
108 os_memcpy(data
, tdls
->init
->addr
, ETH_ALEN
);
109 os_memcpy(data
+ ETH_ALEN
, tdls
->resp
->addr
, ETH_ALEN
);
111 os_memcpy(data
, tdls
->resp
->addr
, ETH_ALEN
);
112 os_memcpy(data
+ ETH_ALEN
, tdls
->init
->addr
, ETH_ALEN
);
114 os_memcpy(data
+ 2 * ETH_ALEN
, bssid
, ETH_ALEN
);
115 wpa_hexdump(MSG_DEBUG
, "TDLS: KDF Context", data
, sizeof(data
));
117 sha256_prf(key_input
, SHA256_MAC_LEN
, "TDLS PMK", data
, sizeof(data
),
118 (u8
*) &tdls
->tpk
, sizeof(tdls
->tpk
));
119 wpa_hexdump_key(MSG_DEBUG
, "TDLS: TPK-KCK",
120 tdls
->tpk
.kck
, sizeof(tdls
->tpk
.kck
));
121 wpa_hexdump_key(MSG_DEBUG
, "TDLS: TPK-TK",
122 tdls
->tpk
.tk
, sizeof(tdls
->tpk
.tk
));
128 static int tdls_verify_mic(struct wlantest_tdls
*tdls
, u8 trans_seq
,
129 struct ieee802_11_elems
*elems
)
135 const struct rsn_ftie
*rx_ftie
;
136 struct rsn_ftie
*tmp_ftie
;
138 if (elems
->link_id
== NULL
|| elems
->rsn_ie
== NULL
||
139 elems
->timeout_int
== NULL
|| elems
->ftie
== NULL
)
142 len
= 2 * ETH_ALEN
+ 1 + 2 + 18 + 2 + elems
->rsn_ie_len
+
143 2 + elems
->timeout_int_len
+ 2 + elems
->ftie_len
;
145 buf
= os_zalloc(len
);
150 /* 1) TDLS initiator STA MAC address */
151 os_memcpy(pos
, elems
->link_id
+ ETH_ALEN
, ETH_ALEN
);
153 /* 2) TDLS responder STA MAC address */
154 os_memcpy(pos
, elems
->link_id
+ 2 * ETH_ALEN
, ETH_ALEN
);
156 /* 3) Transaction Sequence number */
158 /* 4) Link Identifier IE */
159 os_memcpy(pos
, elems
->link_id
- 2, 2 + 18);
162 os_memcpy(pos
, elems
->rsn_ie
- 2, 2 + elems
->rsn_ie_len
);
163 pos
+= 2 + elems
->rsn_ie_len
;
164 /* 6) Timeout Interval IE */
165 os_memcpy(pos
, elems
->timeout_int
- 2, 2 + elems
->timeout_int_len
);
166 pos
+= 2 + elems
->timeout_int_len
;
167 /* 7) FTIE, with the MIC field of the FTIE set to 0 */
168 os_memcpy(pos
, elems
->ftie
- 2, 2 + elems
->ftie_len
);
170 tmp_ftie
= (struct rsn_ftie
*) pos
;
171 os_memset(tmp_ftie
->mic
, 0, 16);
172 pos
+= elems
->ftie_len
;
174 wpa_hexdump(MSG_DEBUG
, "TDLS: Data for FTIE MIC", buf
, pos
- buf
);
175 wpa_hexdump_key(MSG_DEBUG
, "TDLS: KCK", tdls
->tpk
.kck
, 16);
176 ret
= omac1_aes_128(tdls
->tpk
.kck
, buf
, pos
- buf
, mic
);
180 wpa_hexdump(MSG_DEBUG
, "TDLS: FTIE MIC", mic
, 16);
181 rx_ftie
= (const struct rsn_ftie
*) elems
->ftie
;
183 if (os_memcmp(mic
, rx_ftie
->mic
, 16) == 0) {
184 wpa_printf(MSG_DEBUG
, "TDLS: Valid MIC");
187 wpa_printf(MSG_DEBUG
, "TDLS: Invalid MIC");
192 static void rx_data_tdls_setup_request(struct wlantest
*wt
, const u8
*bssid
,
193 const u8
*sta_addr
, const u8
*dst
,
195 const u8
*data
, size_t len
)
197 struct ieee802_11_elems elems
;
198 struct wlantest_tdls
*tdls
;
201 wpa_printf(MSG_INFO
, "Too short TDLS Setup Request " MACSTR
202 " -> " MACSTR
, MAC2STR(src
), MAC2STR(dst
));
205 wpa_printf(MSG_DEBUG
, "TDLS Setup Request " MACSTR
" -> "
206 MACSTR
, MAC2STR(src
), MAC2STR(dst
));
208 if (ieee802_11_parse_elems(data
+ 3, len
- 3, &elems
, 1) ==
209 ParseFailed
|| elems
.link_id
== NULL
)
211 wpa_printf(MSG_DEBUG
, "TDLS Link Identifier: BSSID " MACSTR
212 " initiator STA " MACSTR
" responder STA " MACSTR
,
213 MAC2STR(elems
.link_id
), MAC2STR(elems
.link_id
+ ETH_ALEN
),
214 MAC2STR(elems
.link_id
+ 2 * ETH_ALEN
));
215 tdls
= get_tdls(wt
, elems
.link_id
, 1, bssid
);
217 tdls
->counters
[WLANTEST_TDLS_COUNTER_SETUP_REQ
]++;
218 tdls
->dialog_token
= data
[0];
223 static void rx_data_tdls_setup_response_failure(struct wlantest
*wt
,
226 u8 dialog_token
, u16 status
)
228 struct wlantest_bss
*bss
;
229 struct wlantest_tdls
*tdls
;
230 struct wlantest_sta
*sta
;
232 if (status
== WLAN_STATUS_SUCCESS
) {
233 wpa_printf(MSG_INFO
, "TDLS: Invalid TDLS Setup Response from "
234 MACSTR
, MAC2STR(sta_addr
));
238 bss
= bss_find(wt
, bssid
);
241 sta
= sta_find(bss
, sta_addr
);
245 dl_list_for_each(tdls
, &bss
->tdls
, struct wlantest_tdls
, list
) {
246 if (tdls
->resp
== sta
) {
247 if (dialog_token
!= tdls
->dialog_token
) {
248 wpa_printf(MSG_DEBUG
, "TDLS: Dialog token "
249 "mismatch in TDLS Setup Response "
253 wpa_printf(MSG_DEBUG
, "TDLS: Found matching TDLS "
254 "setup session based on dialog token");
256 WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL
]++;
263 static void rx_data_tdls_setup_response(struct wlantest
*wt
, const u8
*bssid
,
264 const u8
*sta_addr
, const u8
*dst
,
266 const u8
*data
, size_t len
)
269 struct ieee802_11_elems elems
;
270 struct wlantest_tdls
*tdls
;
273 wpa_printf(MSG_INFO
, "Too short TDLS Setup Response " MACSTR
274 " -> " MACSTR
, MAC2STR(src
), MAC2STR(dst
));
277 status
= WPA_GET_LE16(data
);
278 wpa_printf(MSG_DEBUG
, "TDLS Setup Response " MACSTR
" -> "
279 MACSTR
" (status %d)",
280 MAC2STR(src
), MAC2STR(dst
), status
);
281 if (len
< 5 && status
== 0) {
282 wpa_printf(MSG_INFO
, "Too short TDLS Setup Response " MACSTR
283 " -> " MACSTR
, MAC2STR(src
), MAC2STR(dst
));
288 ieee802_11_parse_elems(data
+ 5, len
- 5, &elems
, 1) ==
289 ParseFailed
|| elems
.link_id
== NULL
) {
290 /* Need to match TDLS link based on Dialog Token */
291 rx_data_tdls_setup_response_failure(wt
, bssid
, sta_addr
,
295 wpa_printf(MSG_DEBUG
, "TDLS Link Identifier: BSSID " MACSTR
296 " initiator STA " MACSTR
" responder STA " MACSTR
,
297 MAC2STR(elems
.link_id
), MAC2STR(elems
.link_id
+ ETH_ALEN
),
298 MAC2STR(elems
.link_id
+ 2 * ETH_ALEN
));
300 tdls
= get_tdls(wt
, elems
.link_id
, 1, bssid
);
304 tdls
->counters
[WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL
]++;
306 tdls
->counters
[WLANTEST_TDLS_COUNTER_SETUP_RESP_OK
]++;
308 if (status
!= WLAN_STATUS_SUCCESS
)
311 if (tdls_derive_tpk(tdls
, bssid
, elems
.ftie
, elems
.ftie_len
) < 1)
313 if (tdls_verify_mic(tdls
, 2, &elems
) == 0) {
314 tdls
->dialog_token
= data
[2];
315 wpa_printf(MSG_DEBUG
, "TDLS: Dialog Token for the link: %u",
321 static void rx_data_tdls_setup_confirm_failure(struct wlantest
*wt
,
324 u8 dialog_token
, u16 status
)
326 struct wlantest_bss
*bss
;
327 struct wlantest_tdls
*tdls
;
328 struct wlantest_sta
*sta
;
330 if (status
== WLAN_STATUS_SUCCESS
) {
331 wpa_printf(MSG_INFO
, "TDLS: Invalid TDLS Setup Confirm from "
332 MACSTR
, MAC2STR(src
));
336 bss
= bss_find(wt
, bssid
);
339 sta
= sta_find(bss
, src
);
343 dl_list_for_each(tdls
, &bss
->tdls
, struct wlantest_tdls
, list
) {
344 if (tdls
->init
== sta
) {
345 if (dialog_token
!= tdls
->dialog_token
) {
346 wpa_printf(MSG_DEBUG
, "TDLS: Dialog token "
347 "mismatch in TDLS Setup Confirm "
351 wpa_printf(MSG_DEBUG
, "TDLS: Found matching TDLS "
352 "setup session based on dialog token");
354 WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL
]++;
361 static void rx_data_tdls_setup_confirm(struct wlantest
*wt
, const u8
*bssid
,
362 const u8
*sta_addr
, const u8
*dst
,
364 const u8
*data
, size_t len
)
367 struct ieee802_11_elems elems
;
368 struct wlantest_tdls
*tdls
;
369 u8 link_id
[3 * ETH_ALEN
];
372 wpa_printf(MSG_INFO
, "Too short TDLS Setup Confirm " MACSTR
373 " -> " MACSTR
, MAC2STR(src
), MAC2STR(dst
));
376 status
= WPA_GET_LE16(data
);
377 wpa_printf(MSG_DEBUG
, "TDLS Setup Confirm " MACSTR
" -> "
378 MACSTR
" (status %d)",
379 MAC2STR(src
), MAC2STR(dst
), status
);
381 if (ieee802_11_parse_elems(data
+ 3, len
- 3, &elems
, 1) ==
382 ParseFailed
|| elems
.link_id
== NULL
) {
383 /* Need to match TDLS link based on Dialog Token */
384 rx_data_tdls_setup_confirm_failure(wt
, bssid
, src
,
388 wpa_printf(MSG_DEBUG
, "TDLS Link Identifier: BSSID " MACSTR
389 " initiator STA " MACSTR
" responder STA " MACSTR
,
390 MAC2STR(elems
.link_id
), MAC2STR(elems
.link_id
+ ETH_ALEN
),
391 MAC2STR(elems
.link_id
+ 2 * ETH_ALEN
));
393 tdls
= get_tdls(wt
, elems
.link_id
, 1, bssid
);
397 tdls
->counters
[WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL
]++;
399 tdls
->counters
[WLANTEST_TDLS_COUNTER_SETUP_CONF_OK
]++;
401 if (status
!= WLAN_STATUS_SUCCESS
)
405 if (tdls_derive_tpk(tdls
, bssid
, elems
.ftie
, elems
.ftie_len
) < 1) {
406 if (elems
.ftie
== NULL
)
410 if (tdls_verify_mic(tdls
, 3, &elems
) == 0) {
411 tdls
->dialog_token
= data
[2];
412 wpa_printf(MSG_DEBUG
, "TDLS: Dialog Token for the link: %u",
418 * The TDLS link itself is bidirectional, but there is explicit
419 * initiator/responder roles. Remove the other direction of the link
420 * (if it exists) to make sure that the link counters are stored for
421 * the current TDLS entery.
423 os_memcpy(link_id
, elems
.link_id
, ETH_ALEN
);
424 os_memcpy(link_id
+ ETH_ALEN
, elems
.link_id
+ 2 * ETH_ALEN
, ETH_ALEN
);
425 os_memcpy(link_id
+ 2 * ETH_ALEN
, elems
.link_id
+ ETH_ALEN
, ETH_ALEN
);
426 tdls
= get_tdls(wt
, link_id
, 0, bssid
);
428 wpa_printf(MSG_DEBUG
, "TDLS: Remove reverse link entry");
434 static int tdls_verify_mic_teardown(struct wlantest_tdls
*tdls
, u8 trans_seq
,
435 const u8
*reason_code
,
436 struct ieee802_11_elems
*elems
)
442 const struct rsn_ftie
*rx_ftie
;
443 struct rsn_ftie
*tmp_ftie
;
445 if (elems
->link_id
== NULL
|| elems
->ftie
== NULL
)
448 len
= 2 + 18 + 2 + 1 + 1 + 2 + elems
->ftie_len
;
450 buf
= os_zalloc(len
);
455 /* 1) Link Identifier IE */
456 os_memcpy(pos
, elems
->link_id
- 2, 2 + 18);
459 os_memcpy(pos
, reason_code
, 2);
461 /* 3) Dialog token */
462 *pos
++ = tdls
->dialog_token
;
463 /* 4) Transaction Sequence number */
465 /* 5) FTIE, with the MIC field of the FTIE set to 0 */
466 os_memcpy(pos
, elems
->ftie
- 2, 2 + elems
->ftie_len
);
468 tmp_ftie
= (struct rsn_ftie
*) pos
;
469 os_memset(tmp_ftie
->mic
, 0, 16);
470 pos
+= elems
->ftie_len
;
472 wpa_hexdump(MSG_DEBUG
, "TDLS: Data for FTIE MIC", buf
, pos
- buf
);
473 wpa_hexdump_key(MSG_DEBUG
, "TDLS: KCK", tdls
->tpk
.kck
, 16);
474 ret
= omac1_aes_128(tdls
->tpk
.kck
, buf
, pos
- buf
, mic
);
478 wpa_hexdump(MSG_DEBUG
, "TDLS: FTIE MIC", mic
, 16);
479 rx_ftie
= (const struct rsn_ftie
*) elems
->ftie
;
481 if (os_memcmp(mic
, rx_ftie
->mic
, 16) == 0) {
482 wpa_printf(MSG_DEBUG
, "TDLS: Valid MIC");
485 wpa_printf(MSG_DEBUG
, "TDLS: Invalid MIC");
490 static void rx_data_tdls_teardown(struct wlantest
*wt
, const u8
*bssid
,
491 const u8
*sta_addr
, const u8
*dst
,
493 const u8
*data
, size_t len
)
496 struct ieee802_11_elems elems
;
497 struct wlantest_tdls
*tdls
;
501 reason
= WPA_GET_LE16(data
);
502 wpa_printf(MSG_DEBUG
, "TDLS Teardown " MACSTR
" -> "
503 MACSTR
" (reason %d)",
504 MAC2STR(src
), MAC2STR(dst
), reason
);
506 if (ieee802_11_parse_elems(data
+ 2, len
- 2, &elems
, 1) ==
507 ParseFailed
|| elems
.link_id
== NULL
)
509 wpa_printf(MSG_DEBUG
, "TDLS Link Identifier: BSSID " MACSTR
510 " initiator STA " MACSTR
" responder STA " MACSTR
,
511 MAC2STR(elems
.link_id
), MAC2STR(elems
.link_id
+ ETH_ALEN
),
512 MAC2STR(elems
.link_id
+ 2 * ETH_ALEN
));
514 tdls
= get_tdls(wt
, elems
.link_id
, 1, bssid
);
517 tdls
->counters
[WLANTEST_TDLS_COUNTER_TEARDOWN
]++;
518 tdls_verify_mic_teardown(tdls
, 4, data
, &elems
);
523 static void rx_data_tdls(struct wlantest
*wt
, const u8
*bssid
,
524 const u8
*sta_addr
, const u8
*dst
, const u8
*src
,
525 const u8
*data
, size_t len
)
527 /* data contains the payload of a TDLS Action frame */
528 if (len
< 2 || data
[0] != WLAN_ACTION_TDLS
) {
529 wpa_hexdump(MSG_DEBUG
, "Unrecognized encapsulated TDLS frame",
535 case WLAN_TDLS_SETUP_REQUEST
:
536 rx_data_tdls_setup_request(wt
, bssid
, sta_addr
, dst
, src
,
539 case WLAN_TDLS_SETUP_RESPONSE
:
540 rx_data_tdls_setup_response(wt
, bssid
, sta_addr
, dst
, src
,
543 case WLAN_TDLS_SETUP_CONFIRM
:
544 rx_data_tdls_setup_confirm(wt
, bssid
, sta_addr
, dst
, src
,
547 case WLAN_TDLS_TEARDOWN
:
548 rx_data_tdls_teardown(wt
, bssid
, sta_addr
, dst
, src
, data
+ 2,
551 case WLAN_TDLS_DISCOVERY_REQUEST
:
552 wpa_printf(MSG_DEBUG
, "TDLS Discovery Request " MACSTR
" -> "
553 MACSTR
, MAC2STR(src
), MAC2STR(dst
));
559 void rx_data_80211_encap(struct wlantest
*wt
, const u8
*bssid
,
560 const u8
*sta_addr
, const u8
*dst
, const u8
*src
,
561 const u8
*data
, size_t len
)
563 wpa_hexdump(MSG_EXCESSIVE
, "802.11 data encap frame", data
, len
);
567 rx_data_tdls(wt
, bssid
, sta_addr
, dst
, src
, data
+ 1, len
- 1);