xref: /openbsd/usr.sbin/tcpdump/print-802_11.c (revision 0b19ae84)
1*0b19ae84Sstsp /*	$OpenBSD: print-802_11.c,v 1.27 2015/10/13 14:36:15 stsp Exp $	*/
212a08440Sreyk 
312a08440Sreyk /*
42c56d0d6Sreyk  * Copyright (c) 2005 Reyk Floeter <reyk@openbsd.org>
512a08440Sreyk  *
612a08440Sreyk  * Permission to use, copy, modify, and distribute this software for any
712a08440Sreyk  * purpose with or without fee is hereby granted, provided that the above
812a08440Sreyk  * copyright notice and this permission notice appear in all copies.
912a08440Sreyk  *
1012a08440Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1112a08440Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1212a08440Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1312a08440Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1412a08440Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1512a08440Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1612a08440Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1712a08440Sreyk  */
1812a08440Sreyk 
1912a08440Sreyk #include <sys/time.h>
2012a08440Sreyk #include <sys/socket.h>
2112a08440Sreyk #include <sys/file.h>
2212a08440Sreyk #include <sys/ioctl.h>
2312a08440Sreyk 
2412a08440Sreyk #include <net/if.h>
2512a08440Sreyk 
2612a08440Sreyk #include <netinet/in.h>
2712a08440Sreyk #include <netinet/if_ether.h>
2812a08440Sreyk 
2912a08440Sreyk #include <net80211/ieee80211.h>
3012a08440Sreyk #include <net80211/ieee80211_radiotap.h>
3112a08440Sreyk 
3277734011Sstsp #include <ctype.h>
3312a08440Sreyk #include <pcap.h>
3412a08440Sreyk #include <stdio.h>
3512a08440Sreyk #include <string.h>
3612a08440Sreyk 
3712a08440Sreyk #include "addrtoname.h"
3812a08440Sreyk #include "interface.h"
3912a08440Sreyk 
4012a08440Sreyk const char *ieee80211_mgt_subtype_name[] = {
4112a08440Sreyk 	"association request",
4212a08440Sreyk 	"association response",
4312a08440Sreyk 	"reassociation request",
4412a08440Sreyk 	"reassociation response",
4512a08440Sreyk 	"probe request",
4612a08440Sreyk 	"probe response",
4712a08440Sreyk 	"reserved#6",
4812a08440Sreyk 	"reserved#7",
4912a08440Sreyk 	"beacon",
5012a08440Sreyk 	"atim",
5112a08440Sreyk 	"disassociation",
5212a08440Sreyk 	"authentication",
5312a08440Sreyk 	"deauthentication",
54ab7e388eSclaudio 	"action",
55ab7e388eSclaudio 	"action noack",
5612a08440Sreyk 	"reserved#15"
5712a08440Sreyk };
5812a08440Sreyk 
59ab7e388eSclaudio const char *ieee80211_data_subtype_name[] = {
60ab7e388eSclaudio 	"data",
61ab7e388eSclaudio 	"data cf ack",
62ab7e388eSclaudio 	"data cf poll",
63ab7e388eSclaudio 	"data cf poll ack",
64ab7e388eSclaudio 	"no-data",
65ab7e388eSclaudio 	"no-data cf poll",
66ab7e388eSclaudio 	"no-data cf ack",
67ab7e388eSclaudio 	"no-data cf poll ack",
68ab7e388eSclaudio 	"QoS data",
69ab7e388eSclaudio 	"QoS data cf ack",
70ab7e388eSclaudio 	"QoS data cf poll",
71ab7e388eSclaudio 	"QoS data cf poll ack",
72ab7e388eSclaudio 	"QoS no-data",
73ab7e388eSclaudio 	"QoS no-data cf poll",
74ab7e388eSclaudio 	"QoS no-data cf ack",
75ab7e388eSclaudio 	"QoS no-data cf poll ack"
76ab7e388eSclaudio };
77ab7e388eSclaudio 
7812a08440Sreyk int	 ieee80211_hdr(struct ieee80211_frame *);
79b72abdefSreyk int	 ieee80211_data(struct ieee80211_frame *, u_int);
8012a08440Sreyk void	 ieee80211_print_element(u_int8_t *, u_int);
8112a08440Sreyk void	 ieee80211_print_essid(u_int8_t *, u_int);
8277734011Sstsp void	 ieee80211_print_country(u_int8_t *, u_int);
831e3bf20aSstsp void	 ieee80211_print_htcaps(u_int8_t *, u_int);
8436fa064cSstsp void	 ieee80211_print_htop(u_int8_t *, u_int);
8512a08440Sreyk int	 ieee80211_elements(struct ieee80211_frame *, u_int);
8612a08440Sreyk int	 ieee80211_frame(struct ieee80211_frame *, u_int);
8712a08440Sreyk int	 ieee80211_print(struct ieee80211_frame *, u_int);
8812a08440Sreyk u_int	 ieee80211_any2ieee(u_int, u_int);
8918786664Sclaudio void	 ieee80211_reason(u_int16_t);
9012a08440Sreyk 
9112a08440Sreyk #define TCARR(a)	TCHECK2(*a, sizeof(a))
9212a08440Sreyk 
93c79cf170Sreyk int ieee80211_encap = 0;
94c79cf170Sreyk 
9512a08440Sreyk int
9612a08440Sreyk ieee80211_hdr(struct ieee80211_frame *wh)
9712a08440Sreyk {
9812a08440Sreyk 	struct ieee80211_frame_addr4 *w4;
9912a08440Sreyk 
10012a08440Sreyk 	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
10112a08440Sreyk 	case IEEE80211_FC1_DIR_NODS:
10212a08440Sreyk 		TCARR(wh->i_addr2);
10312a08440Sreyk 		printf("%s", etheraddr_string(wh->i_addr2));
10412a08440Sreyk 		TCARR(wh->i_addr1);
10512a08440Sreyk 		printf(" > %s", etheraddr_string(wh->i_addr1));
10612a08440Sreyk 		TCARR(wh->i_addr3);
10712a08440Sreyk 		printf(", bssid %s", etheraddr_string(wh->i_addr3));
10812a08440Sreyk 		break;
10912a08440Sreyk 	case IEEE80211_FC1_DIR_TODS:
11012a08440Sreyk 		TCARR(wh->i_addr2);
11112a08440Sreyk 		printf("%s", etheraddr_string(wh->i_addr2));
11212a08440Sreyk 		TCARR(wh->i_addr3);
11312a08440Sreyk 		printf(" > %s", etheraddr_string(wh->i_addr3));
11412a08440Sreyk 		TCARR(wh->i_addr1);
11512a08440Sreyk 		printf(", bssid %s, > DS", etheraddr_string(wh->i_addr1));
11612a08440Sreyk 		break;
11712a08440Sreyk 	case IEEE80211_FC1_DIR_FROMDS:
11812a08440Sreyk 		TCARR(wh->i_addr3);
11912a08440Sreyk 		printf("%s", etheraddr_string(wh->i_addr3));
12012a08440Sreyk 		TCARR(wh->i_addr1);
12112a08440Sreyk 		printf(" > %s", etheraddr_string(wh->i_addr1));
12212a08440Sreyk 		TCARR(wh->i_addr2);
12312a08440Sreyk 		printf(", bssid %s, DS >", etheraddr_string(wh->i_addr2));
12412a08440Sreyk 		break;
12512a08440Sreyk 	case IEEE80211_FC1_DIR_DSTODS:
12612a08440Sreyk 		w4 = (struct ieee80211_frame_addr4 *) wh;
12712a08440Sreyk 		TCARR(w4->i_addr4);
12812a08440Sreyk 		printf("%s", etheraddr_string(w4->i_addr4));
12912a08440Sreyk 		TCARR(w4->i_addr3);
13012a08440Sreyk 		printf(" > %s", etheraddr_string(w4->i_addr3));
13112a08440Sreyk 		TCARR(w4->i_addr2);
13212a08440Sreyk 		printf(", bssid %s", etheraddr_string(w4->i_addr2));
13312a08440Sreyk 		TCARR(w4->i_addr1);
13412a08440Sreyk 		printf(" > %s, DS > DS", etheraddr_string(w4->i_addr1));
13512a08440Sreyk 		break;
13612a08440Sreyk 	}
13712a08440Sreyk 	if (vflag) {
138cce1ba42Sclaudio 		u_int16_t seq;
13912a08440Sreyk 		TCARR(wh->i_seq);
140cce1ba42Sclaudio 		bcopy(wh->i_seq, &seq, sizeof(u_int16_t));
141cce1ba42Sclaudio 		printf(" (seq %u): ", letoh16(seq));
14212a08440Sreyk 	} else
14312a08440Sreyk 		printf(": ");
14412a08440Sreyk 
14512a08440Sreyk 	return (0);
14612a08440Sreyk 
14712a08440Sreyk  trunc:
14812a08440Sreyk 	/* Truncated elements in frame */
14912a08440Sreyk 	return (1);
15012a08440Sreyk }
15112a08440Sreyk 
152b72abdefSreyk int
153b72abdefSreyk ieee80211_data(struct ieee80211_frame *wh, u_int len)
154b72abdefSreyk {
155b72abdefSreyk 	u_int8_t *t = (u_int8_t *)wh;
156d49cb46eScanacar 	struct ieee80211_frame_addr4 *w4;
157b72abdefSreyk 	u_int datalen;
15893cbfc44Sstsp 	int data = !(wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_NODATA);
159ab7e388eSclaudio 	u_char *esrc = NULL, *edst = NULL;
160b72abdefSreyk 
161d49cb46eScanacar 	TCHECK(*wh);
162b72abdefSreyk 	t += sizeof(struct ieee80211_frame);
163b72abdefSreyk 	datalen = len - sizeof(struct ieee80211_frame);
164b72abdefSreyk 
165b72abdefSreyk 	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
166b72abdefSreyk 	case IEEE80211_FC1_DIR_TODS:
167ab7e388eSclaudio 		esrc = wh->i_addr2;
168ab7e388eSclaudio 		edst = wh->i_addr3;
169b72abdefSreyk 		break;
170b72abdefSreyk 	case IEEE80211_FC1_DIR_FROMDS:
171ab7e388eSclaudio 		esrc = wh->i_addr3;
172ab7e388eSclaudio 		edst = wh->i_addr1;
173b72abdefSreyk 		break;
174b72abdefSreyk 	case IEEE80211_FC1_DIR_NODS:
175ab7e388eSclaudio 		esrc = wh->i_addr2;
176ab7e388eSclaudio 		edst = wh->i_addr1;
177d49cb46eScanacar 		break;
178b72abdefSreyk 	case IEEE80211_FC1_DIR_DSTODS:
179d49cb46eScanacar 		w4 = (struct ieee80211_frame_addr4 *) wh;
180d49cb46eScanacar 		TCHECK(*w4);
181d49cb46eScanacar 		t = (u_int8_t *) (w4 + 1);
182d49cb46eScanacar 		datalen = len - sizeof(*w4);
183ab7e388eSclaudio 		esrc = w4->i_addr4;
184ab7e388eSclaudio 		edst = w4->i_addr3;
185b72abdefSreyk 		break;
186b72abdefSreyk 	}
187b72abdefSreyk 
188ab7e388eSclaudio 	if (data && esrc)
189ab7e388eSclaudio 		llc_print(t, datalen, datalen, esrc, edst);
190ab7e388eSclaudio 	else if (eflag && esrc)
191ab7e388eSclaudio 		printf("%s > %s",
192ab7e388eSclaudio 		    etheraddr_string(esrc), etheraddr_string(edst));
193ab7e388eSclaudio 
194b72abdefSreyk 	return (0);
195b72abdefSreyk 
196b72abdefSreyk  trunc:
197b72abdefSreyk 	/* Truncated elements in frame */
198b72abdefSreyk 	return (1);
199b72abdefSreyk }
200b72abdefSreyk 
20112a08440Sreyk /* Caller checks len */
20212a08440Sreyk void
20312a08440Sreyk ieee80211_print_element(u_int8_t *data, u_int len)
20412a08440Sreyk {
20512a08440Sreyk 	u_int8_t *p;
2069cbdb746Sderaadt 	int i;
20712a08440Sreyk 
20812a08440Sreyk 	printf(" 0x");
2099cbdb746Sderaadt 	for (i = 0, p = data; i < len; i++, p++)
21012a08440Sreyk 		printf("%02x", *p);
21112a08440Sreyk }
21212a08440Sreyk 
21312a08440Sreyk /* Caller checks len */
21412a08440Sreyk void
21512a08440Sreyk ieee80211_print_essid(u_int8_t *essid, u_int len)
21612a08440Sreyk {
21712a08440Sreyk 	u_int8_t *p;
2189cbdb746Sderaadt 	int i;
21912a08440Sreyk 
22012a08440Sreyk 	if (len > IEEE80211_NWID_LEN)
22112a08440Sreyk 		len = IEEE80211_NWID_LEN;
22212a08440Sreyk 
22312a08440Sreyk 	/* determine printable or not */
22412a08440Sreyk 	for (i = 0, p = essid; i < len; i++, p++) {
22512a08440Sreyk 		if (*p < ' ' || *p > 0x7e)
22612a08440Sreyk 			break;
22712a08440Sreyk 	}
22812a08440Sreyk 	if (i == len) {
22912a08440Sreyk 		printf(" (");
23012a08440Sreyk 		for (i = 0, p = essid; i < len; i++, p++)
23112a08440Sreyk 			putchar(*p);
23212a08440Sreyk 		putchar(')');
2339cbdb746Sderaadt 	} else
23412a08440Sreyk 		ieee80211_print_element(essid, len);
23512a08440Sreyk }
23612a08440Sreyk 
2371e3bf20aSstsp /* Caller checks len */
2381e3bf20aSstsp void
23977734011Sstsp ieee80211_print_country(u_int8_t *data, u_int len)
24077734011Sstsp {
24177734011Sstsp 	u_int8_t first_chan, nchan, maxpower;
24277734011Sstsp 
24377734011Sstsp 	if (len < 6)
24477734011Sstsp 		return;
24577734011Sstsp 
24677734011Sstsp 	/* country string */
24777734011Sstsp 	printf((isprint(data[0]) ? " '%c" : " '\\%03o"), data[0]);
24877734011Sstsp 	printf((isprint(data[1]) ? "%c" : "\\%03o"), data[1]);
24977734011Sstsp 	printf((isprint(data[2]) ? "%c'" : "\\%03o'"), data[2]);
25077734011Sstsp 
25177734011Sstsp 	len -= 3;
25277734011Sstsp 	data += 3;
25377734011Sstsp 
25477734011Sstsp 	/* channels and corresponding TX power limits */
25577734011Sstsp 	while (len > 3)	{
25677734011Sstsp 		/* no pretty-printing for nonsensical zero values,
25777734011Sstsp 		 * nor for operating extension IDs (values >= 201) */
25877734011Sstsp 		if (data[0] == 0 || data[1] == 0 ||
25977734011Sstsp 		    data[0] >= 201 || data[1] >= 201) {
26077734011Sstsp 			printf(", %d %d %d", data[0], data[1], data[2]);
26177734011Sstsp 			continue;
26277734011Sstsp 		}
26377734011Sstsp 
26477734011Sstsp 		first_chan = data[0];
26577734011Sstsp 		nchan = data[1];
26677734011Sstsp 		maxpower = data[2];
26777734011Sstsp 
26877734011Sstsp 		printf(", channel%s %d", nchan == 1 ? "" : "s", first_chan);
26977734011Sstsp 		if (nchan > 1)
27077734011Sstsp 			printf("-%d", first_chan + nchan - 1);
27177734011Sstsp 		printf(" limit %ddB", maxpower);
27277734011Sstsp 
27377734011Sstsp 		len -= 3;
27477734011Sstsp 		data += 3;
27577734011Sstsp 	}
27677734011Sstsp }
27777734011Sstsp 
27877734011Sstsp /* Caller checks len */
27977734011Sstsp void
2801e3bf20aSstsp ieee80211_print_htcaps(u_int8_t *data, u_int len)
2811e3bf20aSstsp {
282537e7cd0Sstsp 	uint16_t htcaps, rxrate;
2831e3bf20aSstsp 	int smps, rxstbc;
284537e7cd0Sstsp 	uint8_t ampdu, txmcs;
285537e7cd0Sstsp 	int i;
286537e7cd0Sstsp 	uint8_t *rxmcs;
2871e3bf20aSstsp 
2881e3bf20aSstsp 	if (len < 2) {
2891e3bf20aSstsp 		ieee80211_print_element(data, len);
2901e3bf20aSstsp 		return;
2911e3bf20aSstsp 	}
2921e3bf20aSstsp 
2931e3bf20aSstsp 	htcaps = (data[0]) | (data[1] << 8);
2941e3bf20aSstsp 	printf("=<");
2951e3bf20aSstsp 
2961e3bf20aSstsp 	/* channel width */
2971e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_CBW20_40)
2981e3bf20aSstsp 		printf("20/40MHz");
2991e3bf20aSstsp 	else
3001e3bf20aSstsp 		printf("20MHz");
3011e3bf20aSstsp 
3021e3bf20aSstsp 	/* LDPC coding */
3031e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_LDPC)
3041e3bf20aSstsp 		printf(",LDPC");
3051e3bf20aSstsp 
3061e3bf20aSstsp 	/* spatial multiplexing power save mode */
3071e3bf20aSstsp 	smps = (htcaps & IEEE80211_HTCAP_SMPS_MASK)
3081e3bf20aSstsp 	    >> IEEE80211_HTCAP_SMPS_SHIFT;
3091e3bf20aSstsp 	if (smps == 0)
3101e3bf20aSstsp 		printf(",SMPS static");
3111e3bf20aSstsp 	else if (smps == 1)
3121e3bf20aSstsp 		printf(",SMPS dynamic");
3131e3bf20aSstsp 
3141e3bf20aSstsp 	/* 11n greenfield mode */
3151e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_GF)
3161e3bf20aSstsp 		printf(",greenfield");
3171e3bf20aSstsp 
3181e3bf20aSstsp 	/* short guard interval */
3191e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_SGI20)
3201e3bf20aSstsp 		printf(",SGI@20MHz");
3211e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_SGI40)
3221e3bf20aSstsp 		printf(",SGI@40MHz");
3231e3bf20aSstsp 
3241e3bf20aSstsp 	/* space-time block coding */
3251e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_TXSTBC)
3261e3bf20aSstsp 		printf(",TXSTBC");
3271e3bf20aSstsp 	rxstbc = (htcaps & IEEE80211_HTCAP_RXSTBC_MASK)
3281e3bf20aSstsp 	    >> IEEE80211_HTCAP_RXSTBC_SHIFT;
3291e3bf20aSstsp 	if (rxstbc > 0 && rxstbc < 4)
3301e3bf20aSstsp 		printf(",RXSTBC %d stream", rxstbc);
3311e3bf20aSstsp 
3321e3bf20aSstsp 	/* delayed block-ack */
3331e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_DELAYEDBA)
3341e3bf20aSstsp 		printf(",delayed BA");
3351e3bf20aSstsp 
3361e3bf20aSstsp 	/* max A-MSDU length */
3371e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_AMSDU7935)
3381e3bf20aSstsp 		printf(",A-MSDU 7935");
3391e3bf20aSstsp 	else
3401e3bf20aSstsp 		printf(",A-MSDU 3839");
3411e3bf20aSstsp 
3421e3bf20aSstsp 	/* DSSS/CCK in 40MHz mode */
3431e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_DSSSCCK40)
3441e3bf20aSstsp 		printf(",DSSS/CCK@40MHz");
3451e3bf20aSstsp 
3461e3bf20aSstsp 	/* 40MHz intolerant */
3471e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_40INTOLERANT)
3481e3bf20aSstsp 		printf(",40MHz intolerant");
3491e3bf20aSstsp 
3501e3bf20aSstsp 	/* L-SIG TXOP protection */
3511e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_LSIGTXOPPROT)
3521e3bf20aSstsp 		printf(",L-SIG TXOP prot");
3531e3bf20aSstsp 
354537e7cd0Sstsp 	if (len < 3) {
355537e7cd0Sstsp 		printf(">");
356537e7cd0Sstsp 		return;
357537e7cd0Sstsp 	}
358537e7cd0Sstsp 
359537e7cd0Sstsp 	/* A-MPDU parameters. */
360537e7cd0Sstsp 	ampdu = data[2];
361537e7cd0Sstsp 
362537e7cd0Sstsp 	/* A-MPDU length exponent */
363537e7cd0Sstsp 	if ((ampdu & IEEE80211_AMPDU_PARAM_LE) >= 0 &&
364537e7cd0Sstsp 	    (ampdu & IEEE80211_AMPDU_PARAM_LE) <= 3)
365537e7cd0Sstsp 		printf(",A-MPDU max %d",
366*0b19ae84Sstsp 		    (1 << (13 + (ampdu & IEEE80211_AMPDU_PARAM_LE))) - 1);
367537e7cd0Sstsp 
368537e7cd0Sstsp 	/* A-MPDU start spacing */
369537e7cd0Sstsp 	if (ampdu & IEEE80211_AMPDU_PARAM_SS) {
370537e7cd0Sstsp 		float ss;
371537e7cd0Sstsp 
372537e7cd0Sstsp 		switch ((ampdu & IEEE80211_AMPDU_PARAM_SS) >> 2) {
373537e7cd0Sstsp 		case 1:
374537e7cd0Sstsp 			ss = 0.25;
375537e7cd0Sstsp 			break;
376537e7cd0Sstsp 		case 2:
377537e7cd0Sstsp 			ss = 0.5;
378537e7cd0Sstsp 			break;
379537e7cd0Sstsp 		case 3:
380537e7cd0Sstsp 			ss = 1;
381537e7cd0Sstsp 			break;
382537e7cd0Sstsp 		case 4:
383537e7cd0Sstsp 			ss = 2;
384537e7cd0Sstsp 			break;
385537e7cd0Sstsp 		case 5:
386537e7cd0Sstsp 			ss = 4;
387537e7cd0Sstsp 			break;
388537e7cd0Sstsp 		case 6:
389537e7cd0Sstsp 			ss = 8;
390537e7cd0Sstsp 			break;
391537e7cd0Sstsp 		case 7:
392537e7cd0Sstsp 			ss = 16;
393537e7cd0Sstsp 			break;
394537e7cd0Sstsp 		default:
395537e7cd0Sstsp 			ss = 0;
396537e7cd0Sstsp 			break;
397537e7cd0Sstsp 		}
398537e7cd0Sstsp 		if (ss != 0)
399537e7cd0Sstsp 			printf(",A-MPDU spacing %.2fus", ss);
400537e7cd0Sstsp 	}
401537e7cd0Sstsp 
402537e7cd0Sstsp 	if (len < 21) {
403537e7cd0Sstsp 		printf(">");
404537e7cd0Sstsp 		return;
405537e7cd0Sstsp 	}
406537e7cd0Sstsp 
407537e7cd0Sstsp 	/* Supported MCS set. */
408537e7cd0Sstsp 	printf(",RxMCS 0x");
409537e7cd0Sstsp 	rxmcs = &data[3];
410537e7cd0Sstsp 	for (i = 0; i < 10; i++)
411537e7cd0Sstsp 		printf("%02x", rxmcs[i]);
412537e7cd0Sstsp 
413537e7cd0Sstsp 	/* Max MCS Rx rate (a value of 0 means "not specified"). */
414537e7cd0Sstsp 	rxrate = ((data[13] | (data[14]) << 8) & IEEE80211_MCS_RX_RATE_HIGH);
415537e7cd0Sstsp 	if (rxrate)
416537e7cd0Sstsp 		printf(",RxMaxrate %huMb/s", rxrate);
417537e7cd0Sstsp 
418537e7cd0Sstsp 	/* Tx MCS Set */
419537e7cd0Sstsp 	txmcs = data[15];
420537e7cd0Sstsp 	if (txmcs & IEEE80211_TX_MCS_SET_DEFINED) {
421537e7cd0Sstsp 		if (txmcs & IEEE80211_TX_RX_MCS_NOT_EQUAL) {
422537e7cd0Sstsp 			/* Number of spatial Tx streams. */
423537e7cd0Sstsp 			printf(",%d Tx streams",
424537e7cd0Sstsp 			     1 + ((txmcs & IEEE80211_TX_SPATIAL_STREAMS) >> 2));
425537e7cd0Sstsp 			/* Transmit unequal modulation supported. */
426537e7cd0Sstsp 			if (txmcs & IEEE80211_TX_UNEQUAL_MODULATION)
427537e7cd0Sstsp 				printf(",UEQM");
428537e7cd0Sstsp 		}
429537e7cd0Sstsp 	}
430537e7cd0Sstsp 
4311e3bf20aSstsp 	printf(">");
4321e3bf20aSstsp }
4331e3bf20aSstsp 
43436fa064cSstsp /* Caller checks len */
43536fa064cSstsp void
43636fa064cSstsp ieee80211_print_htop(u_int8_t *data, u_int len)
43736fa064cSstsp {
43836fa064cSstsp 	u_int8_t primary_chan;
43936fa064cSstsp 	u_int8_t htopinfo[5];
44036fa064cSstsp 	u_int8_t basic_mcs[16];
44136fa064cSstsp 	int sco, prot, i;
44236fa064cSstsp 
44336fa064cSstsp 	if (len < sizeof(primary_chan) + sizeof(htopinfo) + sizeof(basic_mcs)) {
44436fa064cSstsp 		ieee80211_print_element(data, len);
44536fa064cSstsp 		return;
44636fa064cSstsp 	}
44736fa064cSstsp 
44836fa064cSstsp 	htopinfo[0] = data[1];
44936fa064cSstsp 
45036fa064cSstsp 	printf("=<");
45136fa064cSstsp 
45236fa064cSstsp 	/* primary channel and secondary channel offset */
45336fa064cSstsp 	primary_chan = data[0];
45436fa064cSstsp 	sco = ((htopinfo[0] & IEEE80211_HTOP0_SCO_MASK)
45536fa064cSstsp 	    >> IEEE80211_HTOP0_SCO_SHIFT);
456b144e175Sstsp 	if (sco == 0) /* no secondary channel */
45736fa064cSstsp 		printf("20MHz chan %d", primary_chan);
458b144e175Sstsp 	else if (sco == 1) { /* secondary channel above */
459b144e175Sstsp 		if (primary_chan >= 1 && primary_chan <= 13) /* 2GHz */
460b144e175Sstsp 			printf("40MHz chan %d:%d", primary_chan,
461b144e175Sstsp 			    primary_chan + 1);
462b144e175Sstsp 		else if (primary_chan >= 34) /* 5GHz */
463b144e175Sstsp 			printf("40MHz chan %d:%d", primary_chan,
464b144e175Sstsp 			    primary_chan + 4);
46536fa064cSstsp 		else
466b144e175Sstsp 			printf("[invalid 40MHz chan %d+]", primary_chan);
467b144e175Sstsp 	} else if (sco == 3) { /* secondary channel below */
468b144e175Sstsp 		if (primary_chan >= 2 && primary_chan <= 14) /* 2GHz */
469b144e175Sstsp 			printf("40MHz chan %d:%d", primary_chan,
470b144e175Sstsp 			    primary_chan - 1);
471b144e175Sstsp 		else if (primary_chan >= 40) /* 5GHz */
472b144e175Sstsp 			printf("40MHz chan %d:%d", primary_chan,
473b144e175Sstsp 			    primary_chan - 4);
474b144e175Sstsp 		else
475b144e175Sstsp 			printf("[invalid 40MHz chan %d-]", primary_chan);
476b144e175Sstsp 	} else
47736fa064cSstsp 		printf("chan %d [invalid secondary channel offset %d]",
47836fa064cSstsp 		    primary_chan, sco);
47936fa064cSstsp 
48036fa064cSstsp 	/* STA channel width */
48136fa064cSstsp 	if ((htopinfo[0] & IEEE80211_HTOP0_CHW) == 0)
48236fa064cSstsp 		printf(",STA chanw 20MHz");
48336fa064cSstsp 
48436fa064cSstsp 	/* reduced interframe space (RIFS) permitted */
48536fa064cSstsp 	if (htopinfo[0] & IEEE80211_HTOP0_RIFS)
48636fa064cSstsp 		printf(",RIFS");
48736fa064cSstsp 
48836fa064cSstsp 	htopinfo[1] = data[2];
48936fa064cSstsp 
49036fa064cSstsp 	/* protection requirements for HT transmissions */
49136fa064cSstsp 	prot = ((htopinfo[1] & IEEE80211_HTOP1_PROT_MASK)
49236fa064cSstsp 	    >> IEEE80211_HTOP1_PROT_SHIFT);
49336fa064cSstsp 	if (prot == 1)
49436fa064cSstsp 		printf(",protect non-member");
49536fa064cSstsp 	else if (prot == 2)
49636fa064cSstsp 		printf(",protect 20MHz");
49736fa064cSstsp 	else if (prot == 3)
49836fa064cSstsp 		printf(",protect non-HT");
49936fa064cSstsp 
50036fa064cSstsp 	/* non-greenfield STA present */
50136fa064cSstsp 	if (htopinfo[1] & IEEE80211_HTOP1_NONGF_STA)
50236fa064cSstsp 		printf(",non-greenfield STA");
50336fa064cSstsp 
50436fa064cSstsp 	/* non-HT STA present */
50536fa064cSstsp 	if (htopinfo[1] & IEEE80211_HTOP1_OBSS_NONHT_STA)
50636fa064cSstsp 		printf(",non-HT STA");
50736fa064cSstsp 
50836fa064cSstsp 	htopinfo[3] = data[4];
50936fa064cSstsp 
51036fa064cSstsp 	/* dual-beacon */
51136fa064cSstsp 	if (htopinfo[3] & IEEE80211_HTOP2_DUALBEACON)
51236fa064cSstsp 		printf(",dualbeacon");
51336fa064cSstsp 
51436fa064cSstsp 	/* dual CTS protection */
51536fa064cSstsp 	if (htopinfo[3] & IEEE80211_HTOP2_DUALCTSPROT)
51636fa064cSstsp 		printf(",dualctsprot");
51736fa064cSstsp 
51836fa064cSstsp 	htopinfo[4] = data[5];
51936fa064cSstsp 
52036fa064cSstsp 	/* space-time block coding (STBC) beacon */
52158089030Sstsp 	if ((htopinfo[4] << 8) & IEEE80211_HTOP2_STBCBEACON)
52236fa064cSstsp 		printf(",STBC beacon");
52336fa064cSstsp 
52436fa064cSstsp 	/* L-SIG (non-HT signal field) TX opportunity (TXOP) protection */
52536fa064cSstsp 	if ((htopinfo[4] << 8) & IEEE80211_HTOP2_LSIGTXOP)
52636fa064cSstsp 		printf(",lsigtxprot");
52736fa064cSstsp 
52836fa064cSstsp 	/* phased-coexistence operation (PCO) active */
52936fa064cSstsp 	if ((htopinfo[4] << 8) & IEEE80211_HTOP2_PCOACTIVE) {
53036fa064cSstsp 		/* PCO phase */
53136fa064cSstsp 		if ((htopinfo[4] << 8) & IEEE80211_HTOP2_PCOPHASE40)
53236fa064cSstsp 			printf(",pco40MHz");
53336fa064cSstsp 		else
53436fa064cSstsp 			printf(",pco20MHz");
53536fa064cSstsp 	}
53636fa064cSstsp 
53736fa064cSstsp 	/* basic MCS set */
53836fa064cSstsp 	memcpy(basic_mcs, &data[6], sizeof(basic_mcs));
53936fa064cSstsp 	printf(",basic MCS set 0x");
54036fa064cSstsp 	for (i = 0; i < sizeof(basic_mcs) / sizeof(basic_mcs[0]); i++)
54136fa064cSstsp 			printf("%x", basic_mcs[i]);
54236fa064cSstsp 
54336fa064cSstsp 	printf(">");
54436fa064cSstsp }
54536fa064cSstsp 
54612a08440Sreyk int
54712a08440Sreyk ieee80211_elements(struct ieee80211_frame *wh, u_int flen)
54812a08440Sreyk {
54912a08440Sreyk 	u_int8_t *buf, *frm;
550cce1ba42Sclaudio 	u_int64_t tstamp;
551cce1ba42Sclaudio 	u_int16_t bintval, capinfo;
55212a08440Sreyk 	int i;
55312a08440Sreyk 
55412a08440Sreyk 	buf = (u_int8_t *)wh;
55512a08440Sreyk 	frm = (u_int8_t *)&wh[1];
55612a08440Sreyk 
5570a05c5bfSreyk 	TCHECK2(*frm, 8);
558cce1ba42Sclaudio 	bcopy(frm, &tstamp, sizeof(u_int64_t));
55912a08440Sreyk 	frm += 8;
56012a08440Sreyk 
56112a08440Sreyk 	if (vflag > 1)
562cce1ba42Sclaudio 		printf(", timestamp %llu", letoh64(tstamp));
56312a08440Sreyk 
5640a05c5bfSreyk 	TCHECK2(*frm, 2);
565cce1ba42Sclaudio 	bcopy(frm, &bintval, sizeof(u_int16_t));
56612a08440Sreyk 	frm += 2;
56712a08440Sreyk 
56812a08440Sreyk 	if (vflag > 1)
569cce1ba42Sclaudio 		printf(", interval %u", letoh16(bintval));
57012a08440Sreyk 
5710a05c5bfSreyk 	TCHECK2(*frm, 2);
572cce1ba42Sclaudio 	bcopy(frm, &capinfo, sizeof(u_int16_t));
57312a08440Sreyk 	frm += 2;
57412a08440Sreyk 
57512a08440Sreyk 	if (vflag)
576cce1ba42Sclaudio 		printb(", caps", letoh16(capinfo),
57712a08440Sreyk 		    IEEE80211_CAPINFO_BITS);
57812a08440Sreyk 
57912a08440Sreyk 	while (TTEST2(*frm, 2)) {
58012a08440Sreyk 		u_int len = frm[1];
58112a08440Sreyk 		u_int8_t *data = frm + 2;
58212a08440Sreyk 
58312a08440Sreyk 		if (!TTEST2(*data, len))
58412a08440Sreyk 			break;
58512a08440Sreyk 
58612a08440Sreyk #define ELEM_CHECK(l)	if (len != l) break
58712a08440Sreyk 
58812a08440Sreyk 		switch (*frm) {
58912a08440Sreyk 		case IEEE80211_ELEMID_SSID:
59012a08440Sreyk 			printf(", ssid");
59112a08440Sreyk 			ieee80211_print_essid(data, len);
59212a08440Sreyk 			break;
59312a08440Sreyk 		case IEEE80211_ELEMID_RATES:
59412a08440Sreyk 			printf(", rates");
59512a08440Sreyk 			if (!vflag)
59612a08440Sreyk 				break;
59712a08440Sreyk 			for (i = len; i > 0; i--, data++)
59812a08440Sreyk 				printf(" %uM",
59912a08440Sreyk 				    (data[0] & IEEE80211_RATE_VAL) / 2);
60012a08440Sreyk 			break;
60112a08440Sreyk 		case IEEE80211_ELEMID_FHPARMS:
60212a08440Sreyk 			ELEM_CHECK(5);
60312a08440Sreyk 			printf(", fh (dwell %u, chan %u, index %u)",
60412a08440Sreyk 			    (data[1] << 8) | data[0],
60512a08440Sreyk 			    (data[2] - 1) * 80 + data[3],	/* FH_CHAN */
60612a08440Sreyk 			    data[4]);
60712a08440Sreyk 			break;
60812a08440Sreyk 		case IEEE80211_ELEMID_DSPARMS:
60912a08440Sreyk 			ELEM_CHECK(1);
61012a08440Sreyk 			printf(", ds");
61112a08440Sreyk 			if (vflag)
61212a08440Sreyk 				printf(" (chan %u)", data[0]);
61312a08440Sreyk 			break;
61412a08440Sreyk 		case IEEE80211_ELEMID_CFPARMS:
61512a08440Sreyk 			printf(", cf");
61612a08440Sreyk 			if (vflag)
61712a08440Sreyk 				ieee80211_print_element(data, len);
61812a08440Sreyk 			break;
61912a08440Sreyk 		case IEEE80211_ELEMID_TIM:
62012a08440Sreyk 			printf(", tim");
62112a08440Sreyk 			if (vflag)
62212a08440Sreyk 				ieee80211_print_element(data, len);
62312a08440Sreyk 			break;
62412a08440Sreyk 		case IEEE80211_ELEMID_IBSSPARMS:
62512a08440Sreyk 			printf(", ibss");
62612a08440Sreyk 			if (vflag)
62712a08440Sreyk 				ieee80211_print_element(data, len);
62812a08440Sreyk 			break;
62912a08440Sreyk 		case IEEE80211_ELEMID_COUNTRY:
63012a08440Sreyk 			printf(", country");
63177734011Sstsp 			ieee80211_print_country(data, len);
63212a08440Sreyk 			break;
63312a08440Sreyk 		case IEEE80211_ELEMID_CHALLENGE:
63412a08440Sreyk 			printf(", challenge");
63512a08440Sreyk 			if (vflag)
63612a08440Sreyk 				ieee80211_print_element(data, len);
63712a08440Sreyk 			break;
638738acb58Ssthen 		case IEEE80211_ELEMID_CSA:
639958b7e45Ssthen 			ELEM_CHECK(3);
640738acb58Ssthen 			printf(", csa (chan %u count %u%s)", data[1], data[2],
641738acb58Ssthen 			    (data[0] == 1) ? " noTX" : "");
642738acb58Ssthen 			break;
64312a08440Sreyk 		case IEEE80211_ELEMID_ERP:
64412a08440Sreyk 			printf(", erp");
64512a08440Sreyk 			if (vflag)
64612a08440Sreyk 				ieee80211_print_element(data, len);
64712a08440Sreyk 			break;
64812a08440Sreyk 		case IEEE80211_ELEMID_RSN:
64912a08440Sreyk 			printf(", rsn");
65012a08440Sreyk 			if (vflag)
65112a08440Sreyk 				ieee80211_print_element(data, len);
65212a08440Sreyk 			break;
65312a08440Sreyk 		case IEEE80211_ELEMID_XRATES:
65412a08440Sreyk 			printf(", xrates");
65512a08440Sreyk 			if (!vflag)
65612a08440Sreyk 				break;
65712a08440Sreyk 			for (i = len; i > 0; i--, data++)
65812a08440Sreyk 				printf(" %uM",
65912a08440Sreyk 				    (data[0] & IEEE80211_RATE_VAL) / 2);
66012a08440Sreyk 			break;
6618138faddSstsp 		case IEEE80211_ELEMID_TPC_REPORT:
6628138faddSstsp 			printf(", tpcreport");
66312a08440Sreyk 			if (vflag)
66412a08440Sreyk 				ieee80211_print_element(data, len);
66512a08440Sreyk 			break;
6668138faddSstsp 		case IEEE80211_ELEMID_TPC_REQUEST:
6678138faddSstsp 			printf(", tpcrequest");
66812a08440Sreyk 			if (vflag)
66912a08440Sreyk 				ieee80211_print_element(data, len);
67012a08440Sreyk 			break;
6711e3bf20aSstsp 		case IEEE80211_ELEMID_HTCAPS:
6721e3bf20aSstsp 			printf(", htcaps");
6731e3bf20aSstsp 			if (vflag)
6741e3bf20aSstsp 				ieee80211_print_htcaps(data, len);
6751e3bf20aSstsp 			break;
67636fa064cSstsp 		case IEEE80211_ELEMID_HTOP:
67736fa064cSstsp 			printf(", htop");
67836fa064cSstsp 			if (vflag)
67936fa064cSstsp 				ieee80211_print_htop(data, len);
68036fa064cSstsp 			break;
68177734011Sstsp 		case IEEE80211_ELEMID_POWER_CONSTRAINT:
68277734011Sstsp 			ELEM_CHECK(1);
68377734011Sstsp 			printf(", power constraint %udB", data[0]);
68477734011Sstsp 			break;
6858f0a8537Sstsp 		case IEEE80211_ELEMID_QBSS_LOAD:
6868f0a8537Sstsp 			ELEM_CHECK(5);
6878f0a8537Sstsp 			printf(", %u stations, %d%% utilization, "
6888f0a8537Sstsp 			    "admission capacity %uus/s",
6898f0a8537Sstsp 			    (data[0] | data[1] << 8),
6908f0a8537Sstsp 			    (data[2] * 100) / 255,
6918f0a8537Sstsp 			    (data[3] | data[4] << 8) / 32);
6928f0a8537Sstsp 			break;
69312a08440Sreyk 		case IEEE80211_ELEMID_VENDOR:
69412a08440Sreyk 			printf(", vendor");
69512a08440Sreyk 			if (vflag)
69612a08440Sreyk 				ieee80211_print_element(data, len);
69712a08440Sreyk 			break;
69812a08440Sreyk 		default:
69912a08440Sreyk 			printf(", %u:%u", (u_int) *frm, len);
70012a08440Sreyk 			if (vflag)
70112a08440Sreyk 				ieee80211_print_element(data, len);
70212a08440Sreyk 			break;
70312a08440Sreyk 		}
70412a08440Sreyk 		frm += len + 2;
70512a08440Sreyk 
70612a08440Sreyk 		if (frm >= snapend)
70712a08440Sreyk 			break;
70812a08440Sreyk 	}
70912a08440Sreyk 
71012a08440Sreyk #undef ELEM_CHECK
71112a08440Sreyk 
71212a08440Sreyk 	return (0);
71312a08440Sreyk 
71412a08440Sreyk  trunc:
71512a08440Sreyk 	/* Truncated elements in frame */
71612a08440Sreyk 	return (1);
71712a08440Sreyk }
71812a08440Sreyk 
71912a08440Sreyk int
72012a08440Sreyk ieee80211_frame(struct ieee80211_frame *wh, u_int len)
72112a08440Sreyk {
72212a08440Sreyk 	u_int8_t subtype, type, *frm;
72312a08440Sreyk 
72412a08440Sreyk 	TCARR(wh->i_fc);
72512a08440Sreyk 
72612a08440Sreyk 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
72712a08440Sreyk 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
72812a08440Sreyk 
72912a08440Sreyk 	frm = (u_int8_t *)&wh[1];
73012a08440Sreyk 
731ab7e388eSclaudio 	if (vflag)
732ab7e388eSclaudio 		printb(" flags", wh->i_fc[1], IEEE80211_FC1_BITS);
733ab7e388eSclaudio 
73412a08440Sreyk 	switch (type) {
73512a08440Sreyk 	case IEEE80211_FC0_TYPE_DATA:
736ab7e388eSclaudio 		printf(": %s: ", ieee80211_data_subtype_name[
737ab7e388eSclaudio 		    subtype >> IEEE80211_FC0_SUBTYPE_SHIFT]);
738b72abdefSreyk 		ieee80211_data(wh, len);
73912a08440Sreyk 		break;
74012a08440Sreyk 	case IEEE80211_FC0_TYPE_MGT:
74112a08440Sreyk 		printf(": %s", ieee80211_mgt_subtype_name[
74212a08440Sreyk 		    subtype >> IEEE80211_FC0_SUBTYPE_SHIFT]);
74312a08440Sreyk 		switch (subtype) {
74412a08440Sreyk 		case IEEE80211_FC0_SUBTYPE_BEACON:
74512a08440Sreyk 		case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
74612a08440Sreyk 			if (ieee80211_elements(wh, len) != 0)
74712a08440Sreyk 				goto trunc;
74812a08440Sreyk 			break;
74912a08440Sreyk 		case IEEE80211_FC0_SUBTYPE_AUTH:
75012a08440Sreyk 			TCHECK2(*frm, 2);		/* Auth Algorithm */
75112a08440Sreyk 			switch (IEEE80211_AUTH_ALGORITHM(frm)) {
75212a08440Sreyk 			case IEEE80211_AUTH_ALG_OPEN:
75312a08440Sreyk 				TCHECK2(*frm, 4);	/* Auth Transaction */
75412a08440Sreyk 				switch (IEEE80211_AUTH_TRANSACTION(frm)) {
75512a08440Sreyk 				case IEEE80211_AUTH_OPEN_REQUEST:
75612a08440Sreyk 					printf(" request");
75712a08440Sreyk 					break;
75812a08440Sreyk 				case IEEE80211_AUTH_OPEN_RESPONSE:
75912a08440Sreyk 					printf(" response");
76012a08440Sreyk 					break;
76112a08440Sreyk 				}
76212a08440Sreyk 				break;
76312a08440Sreyk 			case IEEE80211_AUTH_ALG_SHARED:
76412a08440Sreyk 				TCHECK2(*frm, 4);	/* Auth Transaction */
76512a08440Sreyk 				switch (IEEE80211_AUTH_TRANSACTION(frm)) {
76612a08440Sreyk 				case IEEE80211_AUTH_SHARED_REQUEST:
76712a08440Sreyk 					printf(" request");
76812a08440Sreyk 					break;
76912a08440Sreyk 				case IEEE80211_AUTH_SHARED_CHALLENGE:
77012a08440Sreyk 					printf(" challenge");
77112a08440Sreyk 					break;
77212a08440Sreyk 				case IEEE80211_AUTH_SHARED_RESPONSE:
77312a08440Sreyk 					printf(" response");
77412a08440Sreyk 					break;
77512a08440Sreyk 				case IEEE80211_AUTH_SHARED_PASS:
77612a08440Sreyk 					printf(" pass");
77712a08440Sreyk 					break;
77812a08440Sreyk 				}
77912a08440Sreyk 				break;
78012a08440Sreyk 			case IEEE80211_AUTH_ALG_LEAP:
78112a08440Sreyk 				printf(" (leap)");
78212a08440Sreyk 				break;
78312a08440Sreyk 			}
78412a08440Sreyk 			break;
78518786664Sclaudio 		case IEEE80211_FC0_SUBTYPE_DEAUTH:
78618786664Sclaudio 		case IEEE80211_FC0_SUBTYPE_DISASSOC:
78718786664Sclaudio 			TCHECK2(*frm, 2);		/* Reason Code */
78818786664Sclaudio 			ieee80211_reason(frm[0] | (frm[1] << 8));
78918786664Sclaudio 			break;
79012a08440Sreyk 		}
79112a08440Sreyk 		break;
79212a08440Sreyk 	default:
79312a08440Sreyk 		printf(": type#%d", type);
79412a08440Sreyk 		break;
79512a08440Sreyk 	}
79612a08440Sreyk 
79712a08440Sreyk 	return (0);
79812a08440Sreyk 
79912a08440Sreyk  trunc:
80012a08440Sreyk 	/* Truncated 802.11 frame */
80112a08440Sreyk 	return (1);
80212a08440Sreyk }
80312a08440Sreyk 
80412a08440Sreyk u_int
80512a08440Sreyk ieee80211_any2ieee(u_int freq, u_int flags)
80612a08440Sreyk {
80712a08440Sreyk 	if (flags & IEEE80211_CHAN_2GHZ) {
80812a08440Sreyk 		if (freq == 2484)
80912a08440Sreyk 			return 14;
81012a08440Sreyk 		if (freq < 2484)
81112a08440Sreyk 			return (freq - 2407) / 5;
81212a08440Sreyk 		else
81312a08440Sreyk 			return 15 + ((freq - 2512) / 20);
81412a08440Sreyk 	} else if (flags & IEEE80211_CHAN_5GHZ) {
81512a08440Sreyk 		return (freq - 5000) / 5;
81612a08440Sreyk 	} else {
81712a08440Sreyk 		/* Assume channel is already an IEEE number */
81812a08440Sreyk 		return (freq);
81912a08440Sreyk 	}
82012a08440Sreyk }
82112a08440Sreyk 
82212a08440Sreyk int
82312a08440Sreyk ieee80211_print(struct ieee80211_frame *wh, u_int len)
82412a08440Sreyk {
82512a08440Sreyk 	if (eflag)
82612a08440Sreyk 		if (ieee80211_hdr(wh))
82712a08440Sreyk 			return (1);
82812a08440Sreyk 
82912a08440Sreyk 	printf("802.11");
83012a08440Sreyk 
83112a08440Sreyk 	return (ieee80211_frame(wh, len));
83212a08440Sreyk }
83312a08440Sreyk 
83412a08440Sreyk void
83512a08440Sreyk ieee802_11_if_print(u_char *user, const struct pcap_pkthdr *h,
83612a08440Sreyk     const u_char *p)
83712a08440Sreyk {
83812a08440Sreyk 	struct ieee80211_frame *wh = (struct ieee80211_frame*)p;
83912a08440Sreyk 
840c79cf170Sreyk 	if (!ieee80211_encap)
84112a08440Sreyk 		ts_print(&h->ts);
84212a08440Sreyk 
84312a08440Sreyk 	packetp = p;
84412a08440Sreyk 	snapend = p + h->caplen;
84512a08440Sreyk 
846a0774ae9Smglocker 	if (ieee80211_print(wh, (u_int)h->len) != 0)
84712a08440Sreyk 		printf("[|802.11]");
84812a08440Sreyk 
849c79cf170Sreyk 	if (!ieee80211_encap) {
85012a08440Sreyk 		if (xflag)
851a0774ae9Smglocker 			default_print(p, (u_int)h->len);
85212a08440Sreyk 		putchar('\n');
85312a08440Sreyk 	}
854c79cf170Sreyk }
85512a08440Sreyk 
85612a08440Sreyk void
85712a08440Sreyk ieee802_11_radio_if_print(u_char *user, const struct pcap_pkthdr *h,
85812a08440Sreyk     const u_char *p)
85912a08440Sreyk {
86012a08440Sreyk 	struct ieee80211_radiotap_header *rh =
86112a08440Sreyk 	    (struct ieee80211_radiotap_header*)p;
86212a08440Sreyk 	struct ieee80211_frame *wh;
86312a08440Sreyk 	u_int8_t *t;
86412a08440Sreyk 	u_int32_t present;
86512a08440Sreyk 	u_int len, rh_len;
866cce1ba42Sclaudio 	u_int16_t tmp;
86712a08440Sreyk 
868c79cf170Sreyk 	if (!ieee80211_encap)
86912a08440Sreyk 		ts_print(&h->ts);
87012a08440Sreyk 
87112a08440Sreyk 	packetp = p;
87212a08440Sreyk 	snapend = p + h->caplen;
87312a08440Sreyk 
87412a08440Sreyk 	TCHECK(*rh);
87512a08440Sreyk 
876a0774ae9Smglocker 	len = h->len;
87712a08440Sreyk 	rh_len = letoh16(rh->it_len);
87812a08440Sreyk 	if (rh->it_version != 0) {
879c79cf170Sreyk 		printf("[?radiotap + 802.11 v:%u]", rh->it_version);
88012a08440Sreyk 		goto out;
88112a08440Sreyk 	}
88212a08440Sreyk 
88312a08440Sreyk 	wh = (struct ieee80211_frame *)(p + rh_len);
88412a08440Sreyk 	if (len <= rh_len || ieee80211_print(wh, len - rh_len))
88512a08440Sreyk 		printf("[|802.11]");
88612a08440Sreyk 
88712a08440Sreyk 	t = (u_int8_t*)p + sizeof(struct ieee80211_radiotap_header);
88812a08440Sreyk 
88912a08440Sreyk 	if ((present = letoh32(rh->it_present)) == 0)
89012a08440Sreyk 		goto out;
89112a08440Sreyk 
89212a08440Sreyk 	printf(", <radiotap v%u", rh->it_version);
89312a08440Sreyk 
89412a08440Sreyk #define RADIOTAP(_x)	\
89512a08440Sreyk 	(present & (1 << IEEE80211_RADIOTAP_##_x))
89612a08440Sreyk 
89712a08440Sreyk 	if (RADIOTAP(TSFT)) {
898cce1ba42Sclaudio 		u_int64_t tsf;
899cce1ba42Sclaudio 
90012a08440Sreyk 		TCHECK2(*t, 8);
901cce1ba42Sclaudio 		bcopy(t, &tsf, sizeof(u_int64_t));
90212a08440Sreyk 		if (vflag > 1)
903cce1ba42Sclaudio 			printf(", tsf %llu", letoh64(tsf));
90412a08440Sreyk 		t += 8;
90512a08440Sreyk 	}
90612a08440Sreyk 
90712a08440Sreyk 	if (RADIOTAP(FLAGS)) {
90812a08440Sreyk 		u_int8_t flags = *(u_int8_t*)t;
909026ca542Sderaadt 		TCHECK2(*t, 1);
910026ca542Sderaadt 
91112a08440Sreyk 		if (flags & IEEE80211_RADIOTAP_F_CFP)
91212a08440Sreyk 			printf(", CFP");
91312a08440Sreyk 		if (flags & IEEE80211_RADIOTAP_F_SHORTPRE)
91412a08440Sreyk 			printf(", SHORTPRE");
91512a08440Sreyk 		if (flags & IEEE80211_RADIOTAP_F_WEP)
91612a08440Sreyk 			printf(", WEP");
91712a08440Sreyk 		if (flags & IEEE80211_RADIOTAP_F_FRAG)
91812a08440Sreyk 			printf(", FRAG");
91912a08440Sreyk 		t += 1;
92012a08440Sreyk 	}
92112a08440Sreyk 
92212a08440Sreyk 	if (RADIOTAP(RATE)) {
92312a08440Sreyk 		TCHECK2(*t, 1);
92412a08440Sreyk 		if (vflag)
92512a08440Sreyk 			printf(", %uMbit/s", (*(u_int8_t*)t) / 2);
92612a08440Sreyk 		t += 1;
92712a08440Sreyk 	}
92812a08440Sreyk 
92912a08440Sreyk 	if (RADIOTAP(CHANNEL)) {
93012a08440Sreyk 		u_int16_t freq, flags;
93112a08440Sreyk 		TCHECK2(*t, 2);
93212a08440Sreyk 
933cce1ba42Sclaudio 		bcopy(t, &freq, sizeof(u_int16_t));
934cce1ba42Sclaudio 		freq = letoh16(freq);
93512a08440Sreyk 		t += 2;
93612a08440Sreyk 		TCHECK2(*t, 2);
937cce1ba42Sclaudio 		bcopy(t, &flags, sizeof(u_int16_t));
938cce1ba42Sclaudio 		flags = letoh16(flags);
93912a08440Sreyk 		t += 2;
94012a08440Sreyk 
94112a08440Sreyk 		printf(", chan %u", ieee80211_any2ieee(freq, flags));
94212a08440Sreyk 
94312a08440Sreyk 		if (flags & IEEE80211_CHAN_DYN &&
94412a08440Sreyk 		    flags & IEEE80211_CHAN_2GHZ)
94512a08440Sreyk 			printf(", 11g");
94612a08440Sreyk 		else if (flags & IEEE80211_CHAN_CCK &&
94712a08440Sreyk 		    flags & IEEE80211_CHAN_2GHZ)
94812a08440Sreyk 			printf(", 11b");
94912a08440Sreyk 		else if (flags & IEEE80211_CHAN_OFDM &&
95012a08440Sreyk 		    flags & IEEE80211_CHAN_2GHZ)
95112a08440Sreyk 			printf(", 11G");
95212a08440Sreyk 		else if (flags & IEEE80211_CHAN_OFDM &&
95312a08440Sreyk 		    flags & IEEE80211_CHAN_5GHZ)
95412a08440Sreyk 			printf(", 11a");
95512a08440Sreyk 
95612a08440Sreyk 		if (flags & IEEE80211_CHAN_TURBO)
95712a08440Sreyk 			printf(", TURBO");
95812a08440Sreyk 		if (flags & IEEE80211_CHAN_XR)
95912a08440Sreyk 			printf(", XR");
96012a08440Sreyk 	}
96112a08440Sreyk 
96212a08440Sreyk 	if (RADIOTAP(FHSS)) {
96312a08440Sreyk 		TCHECK2(*t, 2);
96412a08440Sreyk 		printf(", fhss %u/%u", *(u_int8_t*)t, *(u_int8_t*)t + 1);
96512a08440Sreyk 		t += 2;
96612a08440Sreyk 	}
96712a08440Sreyk 
96812a08440Sreyk 	if (RADIOTAP(DBM_ANTSIGNAL)) {
96912a08440Sreyk 		TCHECK(*t);
97012a08440Sreyk 		printf(", sig %ddBm", *(int8_t*)t);
97112a08440Sreyk 		t += 1;
97212a08440Sreyk 	}
97312a08440Sreyk 
97412a08440Sreyk 	if (RADIOTAP(DBM_ANTNOISE)) {
97512a08440Sreyk 		TCHECK(*t);
97612a08440Sreyk 		printf(", noise %ddBm", *(int8_t*)t);
97712a08440Sreyk 		t += 1;
97812a08440Sreyk 	}
97912a08440Sreyk 
98012a08440Sreyk 	if (RADIOTAP(LOCK_QUALITY)) {
98112a08440Sreyk 		TCHECK2(*t, 2);
982cce1ba42Sclaudio 		if (vflag) {
983cce1ba42Sclaudio 			bcopy(t, &tmp, sizeof(u_int16_t));
984cce1ba42Sclaudio 			printf(", quality %u", letoh16(tmp));
985cce1ba42Sclaudio 		}
98612a08440Sreyk 		t += 2;
98712a08440Sreyk 	}
98812a08440Sreyk 
98912a08440Sreyk 	if (RADIOTAP(TX_ATTENUATION)) {
99012a08440Sreyk 		TCHECK2(*t, 2);
991cce1ba42Sclaudio 		if (vflag) {
992cce1ba42Sclaudio 			bcopy(t, &tmp, sizeof(u_int16_t));
993cce1ba42Sclaudio 			printf(", txatt %u", letoh16(tmp));
994cce1ba42Sclaudio 		}
99512a08440Sreyk 		t += 2;
99612a08440Sreyk 	}
99712a08440Sreyk 
99812a08440Sreyk 	if (RADIOTAP(DB_TX_ATTENUATION)) {
99912a08440Sreyk 		TCHECK2(*t, 2);
1000cce1ba42Sclaudio 		if (vflag) {
1001cce1ba42Sclaudio 			bcopy(t, &tmp, sizeof(u_int16_t));
1002cce1ba42Sclaudio 			printf(", txatt %udB", letoh16(tmp));
1003cce1ba42Sclaudio 		}
100412a08440Sreyk 		t += 2;
100512a08440Sreyk 	}
100612a08440Sreyk 
100712a08440Sreyk 	if (RADIOTAP(DBM_TX_POWER)) {
100812a08440Sreyk 		TCHECK(*t);
100912a08440Sreyk 		printf(", txpower %ddBm", *(int8_t*)t);
101012a08440Sreyk 		t += 1;
101112a08440Sreyk 	}
101212a08440Sreyk 
101312a08440Sreyk 	if (RADIOTAP(ANTENNA)) {
101412a08440Sreyk 		TCHECK(*t);
101512a08440Sreyk 		if (vflag)
101612a08440Sreyk 			printf(", antenna %u", *(u_int8_t*)t);
101712a08440Sreyk 		t += 1;
101812a08440Sreyk 	}
101912a08440Sreyk 
102012a08440Sreyk 	if (RADIOTAP(DB_ANTSIGNAL)) {
102112a08440Sreyk 		TCHECK(*t);
102212a08440Sreyk 		printf(", signal %udB", *(u_int8_t*)t);
102312a08440Sreyk 		t += 1;
102412a08440Sreyk 	}
102512a08440Sreyk 
102612a08440Sreyk 	if (RADIOTAP(DB_ANTNOISE)) {
102712a08440Sreyk 		TCHECK(*t);
102812a08440Sreyk 		printf(", noise %udB", *(u_int8_t*)t);
102912a08440Sreyk 		t += 1;
103012a08440Sreyk 	}
103112a08440Sreyk 
103212a08440Sreyk 	if (RADIOTAP(FCS)) {
103312a08440Sreyk 		TCHECK2(*t, 4);
1034cce1ba42Sclaudio 		if (vflag) {
1035cce1ba42Sclaudio 			u_int32_t fcs;
1036cce1ba42Sclaudio 			bcopy(t, &fcs, sizeof(u_int32_t));
1037cce1ba42Sclaudio 			printf(", fcs %08x", letoh32(fcs));
1038cce1ba42Sclaudio 		}
103912a08440Sreyk 		t += 4;
104012a08440Sreyk 	}
104112a08440Sreyk 
10426fc94ee3Sreyk 	if (RADIOTAP(RSSI)) {
10436fc94ee3Sreyk 		u_int8_t rssi, max_rssi;
10446fc94ee3Sreyk 		TCHECK(*t);
10456fc94ee3Sreyk 		rssi = *(u_int8_t*)t;
10466fc94ee3Sreyk 		t += 1;
10476fc94ee3Sreyk 		TCHECK(*t);
10486fc94ee3Sreyk 		max_rssi = *(u_int8_t*)t;
10496fc94ee3Sreyk 		t += 1;
10506fc94ee3Sreyk 
10516fc94ee3Sreyk 		printf(", rssi %u/%u", rssi, max_rssi);
10526fc94ee3Sreyk 	}
10536fc94ee3Sreyk 
105412a08440Sreyk #undef RADIOTAP
105512a08440Sreyk 
105612a08440Sreyk 	putchar('>');
105712a08440Sreyk 	goto out;
105812a08440Sreyk 
105912a08440Sreyk  trunc:
106012a08440Sreyk 	/* Truncated frame */
106112a08440Sreyk 	printf("[|radiotap + 802.11]");
106212a08440Sreyk 
106312a08440Sreyk  out:
1064c79cf170Sreyk 	if (!ieee80211_encap) {
106512a08440Sreyk 		if (xflag)
1066a0774ae9Smglocker 			default_print(p, h->len);
106712a08440Sreyk 		putchar('\n');
106812a08440Sreyk 	}
1069c79cf170Sreyk }
107018786664Sclaudio 
107118786664Sclaudio void
107218786664Sclaudio ieee80211_reason(u_int16_t reason)
107318786664Sclaudio {
107418786664Sclaudio 	if (!vflag)
107518786664Sclaudio 		return;
107618786664Sclaudio 
107718786664Sclaudio 	switch (reason) {
107818786664Sclaudio 	case IEEE80211_REASON_UNSPECIFIED:
107918786664Sclaudio 		printf(", unspecified failure");
108018786664Sclaudio 		break;
108118786664Sclaudio 	case IEEE80211_REASON_AUTH_EXPIRE:
108218786664Sclaudio 		printf(", authentication expired");
108318786664Sclaudio 		break;
108418786664Sclaudio 	case IEEE80211_REASON_AUTH_LEAVE:
108518786664Sclaudio 		printf(", deauth - station left");
108618786664Sclaudio 		break;
108718786664Sclaudio 	case IEEE80211_REASON_ASSOC_EXPIRE:
108818786664Sclaudio 		printf(", association expired");
108918786664Sclaudio 		break;
109018786664Sclaudio 	case IEEE80211_REASON_ASSOC_TOOMANY:
109118786664Sclaudio 		printf(", too many associated stations");
109218786664Sclaudio 		break;
109318786664Sclaudio 	case IEEE80211_REASON_NOT_AUTHED:
109418786664Sclaudio 		printf(", not authenticated");
109518786664Sclaudio 		break;
109618786664Sclaudio 	case IEEE80211_REASON_NOT_ASSOCED:
109718786664Sclaudio 		printf(", not associated");
109818786664Sclaudio 		break;
109918786664Sclaudio 	case IEEE80211_REASON_ASSOC_LEAVE:
110018786664Sclaudio 		printf(", disassociated - station left");
110118786664Sclaudio 		break;
110218786664Sclaudio 	case IEEE80211_REASON_ASSOC_NOT_AUTHED:
110318786664Sclaudio 		printf(", association but not authenticated");
110418786664Sclaudio 		break;
110518786664Sclaudio 	case IEEE80211_REASON_RSN_REQUIRED:
110618786664Sclaudio 		printf(", rsn required");
110718786664Sclaudio 		break;
110818786664Sclaudio 	case IEEE80211_REASON_RSN_INCONSISTENT:
110918786664Sclaudio 		printf(", rsn inconsistent");
111018786664Sclaudio 		break;
111118786664Sclaudio 	case IEEE80211_REASON_IE_INVALID:
111218786664Sclaudio 		printf(", ie invalid");
111318786664Sclaudio 		break;
111418786664Sclaudio 	case IEEE80211_REASON_MIC_FAILURE:
111518786664Sclaudio 		printf(", mic failure");
111618786664Sclaudio 		break;
111718786664Sclaudio 	default:
111818786664Sclaudio 		printf(", unknown reason %u", reason);
111918786664Sclaudio 	}
112018786664Sclaudio }
1121