0187c41d88
These were somewhat more hidden to avoid direct use, but there are now numerous places where these are needed and more justification to make the extern int declarations available from wpa_debug.h. In addition, this avoids some warnings from sparse. Signed-hostap: Jouni Malinen <j@w1.fi>
624 lines
16 KiB
C
624 lines
16 KiB
C
/*
|
|
* Received Data frame processing
|
|
* Copyright (c) 2010, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*/
|
|
|
|
#include "utils/includes.h"
|
|
#include <linux/if_ether.h>
|
|
|
|
#include "utils/common.h"
|
|
#include "common/defs.h"
|
|
#include "common/ieee802_11_defs.h"
|
|
#include "wlantest.h"
|
|
|
|
|
|
static const char * data_stype(u16 stype)
|
|
{
|
|
switch (stype) {
|
|
case WLAN_FC_STYPE_DATA:
|
|
return "DATA";
|
|
case WLAN_FC_STYPE_DATA_CFACK:
|
|
return "DATA-CFACK";
|
|
case WLAN_FC_STYPE_DATA_CFPOLL:
|
|
return "DATA-CFPOLL";
|
|
case WLAN_FC_STYPE_DATA_CFACKPOLL:
|
|
return "DATA-CFACKPOLL";
|
|
case WLAN_FC_STYPE_NULLFUNC:
|
|
return "NULLFUNC";
|
|
case WLAN_FC_STYPE_CFACK:
|
|
return "CFACK";
|
|
case WLAN_FC_STYPE_CFPOLL:
|
|
return "CFPOLL";
|
|
case WLAN_FC_STYPE_CFACKPOLL:
|
|
return "CFACKPOLL";
|
|
case WLAN_FC_STYPE_QOS_DATA:
|
|
return "QOSDATA";
|
|
case WLAN_FC_STYPE_QOS_DATA_CFACK:
|
|
return "QOSDATA-CFACK";
|
|
case WLAN_FC_STYPE_QOS_DATA_CFPOLL:
|
|
return "QOSDATA-CFPOLL";
|
|
case WLAN_FC_STYPE_QOS_DATA_CFACKPOLL:
|
|
return "QOSDATA-CFACKPOLL";
|
|
case WLAN_FC_STYPE_QOS_NULL:
|
|
return "QOS-NULL";
|
|
case WLAN_FC_STYPE_QOS_CFPOLL:
|
|
return "QOS-CFPOLL";
|
|
case WLAN_FC_STYPE_QOS_CFACKPOLL:
|
|
return "QOS-CFACKPOLL";
|
|
}
|
|
return "??";
|
|
}
|
|
|
|
|
|
static void rx_data_eth(struct wlantest *wt, const u8 *bssid,
|
|
const u8 *sta_addr, const u8 *dst, const u8 *src,
|
|
u16 ethertype, const u8 *data, size_t len, int prot,
|
|
const u8 *peer_addr)
|
|
{
|
|
switch (ethertype) {
|
|
case ETH_P_PAE:
|
|
rx_data_eapol(wt, dst, src, data, len, prot);
|
|
break;
|
|
case ETH_P_IP:
|
|
rx_data_ip(wt, bssid, sta_addr, dst, src, data, len,
|
|
peer_addr);
|
|
break;
|
|
case 0x890d:
|
|
rx_data_80211_encap(wt, bssid, sta_addr, dst, src, data, len);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void rx_data_process(struct wlantest *wt, const u8 *bssid,
|
|
const u8 *sta_addr,
|
|
const u8 *dst, const u8 *src,
|
|
const u8 *data, size_t len, int prot,
|
|
const u8 *peer_addr)
|
|
{
|
|
if (len == 0)
|
|
return;
|
|
|
|
if (len >= 8 && os_memcmp(data, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) {
|
|
rx_data_eth(wt, bssid, sta_addr, dst, src,
|
|
WPA_GET_BE16(data + 6), data + 8, len - 8, prot,
|
|
peer_addr);
|
|
return;
|
|
}
|
|
|
|
wpa_hexdump(MSG_DEBUG, "Unrecognized LLC", data, len > 8 ? 8 : len);
|
|
}
|
|
|
|
|
|
static u8 * try_all_ptk(struct wlantest *wt, int pairwise_cipher,
|
|
const struct ieee80211_hdr *hdr,
|
|
const u8 *data, size_t data_len, size_t *decrypted_len)
|
|
{
|
|
struct wlantest_ptk *ptk;
|
|
u8 *decrypted;
|
|
int prev_level = wpa_debug_level;
|
|
|
|
wpa_debug_level = MSG_WARNING;
|
|
dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) {
|
|
decrypted = NULL;
|
|
if ((pairwise_cipher == WPA_CIPHER_CCMP ||
|
|
pairwise_cipher == 0) && ptk->ptk_len == 48) {
|
|
decrypted = ccmp_decrypt(ptk->ptk.tk1, hdr, data,
|
|
data_len, decrypted_len);
|
|
}
|
|
if ((pairwise_cipher == WPA_CIPHER_TKIP ||
|
|
pairwise_cipher == 0) && ptk->ptk_len == 64) {
|
|
decrypted = tkip_decrypt(ptk->ptk.tk1, hdr, data,
|
|
data_len, decrypted_len);
|
|
}
|
|
if (decrypted) {
|
|
wpa_debug_level = prev_level;
|
|
add_note(wt, MSG_DEBUG, "Found PTK match from list of all known PTKs");
|
|
return decrypted;
|
|
}
|
|
}
|
|
wpa_debug_level = prev_level;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void rx_data_bss_prot_group(struct wlantest *wt,
|
|
const struct ieee80211_hdr *hdr,
|
|
const u8 *qos, const u8 *dst, const u8 *src,
|
|
const u8 *data, size_t len)
|
|
{
|
|
struct wlantest_bss *bss;
|
|
int keyid;
|
|
u8 *decrypted;
|
|
size_t dlen;
|
|
u8 pn[6];
|
|
|
|
bss = bss_get(wt, hdr->addr2);
|
|
if (bss == NULL)
|
|
return;
|
|
if (len < 4) {
|
|
add_note(wt, MSG_INFO, "Too short group addressed data frame");
|
|
return;
|
|
}
|
|
|
|
if (bss->group_cipher & (WPA_CIPHER_TKIP | WPA_CIPHER_CCMP) &&
|
|
!(data[3] & 0x20)) {
|
|
add_note(wt, MSG_INFO, "Expected TKIP/CCMP frame from "
|
|
MACSTR " did not have ExtIV bit set to 1",
|
|
MAC2STR(bss->bssid));
|
|
return;
|
|
}
|
|
|
|
if (bss->group_cipher == WPA_CIPHER_TKIP) {
|
|
if (data[3] & 0x1f) {
|
|
add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
|
|
" used non-zero reserved bit",
|
|
MAC2STR(bss->bssid));
|
|
}
|
|
if (data[1] != ((data[0] | 0x20) & 0x7f)) {
|
|
add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
|
|
" used incorrect WEPSeed[1] (was 0x%x, "
|
|
"expected 0x%x)",
|
|
MAC2STR(bss->bssid), data[1],
|
|
(data[0] | 0x20) & 0x7f);
|
|
}
|
|
} else if (bss->group_cipher == WPA_CIPHER_CCMP) {
|
|
if (data[2] != 0 || (data[3] & 0x1f) != 0) {
|
|
add_note(wt, MSG_INFO, "CCMP frame from " MACSTR
|
|
" used non-zero reserved bit",
|
|
MAC2STR(bss->bssid));
|
|
}
|
|
}
|
|
|
|
keyid = data[3] >> 6;
|
|
if (bss->gtk_len[keyid] == 0 && bss->group_cipher != WPA_CIPHER_WEP40)
|
|
{
|
|
add_note(wt, MSG_MSGDUMP, "No GTK known to decrypt the frame "
|
|
"(A2=" MACSTR " KeyID=%d)",
|
|
MAC2STR(hdr->addr2), keyid);
|
|
return;
|
|
}
|
|
|
|
if (bss->group_cipher == WPA_CIPHER_TKIP)
|
|
tkip_get_pn(pn, data);
|
|
else if (bss->group_cipher == WPA_CIPHER_WEP40)
|
|
goto skip_replay_det;
|
|
else
|
|
ccmp_get_pn(pn, data);
|
|
if (os_memcmp(pn, bss->rsc[keyid], 6) <= 0) {
|
|
u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
|
|
add_note(wt, MSG_INFO, "CCMP/TKIP replay detected: A1=" MACSTR
|
|
" A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
|
|
MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
|
|
MAC2STR(hdr->addr3),
|
|
WLAN_GET_SEQ_SEQ(seq_ctrl),
|
|
WLAN_GET_SEQ_FRAG(seq_ctrl));
|
|
wpa_hexdump(MSG_INFO, "RX PN", pn, 6);
|
|
wpa_hexdump(MSG_INFO, "RSC", bss->rsc[keyid], 6);
|
|
}
|
|
|
|
skip_replay_det:
|
|
if (bss->group_cipher == WPA_CIPHER_TKIP)
|
|
decrypted = tkip_decrypt(bss->gtk[keyid], hdr, data, len,
|
|
&dlen);
|
|
else if (bss->group_cipher == WPA_CIPHER_WEP40)
|
|
decrypted = wep_decrypt(wt, hdr, data, len, &dlen);
|
|
else
|
|
decrypted = ccmp_decrypt(bss->gtk[keyid], hdr, data, len,
|
|
&dlen);
|
|
if (decrypted) {
|
|
rx_data_process(wt, bss->bssid, NULL, dst, src, decrypted,
|
|
dlen, 1, NULL);
|
|
os_memcpy(bss->rsc[keyid], pn, 6);
|
|
write_pcap_decrypted(wt, (const u8 *) hdr, 24 + (qos ? 2 : 0),
|
|
decrypted, dlen);
|
|
} else
|
|
add_note(wt, MSG_DEBUG, "Failed to decrypt frame");
|
|
os_free(decrypted);
|
|
}
|
|
|
|
|
|
static void rx_data_bss_prot(struct wlantest *wt,
|
|
const struct ieee80211_hdr *hdr, const u8 *qos,
|
|
const u8 *dst, const u8 *src, const u8 *data,
|
|
size_t len)
|
|
{
|
|
struct wlantest_bss *bss;
|
|
struct wlantest_sta *sta, *sta2;
|
|
int keyid;
|
|
u16 fc = le_to_host16(hdr->frame_control);
|
|
u8 *decrypted;
|
|
size_t dlen;
|
|
int tid;
|
|
u8 pn[6], *rsc;
|
|
struct wlantest_tdls *tdls = NULL, *found;
|
|
const u8 *tk = NULL;
|
|
int ptk_iter_done = 0;
|
|
int try_ptk_iter = 0;
|
|
|
|
if (hdr->addr1[0] & 0x01) {
|
|
rx_data_bss_prot_group(wt, hdr, qos, dst, src, data, len);
|
|
return;
|
|
}
|
|
|
|
if (fc & WLAN_FC_TODS) {
|
|
bss = bss_get(wt, hdr->addr1);
|
|
if (bss == NULL)
|
|
return;
|
|
sta = sta_get(bss, hdr->addr2);
|
|
if (sta)
|
|
sta->counters[WLANTEST_STA_COUNTER_PROT_DATA_TX]++;
|
|
} else if (fc & WLAN_FC_FROMDS) {
|
|
bss = bss_get(wt, hdr->addr2);
|
|
if (bss == NULL)
|
|
return;
|
|
sta = sta_get(bss, hdr->addr1);
|
|
} else {
|
|
bss = bss_get(wt, hdr->addr3);
|
|
if (bss == NULL)
|
|
return;
|
|
sta = sta_find(bss, hdr->addr2);
|
|
sta2 = sta_find(bss, hdr->addr1);
|
|
if (sta == NULL || sta2 == NULL)
|
|
return;
|
|
found = NULL;
|
|
dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list)
|
|
{
|
|
if ((tdls->init == sta && tdls->resp == sta2) ||
|
|
(tdls->init == sta2 && tdls->resp == sta)) {
|
|
found = tdls;
|
|
if (tdls->link_up)
|
|
break;
|
|
}
|
|
}
|
|
if (found) {
|
|
if (!found->link_up)
|
|
add_note(wt, MSG_DEBUG,
|
|
"TDLS: Link not up, but Data "
|
|
"frame seen");
|
|
tk = found->tpk.tk;
|
|
tdls = found;
|
|
}
|
|
}
|
|
if ((sta == NULL ||
|
|
(!sta->ptk_set && sta->pairwise_cipher != WPA_CIPHER_WEP40)) &&
|
|
tk == NULL) {
|
|
add_note(wt, MSG_MSGDUMP, "No PTK known to decrypt the frame");
|
|
if (dl_list_empty(&wt->ptk))
|
|
return;
|
|
try_ptk_iter = 1;
|
|
}
|
|
|
|
if (len < 4) {
|
|
add_note(wt, MSG_INFO, "Too short encrypted data frame");
|
|
return;
|
|
}
|
|
|
|
if (sta == NULL)
|
|
return;
|
|
if (sta->pairwise_cipher & (WPA_CIPHER_TKIP | WPA_CIPHER_CCMP) &&
|
|
!(data[3] & 0x20)) {
|
|
add_note(wt, MSG_INFO, "Expected TKIP/CCMP frame from "
|
|
MACSTR " did not have ExtIV bit set to 1",
|
|
MAC2STR(src));
|
|
return;
|
|
}
|
|
|
|
if (tk == NULL && sta->pairwise_cipher == WPA_CIPHER_TKIP) {
|
|
if (data[3] & 0x1f) {
|
|
add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
|
|
" used non-zero reserved bit",
|
|
MAC2STR(hdr->addr2));
|
|
}
|
|
if (data[1] != ((data[0] | 0x20) & 0x7f)) {
|
|
add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
|
|
" used incorrect WEPSeed[1] (was 0x%x, "
|
|
"expected 0x%x)",
|
|
MAC2STR(hdr->addr2), data[1],
|
|
(data[0] | 0x20) & 0x7f);
|
|
}
|
|
} else if (tk || sta->pairwise_cipher == WPA_CIPHER_CCMP) {
|
|
if (data[2] != 0 || (data[3] & 0x1f) != 0) {
|
|
add_note(wt, MSG_INFO, "CCMP frame from " MACSTR
|
|
" used non-zero reserved bit",
|
|
MAC2STR(hdr->addr2));
|
|
}
|
|
}
|
|
|
|
keyid = data[3] >> 6;
|
|
if (keyid != 0) {
|
|
add_note(wt, MSG_INFO, "Unexpected non-zero KeyID %d in "
|
|
"individually addressed Data frame from " MACSTR,
|
|
keyid, MAC2STR(hdr->addr2));
|
|
}
|
|
|
|
if (qos) {
|
|
tid = qos[0] & 0x0f;
|
|
if (fc & WLAN_FC_TODS)
|
|
sta->tx_tid[tid]++;
|
|
else
|
|
sta->rx_tid[tid]++;
|
|
} else {
|
|
tid = 0;
|
|
if (fc & WLAN_FC_TODS)
|
|
sta->tx_tid[16]++;
|
|
else
|
|
sta->rx_tid[16]++;
|
|
}
|
|
if (tk) {
|
|
if (os_memcmp(hdr->addr2, tdls->init->addr, ETH_ALEN) == 0)
|
|
rsc = tdls->rsc_init[tid];
|
|
else
|
|
rsc = tdls->rsc_resp[tid];
|
|
} else if (fc & WLAN_FC_TODS)
|
|
rsc = sta->rsc_tods[tid];
|
|
else
|
|
rsc = sta->rsc_fromds[tid];
|
|
|
|
|
|
if (tk == NULL && sta->pairwise_cipher == WPA_CIPHER_TKIP)
|
|
tkip_get_pn(pn, data);
|
|
else if (sta->pairwise_cipher == WPA_CIPHER_WEP40)
|
|
goto skip_replay_det;
|
|
else
|
|
ccmp_get_pn(pn, data);
|
|
if (os_memcmp(pn, rsc, 6) <= 0) {
|
|
u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
|
|
add_note(wt, MSG_INFO, "CCMP/TKIP replay detected: A1=" MACSTR
|
|
" A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
|
|
MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
|
|
MAC2STR(hdr->addr3),
|
|
WLAN_GET_SEQ_SEQ(seq_ctrl),
|
|
WLAN_GET_SEQ_FRAG(seq_ctrl));
|
|
wpa_hexdump(MSG_INFO, "RX PN", pn, 6);
|
|
wpa_hexdump(MSG_INFO, "RSC", rsc, 6);
|
|
}
|
|
|
|
skip_replay_det:
|
|
if (tk)
|
|
decrypted = ccmp_decrypt(tk, hdr, data, len, &dlen);
|
|
else if (sta->pairwise_cipher == WPA_CIPHER_TKIP)
|
|
decrypted = tkip_decrypt(sta->ptk.tk1, hdr, data, len, &dlen);
|
|
else if (sta->pairwise_cipher == WPA_CIPHER_WEP40)
|
|
decrypted = wep_decrypt(wt, hdr, data, len, &dlen);
|
|
else if (sta->ptk_set)
|
|
decrypted = ccmp_decrypt(sta->ptk.tk1, hdr, data, len, &dlen);
|
|
else {
|
|
decrypted = try_all_ptk(wt, sta->pairwise_cipher, hdr, data,
|
|
len, &dlen);
|
|
ptk_iter_done = 1;
|
|
}
|
|
if (!decrypted && !ptk_iter_done) {
|
|
decrypted = try_all_ptk(wt, sta->pairwise_cipher, hdr, data,
|
|
len, &dlen);
|
|
if (decrypted) {
|
|
add_note(wt, MSG_DEBUG, "Current PTK did not work, but found a match from all known PTKs");
|
|
}
|
|
}
|
|
if (decrypted) {
|
|
u16 fc = le_to_host16(hdr->frame_control);
|
|
const u8 *peer_addr = NULL;
|
|
if (!(fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)))
|
|
peer_addr = hdr->addr1;
|
|
os_memcpy(rsc, pn, 6);
|
|
rx_data_process(wt, bss->bssid, sta->addr, dst, src, decrypted,
|
|
dlen, 1, peer_addr);
|
|
write_pcap_decrypted(wt, (const u8 *) hdr, 24 + (qos ? 2 : 0),
|
|
decrypted, dlen);
|
|
} else if (!try_ptk_iter)
|
|
add_note(wt, MSG_DEBUG, "Failed to decrypt frame");
|
|
os_free(decrypted);
|
|
}
|
|
|
|
|
|
static void rx_data_bss(struct wlantest *wt, const struct ieee80211_hdr *hdr,
|
|
const u8 *qos, const u8 *dst, const u8 *src,
|
|
const u8 *data, size_t len)
|
|
{
|
|
u16 fc = le_to_host16(hdr->frame_control);
|
|
int prot = !!(fc & WLAN_FC_ISWEP);
|
|
|
|
if (qos) {
|
|
u8 ack = (qos[0] & 0x60) >> 5;
|
|
wpa_printf(MSG_MSGDUMP, "BSS DATA: " MACSTR " -> " MACSTR
|
|
" len=%u%s tid=%u%s%s",
|
|
MAC2STR(src), MAC2STR(dst), (unsigned int) len,
|
|
prot ? " Prot" : "", qos[0] & 0x0f,
|
|
(qos[0] & 0x10) ? " EOSP" : "",
|
|
ack == 0 ? "" :
|
|
(ack == 1 ? " NoAck" :
|
|
(ack == 2 ? " NoExpAck" : " BA")));
|
|
} else {
|
|
wpa_printf(MSG_MSGDUMP, "BSS DATA: " MACSTR " -> " MACSTR
|
|
" len=%u%s",
|
|
MAC2STR(src), MAC2STR(dst), (unsigned int) len,
|
|
prot ? " Prot" : "");
|
|
}
|
|
|
|
if (prot)
|
|
rx_data_bss_prot(wt, hdr, qos, dst, src, data, len);
|
|
else {
|
|
const u8 *bssid, *sta_addr, *peer_addr;
|
|
struct wlantest_bss *bss;
|
|
|
|
if (fc & WLAN_FC_TODS) {
|
|
bssid = hdr->addr1;
|
|
sta_addr = hdr->addr2;
|
|
peer_addr = NULL;
|
|
} else if (fc & WLAN_FC_FROMDS) {
|
|
bssid = hdr->addr2;
|
|
sta_addr = hdr->addr1;
|
|
peer_addr = NULL;
|
|
} else {
|
|
bssid = hdr->addr3;
|
|
sta_addr = hdr->addr2;
|
|
peer_addr = hdr->addr1;
|
|
}
|
|
|
|
bss = bss_get(wt, bssid);
|
|
if (bss) {
|
|
struct wlantest_sta *sta = sta_get(bss, sta_addr);
|
|
|
|
if (sta) {
|
|
if (qos) {
|
|
int tid = qos[0] & 0x0f;
|
|
if (fc & WLAN_FC_TODS)
|
|
sta->tx_tid[tid]++;
|
|
else
|
|
sta->rx_tid[tid]++;
|
|
} else {
|
|
if (fc & WLAN_FC_TODS)
|
|
sta->tx_tid[16]++;
|
|
else
|
|
sta->rx_tid[16]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
rx_data_process(wt, bssid, sta_addr, dst, src, data, len, 0,
|
|
peer_addr);
|
|
}
|
|
}
|
|
|
|
|
|
static struct wlantest_tdls * get_tdls(struct wlantest *wt, const u8 *bssid,
|
|
const u8 *sta1_addr,
|
|
const u8 *sta2_addr)
|
|
{
|
|
struct wlantest_bss *bss;
|
|
struct wlantest_sta *sta1, *sta2;
|
|
struct wlantest_tdls *tdls, *found = NULL;
|
|
|
|
bss = bss_find(wt, bssid);
|
|
if (bss == NULL)
|
|
return NULL;
|
|
sta1 = sta_find(bss, sta1_addr);
|
|
if (sta1 == NULL)
|
|
return NULL;
|
|
sta2 = sta_find(bss, sta2_addr);
|
|
if (sta2 == NULL)
|
|
return NULL;
|
|
|
|
dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
|
|
if ((tdls->init == sta1 && tdls->resp == sta2) ||
|
|
(tdls->init == sta2 && tdls->resp == sta1)) {
|
|
found = tdls;
|
|
if (tdls->link_up)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
static void add_direct_link(struct wlantest *wt, const u8 *bssid,
|
|
const u8 *sta1_addr, const u8 *sta2_addr)
|
|
{
|
|
struct wlantest_tdls *tdls;
|
|
|
|
tdls = get_tdls(wt, bssid, sta1_addr, sta2_addr);
|
|
if (tdls == NULL)
|
|
return;
|
|
|
|
if (tdls->link_up)
|
|
tdls->counters[WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK]++;
|
|
else
|
|
tdls->counters[WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK]++;
|
|
}
|
|
|
|
|
|
static void add_ap_path(struct wlantest *wt, const u8 *bssid,
|
|
const u8 *sta1_addr, const u8 *sta2_addr)
|
|
{
|
|
struct wlantest_tdls *tdls;
|
|
|
|
tdls = get_tdls(wt, bssid, sta1_addr, sta2_addr);
|
|
if (tdls == NULL)
|
|
return;
|
|
|
|
if (tdls->link_up)
|
|
tdls->counters[WLANTEST_TDLS_COUNTER_INVALID_AP_PATH]++;
|
|
else
|
|
tdls->counters[WLANTEST_TDLS_COUNTER_VALID_AP_PATH]++;
|
|
}
|
|
|
|
|
|
void rx_data(struct wlantest *wt, const u8 *data, size_t len)
|
|
{
|
|
const struct ieee80211_hdr *hdr;
|
|
u16 fc, stype;
|
|
size_t hdrlen;
|
|
const u8 *qos = NULL;
|
|
|
|
if (len < 24)
|
|
return;
|
|
|
|
hdr = (const struct ieee80211_hdr *) data;
|
|
fc = le_to_host16(hdr->frame_control);
|
|
stype = WLAN_FC_GET_STYPE(fc);
|
|
hdrlen = 24;
|
|
if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
|
|
(WLAN_FC_TODS | WLAN_FC_FROMDS))
|
|
hdrlen += ETH_ALEN;
|
|
if (stype & 0x08) {
|
|
qos = data + hdrlen;
|
|
hdrlen += 2;
|
|
}
|
|
if (len < hdrlen)
|
|
return;
|
|
wt->rx_data++;
|
|
|
|
switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
|
|
case 0:
|
|
wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s IBSS DA=" MACSTR " SA="
|
|
MACSTR " BSSID=" MACSTR,
|
|
data_stype(WLAN_FC_GET_STYPE(fc)),
|
|
fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
|
|
fc & WLAN_FC_ISWEP ? " Prot" : "",
|
|
MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
|
|
MAC2STR(hdr->addr3));
|
|
add_direct_link(wt, hdr->addr3, hdr->addr1, hdr->addr2);
|
|
rx_data_bss(wt, hdr, qos, hdr->addr1, hdr->addr2,
|
|
data + hdrlen, len - hdrlen);
|
|
break;
|
|
case WLAN_FC_FROMDS:
|
|
wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s FromDS DA=" MACSTR
|
|
" BSSID=" MACSTR " SA=" MACSTR,
|
|
data_stype(WLAN_FC_GET_STYPE(fc)),
|
|
fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
|
|
fc & WLAN_FC_ISWEP ? " Prot" : "",
|
|
MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
|
|
MAC2STR(hdr->addr3));
|
|
add_ap_path(wt, hdr->addr2, hdr->addr1, hdr->addr3);
|
|
rx_data_bss(wt, hdr, qos, hdr->addr1, hdr->addr3,
|
|
data + hdrlen, len - hdrlen);
|
|
break;
|
|
case WLAN_FC_TODS:
|
|
wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s ToDS BSSID=" MACSTR
|
|
" SA=" MACSTR " DA=" MACSTR,
|
|
data_stype(WLAN_FC_GET_STYPE(fc)),
|
|
fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
|
|
fc & WLAN_FC_ISWEP ? " Prot" : "",
|
|
MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
|
|
MAC2STR(hdr->addr3));
|
|
add_ap_path(wt, hdr->addr1, hdr->addr3, hdr->addr2);
|
|
rx_data_bss(wt, hdr, qos, hdr->addr3, hdr->addr2,
|
|
data + hdrlen, len - hdrlen);
|
|
break;
|
|
case WLAN_FC_TODS | WLAN_FC_FROMDS:
|
|
wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s WDS RA=" MACSTR " TA="
|
|
MACSTR " DA=" MACSTR " SA=" MACSTR,
|
|
data_stype(WLAN_FC_GET_STYPE(fc)),
|
|
fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
|
|
fc & WLAN_FC_ISWEP ? " Prot" : "",
|
|
MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
|
|
MAC2STR(hdr->addr3),
|
|
MAC2STR((const u8 *) (hdr + 1)));
|
|
break;
|
|
}
|
|
}
|