mirror of
https://github.com/vanhoefm/fragattacks.git
synced 2024-11-28 18:28:23 -05:00
ace4e460e5
Some corner cases may result in both directions of TDLS tracking context existing. If that is the case, the incorrect one may end up getting picked when figuring out which TK to use for decryption or fix statistics counter to increment. Fix this by preferring the context that has TDLS link up. Signed-hostap: Jouni Malinen <j@w1.fi>
542 lines
14 KiB
C
542 lines
14 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 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;
|
|
|
|
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");
|
|
return;
|
|
}
|
|
|
|
if (len < 4) {
|
|
add_note(wt, MSG_INFO, "Too short encrypted data frame");
|
|
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;
|
|
else
|
|
tid = 0;
|
|
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
|
|
decrypted = ccmp_decrypt(sta->ptk.tk1, hdr, data, len, &dlen);
|
|
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
|
|
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;
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
}
|