OpenBSD-4.6/usr.sbin/hostapd/print-802_11.c

Compare this file to the similar file:
Show the results in this format:

/*	$OpenBSD: print-802_11.c,v 1.6 2006/06/26 23:06:07 reyk Exp $	*/

/*
 * Copyright (c) 2005 Reyk Floeter <reyk@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* usr.sbin/tcpdump/print-802_11.c,v 1.3 2005/03/09 11:43:17 deraadt Exp */

#include <sys/param.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/ioctl.h>

#include <net/if.h>
#include <net/if_dl.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 "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:
	/* Truncated elements in frame */
	return (1);
}

/* Caller checks len */
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);
}

/* Caller checks len */
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;

	/* determine printable or not */
	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],	/* FH_CHAN */
			    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:
			if (!vflag)
				break;
			PRINTF(", tpc");
			ieee80211_print_element(data, len);
			break;
		case IEEE80211_ELEMID_CCKM:
			if (!vflag)
				break;
			PRINTF(", cckm");
			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:
	/* Truncated elements in frame */
	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);		/* Auth Algorithm */
			switch (IEEE80211_AUTH_ALGORITHM(frm)) {
			case IEEE80211_AUTH_ALG_OPEN:
				TCHECK2(*frm, 4);	/* Auth Transaction */
				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);	/* Auth Transaction */
				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:
	/* Truncated 802.11 frame */
	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 {
		/* Assume channel is already an IEEE number */
		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_TURBO)
			PRINTF(", TURBO");
		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:
	/* Truncated frame */
	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);
}