#include <sys/time.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_arp.h>
#include <net/if_llc.h>
#include <net/bpf.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_radiotap.h>
#include <pcap.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include "hostapd.h"
const char *ieee80211_mgt_subtype_name[] = {
"association request",
"association response",
"reassociation request",
"reassociation response",
"probe request",
"probe response",
"reserved#6",
"reserved#7",
"beacon",
"atim",
"disassociation",
"authentication",
"deauthentication",
"reserved#13",
"reserved#14",
"reserved#15"
};
const u_int8_t *snapend;
int vflag = 1, eflag = 1;
int ieee80211_hdr(struct ieee80211_frame *);
void ieee80211_print_element(u_int8_t *, u_int);
void ieee80211_print_essid(u_int8_t *, u_int);
int ieee80211_elements(struct ieee80211_frame *);
int ieee80211_frame(struct ieee80211_frame *);
int ieee80211_print(struct ieee80211_frame *);
u_int ieee80211_any2ieee(u_int, u_int);
void ieee802_11_if_print(u_int8_t *, u_int);
void ieee802_11_radio_if_print(u_int8_t *, u_int);
#define TCARR(a) TCHECK2(*a, sizeof(a))
int
ieee80211_hdr(struct ieee80211_frame *wh)
{
struct ieee80211_frame_addr4 *w4;
switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
case IEEE80211_FC1_DIR_NODS:
TCARR(wh->i_addr2);
PRINTF("%s", etheraddr_string(wh->i_addr2));
TCARR(wh->i_addr1);
PRINTF(" > %s", etheraddr_string(wh->i_addr1));
TCARR(wh->i_addr3);
PRINTF(", bssid %s", etheraddr_string(wh->i_addr3));
break;
case IEEE80211_FC1_DIR_TODS:
TCARR(wh->i_addr2);
PRINTF("%s", etheraddr_string(wh->i_addr2));
TCARR(wh->i_addr3);
PRINTF(" > %s", etheraddr_string(wh->i_addr3));
TCARR(wh->i_addr1);
PRINTF(", bssid %s, > DS", etheraddr_string(wh->i_addr1));
break;
case IEEE80211_FC1_DIR_FROMDS:
TCARR(wh->i_addr3);
PRINTF("%s", etheraddr_string(wh->i_addr3));
TCARR(wh->i_addr1);
PRINTF(" > %s", etheraddr_string(wh->i_addr1));
TCARR(wh->i_addr2);
PRINTF(", bssid %s, DS >", etheraddr_string(wh->i_addr2));
break;
case IEEE80211_FC1_DIR_DSTODS:
w4 = (struct ieee80211_frame_addr4 *) wh;
TCARR(w4->i_addr4);
PRINTF("%s", etheraddr_string(w4->i_addr4));
TCARR(w4->i_addr3);
PRINTF(" > %s", etheraddr_string(w4->i_addr3));
TCARR(w4->i_addr2);
PRINTF(", bssid %s", etheraddr_string(w4->i_addr2));
TCARR(w4->i_addr1);
PRINTF(" > %s, DS > DS", etheraddr_string(w4->i_addr1));
break;
}
if (vflag) {
TCARR(wh->i_seq);
PRINTF(" (seq %u)", letoh16(*(u_int16_t *)&wh->i_seq[0]));
}
return (0);
trunc:
return (1);
}
void
ieee80211_print_element(u_int8_t *data, u_int len)
{
u_int8_t *p;
u_int i;
PRINTF(" 0x");
for (i = 0, p = data; i < len; i++, p++)
PRINTF("%02x", *p);
}
void
ieee80211_print_essid(u_int8_t *essid, u_int len)
{
u_int8_t *p;
u_int i;
if (len > IEEE80211_NWID_LEN)
len = IEEE80211_NWID_LEN;
for (i = 0, p = essid; i < len; i++, p++) {
if (*p < ' ' || *p > 0x7e)
break;
}
if (i == len) {
PRINTF(" (");
for (i = 0, p = essid; i < len; i++, p++)
PRINTF("%c", *p);
PRINTF(")");
} else
ieee80211_print_element(essid, len);
}
int
ieee80211_elements(struct ieee80211_frame *wh)
{
u_int8_t *frm;
u_int8_t *tstamp, *bintval, *capinfo;
int i;
frm = (u_int8_t *)&wh[1];
tstamp = frm;
TCHECK2(*tstamp, 8);
frm += 8;
bintval = frm;
TCHECK2(*bintval, 2);
frm += 2;
if (vflag)
PRINTF(", interval %u", letoh16(*(u_int16_t *)bintval));
capinfo = frm;
TCHECK2(*capinfo, 2);
frm += 2;
#if 0
if (vflag)
printb(", caps", letoh16(*(u_int16_t *)capinfo),
IEEE80211_CAPINFO_BITS);
#endif
while (TTEST2(*frm, 2)) {
u_int len = frm[1];
u_int8_t *data = frm + 2;
if (!TTEST2(*data, len))
break;
#define ELEM_CHECK(l) if (len != l) break
switch (*frm) {
case IEEE80211_ELEMID_SSID:
PRINTF(", ssid");
ieee80211_print_essid(data, len);
break;
case IEEE80211_ELEMID_RATES:
if (!vflag)
break;
PRINTF(", rates");
for (i = len; i > 0; i--, data++)
PRINTF(" %uM",
(data[0] & IEEE80211_RATE_VAL) / 2);
break;
case IEEE80211_ELEMID_FHPARMS:
ELEM_CHECK(5);
PRINTF(", fh (dwell %u, chan %u, index %u)",
(data[1] << 8) | data[0],
(data[2] - 1) * 80 + data[3],
data[4]);
break;
case IEEE80211_ELEMID_DSPARMS:
ELEM_CHECK(1);
if (!vflag)
break;
PRINTF(", ds");
PRINTF(" (chan %u)", data[0]);
break;
case IEEE80211_ELEMID_CFPARMS:
if (!vflag)
break;
PRINTF(", cf");
ieee80211_print_element(data, len);
break;
case IEEE80211_ELEMID_TIM:
if (!vflag)
break;
PRINTF(", tim");
ieee80211_print_element(data, len);
break;
case IEEE80211_ELEMID_IBSSPARMS:
if (!vflag)
break;
PRINTF(", ibss");
ieee80211_print_element(data, len);
break;
case IEEE80211_ELEMID_COUNTRY:
if (!vflag)
break;
PRINTF(", country");
for (i = len; i > 0; i--, data++)
PRINTF(" %u", data[0]);
break;
case IEEE80211_ELEMID_CHALLENGE:
if (!vflag)
break;
PRINTF(", challenge");
ieee80211_print_element(data, len);
break;
case IEEE80211_ELEMID_ERP:
if (!vflag)
break;
PRINTF(", erp");
ieee80211_print_element(data, len);
break;
case IEEE80211_ELEMID_RSN:
if (!vflag)
break;
PRINTF(", rsn");
ieee80211_print_element(data, len);
break;
case IEEE80211_ELEMID_XRATES:
if (!vflag)
break;
PRINTF(", xrates");
for (i = len; i > 0; i--, data++)
PRINTF(" %uM",
(data[0] & IEEE80211_RATE_VAL) / 2);
break;
case IEEE80211_ELEMID_TPC_REQUEST:
if (!vflag)
break;
PRINTF(", tpcrequest");
ieee80211_print_element(data, len);
break;
case IEEE80211_ELEMID_TPC_REPORT:
if (!vflag)
break;
PRINTF(", tpcreport");
ieee80211_print_element(data, len);
break;
case IEEE80211_ELEMID_VENDOR:
if (!vflag)
break;
PRINTF(", vendor");
ieee80211_print_element(data, len);
break;
default:
if (!vflag)
break;
PRINTF(", %u:%u", (u_int) *frm, len);
ieee80211_print_element(data, len);
break;
}
frm += len + 2;
if (frm >= snapend)
break;
}
#undef ELEM_CHECK
return (0);
trunc:
return (1);
}
int
ieee80211_frame(struct ieee80211_frame *wh)
{
u_int8_t subtype, type, *frm;
TCARR(wh->i_fc);
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
frm = (u_int8_t *)&wh[1];
switch (type) {
case IEEE80211_FC0_TYPE_DATA:
PRINTF(": data");
break;
case IEEE80211_FC0_TYPE_MGT:
PRINTF(": %s", ieee80211_mgt_subtype_name[
subtype >> IEEE80211_FC0_SUBTYPE_SHIFT]);
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_BEACON:
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
if (ieee80211_elements(wh) != 0)
goto trunc;
break;
case IEEE80211_FC0_SUBTYPE_AUTH:
TCHECK2(*frm, 2);
switch (IEEE80211_AUTH_ALGORITHM(frm)) {
case IEEE80211_AUTH_ALG_OPEN:
TCHECK2(*frm, 4);
switch (IEEE80211_AUTH_TRANSACTION(frm)) {
case IEEE80211_AUTH_OPEN_REQUEST:
PRINTF(" request");
break;
case IEEE80211_AUTH_OPEN_RESPONSE:
PRINTF(" response");
break;
}
break;
case IEEE80211_AUTH_ALG_SHARED:
TCHECK2(*frm, 4);
switch (IEEE80211_AUTH_TRANSACTION(frm)) {
case IEEE80211_AUTH_SHARED_REQUEST:
PRINTF(" request");
break;
case IEEE80211_AUTH_SHARED_CHALLENGE:
PRINTF(" challenge");
break;
case IEEE80211_AUTH_SHARED_RESPONSE:
PRINTF(" response");
break;
case IEEE80211_AUTH_SHARED_PASS:
PRINTF(" pass");
break;
}
break;
case IEEE80211_AUTH_ALG_LEAP:
PRINTF(" (leap)");
break;
}
break;
}
break;
default:
PRINTF(": type#%d", type);
break;
}
if (wh->i_fc[1] & IEEE80211_FC1_WEP)
PRINTF(", WEP");
return (0);
trunc:
return (1);
}
u_int
ieee80211_any2ieee(u_int freq, u_int flags)
{
if (flags & IEEE80211_CHAN_2GHZ) {
if (freq == 2484)
return 14;
if (freq < 2484)
return (freq - 2407) / 5;
else
return 15 + ((freq - 2512) / 20);
} else if (flags & IEEE80211_CHAN_5GHZ) {
return (freq - 5000) / 5;
} else {
return (freq);
}
}
int
ieee80211_print(struct ieee80211_frame *wh)
{
if (eflag)
if (ieee80211_hdr(wh))
return (1);
return (ieee80211_frame(wh));
}
void
ieee802_11_if_print(u_int8_t *buf, u_int len)
{
struct ieee80211_frame *wh = (struct ieee80211_frame*)buf;
snapend = buf + len;
if (ieee80211_print(wh) != 0)
PRINTF("[|802.11]");
PRINTF(NULL);
}
void
ieee802_11_radio_if_print(u_int8_t *buf, u_int len)
{
struct ieee80211_radiotap_header *rh =
(struct ieee80211_radiotap_header*)buf;
struct ieee80211_frame *wh;
u_int8_t *t;
u_int32_t present;
u_int rh_len;
snapend = buf + len;
TCHECK(*rh);
rh_len = letoh16(rh->it_len);
if (rh->it_version != 0) {
PRINTF("[?radiotap + 802.11 v:%u]", rh->it_version);
goto out;
}
wh = (struct ieee80211_frame *)(buf + rh_len);
if (len <= rh_len || ieee80211_print(wh))
PRINTF("[|802.11]");
t = (u_int8_t*)buf + sizeof(struct ieee80211_radiotap_header);
if ((present = letoh32(rh->it_present)) == 0)
goto out;
PRINTF(", <radiotap v%u", rh->it_version);
#define RADIOTAP(_x) \
(present & (1 << IEEE80211_RADIOTAP_##_x))
if (RADIOTAP(TSFT)) {
u_int64_t tsf;
u_int32_t tsf_v[2];
TCHECK2(*t, 8);
tsf = letoh64(*(u_int64_t *)t);
tsf_v[0] = (u_int32_t)(tsf >> 32);
tsf_v[1] = (u_int32_t)(tsf & 0x00000000ffffffff);
if (vflag > 1)
PRINTF(", tsf 0x%08x%08x", tsf_v[0], tsf_v[1]);
t += 8;
}
if (RADIOTAP(FLAGS)) {
u_int8_t flags = *(u_int8_t*)t;
TCHECK2(*t, 1);
if (flags & IEEE80211_RADIOTAP_F_CFP)
PRINTF(", CFP");
if (flags & IEEE80211_RADIOTAP_F_SHORTPRE)
PRINTF(", SHORTPRE");
if (flags & IEEE80211_RADIOTAP_F_WEP)
PRINTF(", WEP");
if (flags & IEEE80211_RADIOTAP_F_FRAG)
PRINTF(", FRAG");
t += 1;
}
if (RADIOTAP(RATE)) {
TCHECK2(*t, 1);
if (vflag)
PRINTF(", %uMbit/s", (*(u_int8_t*)t) / 2);
t += 1;
}
if (RADIOTAP(CHANNEL)) {
u_int16_t freq, flags;
TCHECK2(*t, 2);
freq = letoh16(*(u_int16_t*)t);
t += 2;
TCHECK2(*t, 2);
flags = letoh16(*(u_int16_t*)t);
t += 2;
PRINTF(", chan %u", ieee80211_any2ieee(freq, flags));
if (flags & IEEE80211_CHAN_DYN &&
flags & IEEE80211_CHAN_2GHZ)
PRINTF(", 11g");
else if (flags & IEEE80211_CHAN_CCK &&
flags & IEEE80211_CHAN_2GHZ)
PRINTF(", 11b");
else if (flags & IEEE80211_CHAN_OFDM &&
flags & IEEE80211_CHAN_2GHZ)
PRINTF(", 11G");
else if (flags & IEEE80211_CHAN_OFDM &&
flags & IEEE80211_CHAN_5GHZ)
PRINTF(", 11a");
if (flags & IEEE80211_CHAN_XR)
PRINTF(", XR");
}
if (RADIOTAP(FHSS)) {
TCHECK2(*t, 2);
PRINTF(", fhss %u/%u", *(u_int8_t*)t, *(u_int8_t*)t + 1);
t += 2;
}
if (RADIOTAP(DBM_ANTSIGNAL)) {
TCHECK(*t);
PRINTF(", sig %ddBm", *(int8_t*)t);
t += 1;
}
if (RADIOTAP(DBM_ANTNOISE)) {
TCHECK(*t);
PRINTF(", noise %ddBm", *(int8_t*)t);
t += 1;
}
if (RADIOTAP(LOCK_QUALITY)) {
TCHECK2(*t, 2);
if (vflag)
PRINTF(", quality %u", letoh16(*(u_int16_t*)t));
t += 2;
}
if (RADIOTAP(TX_ATTENUATION)) {
TCHECK2(*t, 2);
if (vflag)
PRINTF(", txatt %u",
letoh16(*(u_int16_t*)t));
t += 2;
}
if (RADIOTAP(DB_TX_ATTENUATION)) {
TCHECK2(*t, 2);
if (vflag)
PRINTF(", txatt %udB",
letoh16(*(u_int16_t*)t));
t += 2;
}
if (RADIOTAP(DBM_TX_POWER)) {
TCHECK(*t);
PRINTF(", txpower %ddBm", *(int8_t*)t);
t += 1;
}
if (RADIOTAP(ANTENNA)) {
TCHECK(*t);
if (vflag)
PRINTF(", antenna %u", *(u_int8_t*)t);
t += 1;
}
if (RADIOTAP(DB_ANTSIGNAL)) {
TCHECK(*t);
PRINTF(", signal %udB", *(u_int8_t*)t);
t += 1;
}
if (RADIOTAP(DB_ANTNOISE)) {
TCHECK(*t);
PRINTF(", noise %udB", *(u_int8_t*)t);
t += 1;
}
if (RADIOTAP(FCS)) {
TCHECK2(*t, 4);
if (vflag)
PRINTF(", fcs %08x", letoh32(*(u_int32_t*)t));
t += 4;
}
if (RADIOTAP(RSSI)) {
u_int8_t rssi, max_rssi;
TCHECK(*t);
rssi = *(u_int8_t*)t;
t += 1;
TCHECK(*t);
max_rssi = *(u_int8_t*)t;
t += 1;
PRINTF(", rssi %u/%u", rssi, max_rssi);
}
#undef RADIOTAP
PRINTF(">");
goto out;
trunc:
PRINTF("[|radiotap + 802.11]");
out:
PRINTF(NULL);
}
void
hostapd_print_ieee80211(u_int dlt, u_int verbose, u_int8_t *buf, u_int len)
{
if (verbose)
vflag = 1;
else
vflag = 0;
if (dlt == DLT_IEEE802_11)
ieee802_11_if_print(buf, len);
else
ieee802_11_radio_if_print(buf, len);
}