xref: /openbsd/usr.sbin/tcpdump/print-802_11.c (revision 9d5679ea)
1*9d5679eaSstsp /*	$OpenBSD: print-802_11.c,v 1.29 2016/02/01 10:09:44 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;
156b72abdefSreyk 	u_int datalen;
15793cbfc44Sstsp 	int data = !(wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_NODATA);
158*9d5679eaSstsp 	int hasqos = ((wh->i_fc[0] &
159*9d5679eaSstsp 	    (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) ==
160*9d5679eaSstsp 	    (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS));
161ab7e388eSclaudio 	u_char *esrc = NULL, *edst = NULL;
162b72abdefSreyk 
163*9d5679eaSstsp 	if (hasqos) {
164*9d5679eaSstsp 		struct ieee80211_qosframe *wq;
165*9d5679eaSstsp 
166*9d5679eaSstsp 		wq = (struct ieee80211_qosframe *) wh;
167*9d5679eaSstsp 		TCHECK(*wq);
168*9d5679eaSstsp 		t += sizeof(*wq);
169*9d5679eaSstsp 		datalen = len - sizeof(*wq);
170*9d5679eaSstsp 	} else {
171d49cb46eScanacar 		TCHECK(*wh);
172*9d5679eaSstsp 		t += sizeof(*wh);
173*9d5679eaSstsp 		datalen = len - sizeof(*wh);
174*9d5679eaSstsp 	}
175b72abdefSreyk 
176b72abdefSreyk 	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
177b72abdefSreyk 	case IEEE80211_FC1_DIR_TODS:
178ab7e388eSclaudio 		esrc = wh->i_addr2;
179ab7e388eSclaudio 		edst = wh->i_addr3;
180b72abdefSreyk 		break;
181b72abdefSreyk 	case IEEE80211_FC1_DIR_FROMDS:
182ab7e388eSclaudio 		esrc = wh->i_addr3;
183ab7e388eSclaudio 		edst = wh->i_addr1;
184b72abdefSreyk 		break;
185b72abdefSreyk 	case IEEE80211_FC1_DIR_NODS:
186ab7e388eSclaudio 		esrc = wh->i_addr2;
187ab7e388eSclaudio 		edst = wh->i_addr1;
188d49cb46eScanacar 		break;
189b72abdefSreyk 	case IEEE80211_FC1_DIR_DSTODS:
190*9d5679eaSstsp 		if (hasqos) {
191*9d5679eaSstsp 			struct ieee80211_qosframe_addr4 *w4;
192*9d5679eaSstsp 
193*9d5679eaSstsp 			w4 = (struct ieee80211_qosframe_addr4 *) wh;
194*9d5679eaSstsp 			TCHECK(*w4);
195*9d5679eaSstsp 			t = (u_int8_t *) (w4 + 1);
196*9d5679eaSstsp 			datalen = len - sizeof(*w4);
197*9d5679eaSstsp 			esrc = w4->i_addr4;
198*9d5679eaSstsp 			edst = w4->i_addr3;
199*9d5679eaSstsp 		} else {
200*9d5679eaSstsp 			struct ieee80211_frame_addr4 *w4;
201*9d5679eaSstsp 
202d49cb46eScanacar 			w4 = (struct ieee80211_frame_addr4 *) wh;
203d49cb46eScanacar 			TCHECK(*w4);
204d49cb46eScanacar 			t = (u_int8_t *) (w4 + 1);
205d49cb46eScanacar 			datalen = len - sizeof(*w4);
206ab7e388eSclaudio 			esrc = w4->i_addr4;
207ab7e388eSclaudio 			edst = w4->i_addr3;
208*9d5679eaSstsp 		}
209b72abdefSreyk 		break;
210b72abdefSreyk 	}
211b72abdefSreyk 
212ab7e388eSclaudio 	if (data && esrc)
213ab7e388eSclaudio 		llc_print(t, datalen, datalen, esrc, edst);
214ab7e388eSclaudio 	else if (eflag && esrc)
215ab7e388eSclaudio 		printf("%s > %s",
216ab7e388eSclaudio 		    etheraddr_string(esrc), etheraddr_string(edst));
217ab7e388eSclaudio 
218b72abdefSreyk 	return (0);
219b72abdefSreyk 
220b72abdefSreyk  trunc:
221b72abdefSreyk 	/* Truncated elements in frame */
222b72abdefSreyk 	return (1);
223b72abdefSreyk }
224b72abdefSreyk 
22512a08440Sreyk /* Caller checks len */
22612a08440Sreyk void
22712a08440Sreyk ieee80211_print_element(u_int8_t *data, u_int len)
22812a08440Sreyk {
22912a08440Sreyk 	u_int8_t *p;
2309cbdb746Sderaadt 	int i;
23112a08440Sreyk 
23212a08440Sreyk 	printf(" 0x");
2339cbdb746Sderaadt 	for (i = 0, p = data; i < len; i++, p++)
23412a08440Sreyk 		printf("%02x", *p);
23512a08440Sreyk }
23612a08440Sreyk 
23712a08440Sreyk /* Caller checks len */
23812a08440Sreyk void
23912a08440Sreyk ieee80211_print_essid(u_int8_t *essid, u_int len)
24012a08440Sreyk {
24112a08440Sreyk 	u_int8_t *p;
2429cbdb746Sderaadt 	int i;
24312a08440Sreyk 
24412a08440Sreyk 	if (len > IEEE80211_NWID_LEN)
24512a08440Sreyk 		len = IEEE80211_NWID_LEN;
24612a08440Sreyk 
24712a08440Sreyk 	/* determine printable or not */
24812a08440Sreyk 	for (i = 0, p = essid; i < len; i++, p++) {
24912a08440Sreyk 		if (*p < ' ' || *p > 0x7e)
25012a08440Sreyk 			break;
25112a08440Sreyk 	}
25212a08440Sreyk 	if (i == len) {
25312a08440Sreyk 		printf(" (");
25412a08440Sreyk 		for (i = 0, p = essid; i < len; i++, p++)
25512a08440Sreyk 			putchar(*p);
25612a08440Sreyk 		putchar(')');
2579cbdb746Sderaadt 	} else
25812a08440Sreyk 		ieee80211_print_element(essid, len);
25912a08440Sreyk }
26012a08440Sreyk 
2611e3bf20aSstsp /* Caller checks len */
2621e3bf20aSstsp void
26377734011Sstsp ieee80211_print_country(u_int8_t *data, u_int len)
26477734011Sstsp {
26577734011Sstsp 	u_int8_t first_chan, nchan, maxpower;
26677734011Sstsp 
26777734011Sstsp 	if (len < 6)
26877734011Sstsp 		return;
26977734011Sstsp 
27077734011Sstsp 	/* country string */
27177734011Sstsp 	printf((isprint(data[0]) ? " '%c" : " '\\%03o"), data[0]);
27277734011Sstsp 	printf((isprint(data[1]) ? "%c" : "\\%03o"), data[1]);
27377734011Sstsp 	printf((isprint(data[2]) ? "%c'" : "\\%03o'"), data[2]);
27477734011Sstsp 
27577734011Sstsp 	len -= 3;
27677734011Sstsp 	data += 3;
27777734011Sstsp 
27877734011Sstsp 	/* channels and corresponding TX power limits */
27977734011Sstsp 	while (len > 3)	{
28077734011Sstsp 		/* no pretty-printing for nonsensical zero values,
28177734011Sstsp 		 * nor for operating extension IDs (values >= 201) */
28277734011Sstsp 		if (data[0] == 0 || data[1] == 0 ||
28377734011Sstsp 		    data[0] >= 201 || data[1] >= 201) {
28477734011Sstsp 			printf(", %d %d %d", data[0], data[1], data[2]);
28577734011Sstsp 			continue;
28677734011Sstsp 		}
28777734011Sstsp 
28877734011Sstsp 		first_chan = data[0];
28977734011Sstsp 		nchan = data[1];
29077734011Sstsp 		maxpower = data[2];
29177734011Sstsp 
29277734011Sstsp 		printf(", channel%s %d", nchan == 1 ? "" : "s", first_chan);
29377734011Sstsp 		if (nchan > 1)
29477734011Sstsp 			printf("-%d", first_chan + nchan - 1);
29577734011Sstsp 		printf(" limit %ddB", maxpower);
29677734011Sstsp 
29777734011Sstsp 		len -= 3;
29877734011Sstsp 		data += 3;
29977734011Sstsp 	}
30077734011Sstsp }
30177734011Sstsp 
30277734011Sstsp /* Caller checks len */
30377734011Sstsp void
3041e3bf20aSstsp ieee80211_print_htcaps(u_int8_t *data, u_int len)
3051e3bf20aSstsp {
306537e7cd0Sstsp 	uint16_t htcaps, rxrate;
3071e3bf20aSstsp 	int smps, rxstbc;
308537e7cd0Sstsp 	uint8_t ampdu, txmcs;
309537e7cd0Sstsp 	int i;
310537e7cd0Sstsp 	uint8_t *rxmcs;
3111e3bf20aSstsp 
3121e3bf20aSstsp 	if (len < 2) {
3131e3bf20aSstsp 		ieee80211_print_element(data, len);
3141e3bf20aSstsp 		return;
3151e3bf20aSstsp 	}
3161e3bf20aSstsp 
3171e3bf20aSstsp 	htcaps = (data[0]) | (data[1] << 8);
3181e3bf20aSstsp 	printf("=<");
3191e3bf20aSstsp 
3201e3bf20aSstsp 	/* channel width */
3211e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_CBW20_40)
3221e3bf20aSstsp 		printf("20/40MHz");
3231e3bf20aSstsp 	else
3241e3bf20aSstsp 		printf("20MHz");
3251e3bf20aSstsp 
3261e3bf20aSstsp 	/* LDPC coding */
3271e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_LDPC)
3281e3bf20aSstsp 		printf(",LDPC");
3291e3bf20aSstsp 
3301e3bf20aSstsp 	/* spatial multiplexing power save mode */
3311e3bf20aSstsp 	smps = (htcaps & IEEE80211_HTCAP_SMPS_MASK)
3321e3bf20aSstsp 	    >> IEEE80211_HTCAP_SMPS_SHIFT;
3331e3bf20aSstsp 	if (smps == 0)
3341e3bf20aSstsp 		printf(",SMPS static");
3351e3bf20aSstsp 	else if (smps == 1)
3361e3bf20aSstsp 		printf(",SMPS dynamic");
3371e3bf20aSstsp 
3381e3bf20aSstsp 	/* 11n greenfield mode */
3391e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_GF)
3401e3bf20aSstsp 		printf(",greenfield");
3411e3bf20aSstsp 
3421e3bf20aSstsp 	/* short guard interval */
3431e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_SGI20)
3441e3bf20aSstsp 		printf(",SGI@20MHz");
3451e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_SGI40)
3461e3bf20aSstsp 		printf(",SGI@40MHz");
3471e3bf20aSstsp 
3481e3bf20aSstsp 	/* space-time block coding */
3491e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_TXSTBC)
3501e3bf20aSstsp 		printf(",TXSTBC");
3511e3bf20aSstsp 	rxstbc = (htcaps & IEEE80211_HTCAP_RXSTBC_MASK)
3521e3bf20aSstsp 	    >> IEEE80211_HTCAP_RXSTBC_SHIFT;
3531e3bf20aSstsp 	if (rxstbc > 0 && rxstbc < 4)
3541e3bf20aSstsp 		printf(",RXSTBC %d stream", rxstbc);
3551e3bf20aSstsp 
3561e3bf20aSstsp 	/* delayed block-ack */
3571e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_DELAYEDBA)
3581e3bf20aSstsp 		printf(",delayed BA");
3591e3bf20aSstsp 
3601e3bf20aSstsp 	/* max A-MSDU length */
3611e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_AMSDU7935)
3621e3bf20aSstsp 		printf(",A-MSDU 7935");
3631e3bf20aSstsp 	else
3641e3bf20aSstsp 		printf(",A-MSDU 3839");
3651e3bf20aSstsp 
3661e3bf20aSstsp 	/* DSSS/CCK in 40MHz mode */
3671e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_DSSSCCK40)
3681e3bf20aSstsp 		printf(",DSSS/CCK@40MHz");
3691e3bf20aSstsp 
3701e3bf20aSstsp 	/* 40MHz intolerant */
3711e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_40INTOLERANT)
3721e3bf20aSstsp 		printf(",40MHz intolerant");
3731e3bf20aSstsp 
3741e3bf20aSstsp 	/* L-SIG TXOP protection */
3751e3bf20aSstsp 	if (htcaps & IEEE80211_HTCAP_LSIGTXOPPROT)
3761e3bf20aSstsp 		printf(",L-SIG TXOP prot");
3771e3bf20aSstsp 
378537e7cd0Sstsp 	if (len < 3) {
379537e7cd0Sstsp 		printf(">");
380537e7cd0Sstsp 		return;
381537e7cd0Sstsp 	}
382537e7cd0Sstsp 
383537e7cd0Sstsp 	/* A-MPDU parameters. */
384537e7cd0Sstsp 	ampdu = data[2];
385537e7cd0Sstsp 
386537e7cd0Sstsp 	/* A-MPDU length exponent */
387537e7cd0Sstsp 	if ((ampdu & IEEE80211_AMPDU_PARAM_LE) >= 0 &&
388537e7cd0Sstsp 	    (ampdu & IEEE80211_AMPDU_PARAM_LE) <= 3)
389537e7cd0Sstsp 		printf(",A-MPDU max %d",
3900b19ae84Sstsp 		    (1 << (13 + (ampdu & IEEE80211_AMPDU_PARAM_LE))) - 1);
391537e7cd0Sstsp 
392537e7cd0Sstsp 	/* A-MPDU start spacing */
393537e7cd0Sstsp 	if (ampdu & IEEE80211_AMPDU_PARAM_SS) {
394537e7cd0Sstsp 		float ss;
395537e7cd0Sstsp 
396537e7cd0Sstsp 		switch ((ampdu & IEEE80211_AMPDU_PARAM_SS) >> 2) {
397537e7cd0Sstsp 		case 1:
398537e7cd0Sstsp 			ss = 0.25;
399537e7cd0Sstsp 			break;
400537e7cd0Sstsp 		case 2:
401537e7cd0Sstsp 			ss = 0.5;
402537e7cd0Sstsp 			break;
403537e7cd0Sstsp 		case 3:
404537e7cd0Sstsp 			ss = 1;
405537e7cd0Sstsp 			break;
406537e7cd0Sstsp 		case 4:
407537e7cd0Sstsp 			ss = 2;
408537e7cd0Sstsp 			break;
409537e7cd0Sstsp 		case 5:
410537e7cd0Sstsp 			ss = 4;
411537e7cd0Sstsp 			break;
412537e7cd0Sstsp 		case 6:
413537e7cd0Sstsp 			ss = 8;
414537e7cd0Sstsp 			break;
415537e7cd0Sstsp 		case 7:
416537e7cd0Sstsp 			ss = 16;
417537e7cd0Sstsp 			break;
418537e7cd0Sstsp 		default:
419537e7cd0Sstsp 			ss = 0;
420537e7cd0Sstsp 			break;
421537e7cd0Sstsp 		}
422537e7cd0Sstsp 		if (ss != 0)
423537e7cd0Sstsp 			printf(",A-MPDU spacing %.2fus", ss);
424537e7cd0Sstsp 	}
425537e7cd0Sstsp 
426537e7cd0Sstsp 	if (len < 21) {
427537e7cd0Sstsp 		printf(">");
428537e7cd0Sstsp 		return;
429537e7cd0Sstsp 	}
430537e7cd0Sstsp 
431537e7cd0Sstsp 	/* Supported MCS set. */
432537e7cd0Sstsp 	printf(",RxMCS 0x");
433537e7cd0Sstsp 	rxmcs = &data[3];
434537e7cd0Sstsp 	for (i = 0; i < 10; i++)
435537e7cd0Sstsp 		printf("%02x", rxmcs[i]);
436537e7cd0Sstsp 
437537e7cd0Sstsp 	/* Max MCS Rx rate (a value of 0 means "not specified"). */
438537e7cd0Sstsp 	rxrate = ((data[13] | (data[14]) << 8) & IEEE80211_MCS_RX_RATE_HIGH);
439537e7cd0Sstsp 	if (rxrate)
440537e7cd0Sstsp 		printf(",RxMaxrate %huMb/s", rxrate);
441537e7cd0Sstsp 
442537e7cd0Sstsp 	/* Tx MCS Set */
443537e7cd0Sstsp 	txmcs = data[15];
444537e7cd0Sstsp 	if (txmcs & IEEE80211_TX_MCS_SET_DEFINED) {
445537e7cd0Sstsp 		if (txmcs & IEEE80211_TX_RX_MCS_NOT_EQUAL) {
446537e7cd0Sstsp 			/* Number of spatial Tx streams. */
447537e7cd0Sstsp 			printf(",%d Tx streams",
448537e7cd0Sstsp 			     1 + ((txmcs & IEEE80211_TX_SPATIAL_STREAMS) >> 2));
449537e7cd0Sstsp 			/* Transmit unequal modulation supported. */
450537e7cd0Sstsp 			if (txmcs & IEEE80211_TX_UNEQUAL_MODULATION)
451537e7cd0Sstsp 				printf(",UEQM");
452537e7cd0Sstsp 		}
453537e7cd0Sstsp 	}
454537e7cd0Sstsp 
4551e3bf20aSstsp 	printf(">");
4561e3bf20aSstsp }
4571e3bf20aSstsp 
45836fa064cSstsp /* Caller checks len */
45936fa064cSstsp void
46036fa064cSstsp ieee80211_print_htop(u_int8_t *data, u_int len)
46136fa064cSstsp {
46236fa064cSstsp 	u_int8_t primary_chan;
46336fa064cSstsp 	u_int8_t htopinfo[5];
46436fa064cSstsp 	u_int8_t basic_mcs[16];
46536fa064cSstsp 	int sco, prot, i;
46636fa064cSstsp 
46736fa064cSstsp 	if (len < sizeof(primary_chan) + sizeof(htopinfo) + sizeof(basic_mcs)) {
46836fa064cSstsp 		ieee80211_print_element(data, len);
46936fa064cSstsp 		return;
47036fa064cSstsp 	}
47136fa064cSstsp 
47236fa064cSstsp 	htopinfo[0] = data[1];
47336fa064cSstsp 
47436fa064cSstsp 	printf("=<");
47536fa064cSstsp 
47636fa064cSstsp 	/* primary channel and secondary channel offset */
47736fa064cSstsp 	primary_chan = data[0];
47836fa064cSstsp 	sco = ((htopinfo[0] & IEEE80211_HTOP0_SCO_MASK)
47936fa064cSstsp 	    >> IEEE80211_HTOP0_SCO_SHIFT);
480b144e175Sstsp 	if (sco == 0) /* no secondary channel */
48136fa064cSstsp 		printf("20MHz chan %d", primary_chan);
482b144e175Sstsp 	else if (sco == 1) { /* secondary channel above */
483b144e175Sstsp 		if (primary_chan >= 1 && primary_chan <= 13) /* 2GHz */
484b144e175Sstsp 			printf("40MHz chan %d:%d", primary_chan,
485b144e175Sstsp 			    primary_chan + 1);
486b144e175Sstsp 		else if (primary_chan >= 34) /* 5GHz */
487b144e175Sstsp 			printf("40MHz chan %d:%d", primary_chan,
488b144e175Sstsp 			    primary_chan + 4);
48936fa064cSstsp 		else
490b144e175Sstsp 			printf("[invalid 40MHz chan %d+]", primary_chan);
491b144e175Sstsp 	} else if (sco == 3) { /* secondary channel below */
492b144e175Sstsp 		if (primary_chan >= 2 && primary_chan <= 14) /* 2GHz */
493b144e175Sstsp 			printf("40MHz chan %d:%d", primary_chan,
494b144e175Sstsp 			    primary_chan - 1);
495b144e175Sstsp 		else if (primary_chan >= 40) /* 5GHz */
496b144e175Sstsp 			printf("40MHz chan %d:%d", primary_chan,
497b144e175Sstsp 			    primary_chan - 4);
498b144e175Sstsp 		else
499b144e175Sstsp 			printf("[invalid 40MHz chan %d-]", primary_chan);
500b144e175Sstsp 	} else
50136fa064cSstsp 		printf("chan %d [invalid secondary channel offset %d]",
50236fa064cSstsp 		    primary_chan, sco);
50336fa064cSstsp 
50436fa064cSstsp 	/* STA channel width */
50536fa064cSstsp 	if ((htopinfo[0] & IEEE80211_HTOP0_CHW) == 0)
50636fa064cSstsp 		printf(",STA chanw 20MHz");
50736fa064cSstsp 
50836fa064cSstsp 	/* reduced interframe space (RIFS) permitted */
50936fa064cSstsp 	if (htopinfo[0] & IEEE80211_HTOP0_RIFS)
51036fa064cSstsp 		printf(",RIFS");
51136fa064cSstsp 
51236fa064cSstsp 	htopinfo[1] = data[2];
51336fa064cSstsp 
51436fa064cSstsp 	/* protection requirements for HT transmissions */
51536fa064cSstsp 	prot = ((htopinfo[1] & IEEE80211_HTOP1_PROT_MASK)
51636fa064cSstsp 	    >> IEEE80211_HTOP1_PROT_SHIFT);
51736fa064cSstsp 	if (prot == 1)
51836fa064cSstsp 		printf(",protect non-member");
51936fa064cSstsp 	else if (prot == 2)
52036fa064cSstsp 		printf(",protect 20MHz");
52136fa064cSstsp 	else if (prot == 3)
52236fa064cSstsp 		printf(",protect non-HT");
52336fa064cSstsp 
52436fa064cSstsp 	/* non-greenfield STA present */
52536fa064cSstsp 	if (htopinfo[1] & IEEE80211_HTOP1_NONGF_STA)
52636fa064cSstsp 		printf(",non-greenfield STA");
52736fa064cSstsp 
52836fa064cSstsp 	/* non-HT STA present */
52936fa064cSstsp 	if (htopinfo[1] & IEEE80211_HTOP1_OBSS_NONHT_STA)
53036fa064cSstsp 		printf(",non-HT STA");
53136fa064cSstsp 
53236fa064cSstsp 	htopinfo[3] = data[4];
53336fa064cSstsp 
53436fa064cSstsp 	/* dual-beacon */
53536fa064cSstsp 	if (htopinfo[3] & IEEE80211_HTOP2_DUALBEACON)
53636fa064cSstsp 		printf(",dualbeacon");
53736fa064cSstsp 
53836fa064cSstsp 	/* dual CTS protection */
53936fa064cSstsp 	if (htopinfo[3] & IEEE80211_HTOP2_DUALCTSPROT)
54036fa064cSstsp 		printf(",dualctsprot");
54136fa064cSstsp 
54236fa064cSstsp 	htopinfo[4] = data[5];
54336fa064cSstsp 
54436fa064cSstsp 	/* space-time block coding (STBC) beacon */
54558089030Sstsp 	if ((htopinfo[4] << 8) & IEEE80211_HTOP2_STBCBEACON)
54636fa064cSstsp 		printf(",STBC beacon");
54736fa064cSstsp 
54836fa064cSstsp 	/* L-SIG (non-HT signal field) TX opportunity (TXOP) protection */
54936fa064cSstsp 	if ((htopinfo[4] << 8) & IEEE80211_HTOP2_LSIGTXOP)
55036fa064cSstsp 		printf(",lsigtxprot");
55136fa064cSstsp 
55236fa064cSstsp 	/* phased-coexistence operation (PCO) active */
55336fa064cSstsp 	if ((htopinfo[4] << 8) & IEEE80211_HTOP2_PCOACTIVE) {
55436fa064cSstsp 		/* PCO phase */
55536fa064cSstsp 		if ((htopinfo[4] << 8) & IEEE80211_HTOP2_PCOPHASE40)
55636fa064cSstsp 			printf(",pco40MHz");
55736fa064cSstsp 		else
55836fa064cSstsp 			printf(",pco20MHz");
55936fa064cSstsp 	}
56036fa064cSstsp 
56136fa064cSstsp 	/* basic MCS set */
56236fa064cSstsp 	memcpy(basic_mcs, &data[6], sizeof(basic_mcs));
56336fa064cSstsp 	printf(",basic MCS set 0x");
56436fa064cSstsp 	for (i = 0; i < sizeof(basic_mcs) / sizeof(basic_mcs[0]); i++)
56536fa064cSstsp 			printf("%x", basic_mcs[i]);
56636fa064cSstsp 
56736fa064cSstsp 	printf(">");
56836fa064cSstsp }
56936fa064cSstsp 
57012a08440Sreyk int
57112a08440Sreyk ieee80211_elements(struct ieee80211_frame *wh, u_int flen)
57212a08440Sreyk {
57312a08440Sreyk 	u_int8_t *buf, *frm;
574cce1ba42Sclaudio 	u_int64_t tstamp;
575cce1ba42Sclaudio 	u_int16_t bintval, capinfo;
57612a08440Sreyk 	int i;
57712a08440Sreyk 
57812a08440Sreyk 	buf = (u_int8_t *)wh;
57912a08440Sreyk 	frm = (u_int8_t *)&wh[1];
58012a08440Sreyk 
5810a05c5bfSreyk 	TCHECK2(*frm, 8);
582cce1ba42Sclaudio 	bcopy(frm, &tstamp, sizeof(u_int64_t));
58312a08440Sreyk 	frm += 8;
58412a08440Sreyk 
58512a08440Sreyk 	if (vflag > 1)
586cce1ba42Sclaudio 		printf(", timestamp %llu", letoh64(tstamp));
58712a08440Sreyk 
5880a05c5bfSreyk 	TCHECK2(*frm, 2);
589cce1ba42Sclaudio 	bcopy(frm, &bintval, sizeof(u_int16_t));
59012a08440Sreyk 	frm += 2;
59112a08440Sreyk 
59212a08440Sreyk 	if (vflag > 1)
593cce1ba42Sclaudio 		printf(", interval %u", letoh16(bintval));
59412a08440Sreyk 
5950a05c5bfSreyk 	TCHECK2(*frm, 2);
596cce1ba42Sclaudio 	bcopy(frm, &capinfo, sizeof(u_int16_t));
59712a08440Sreyk 	frm += 2;
59812a08440Sreyk 
59912a08440Sreyk 	if (vflag)
600cce1ba42Sclaudio 		printb(", caps", letoh16(capinfo),
60112a08440Sreyk 		    IEEE80211_CAPINFO_BITS);
60212a08440Sreyk 
60312a08440Sreyk 	while (TTEST2(*frm, 2)) {
60412a08440Sreyk 		u_int len = frm[1];
60512a08440Sreyk 		u_int8_t *data = frm + 2;
60612a08440Sreyk 
60712a08440Sreyk 		if (!TTEST2(*data, len))
60812a08440Sreyk 			break;
60912a08440Sreyk 
61012a08440Sreyk #define ELEM_CHECK(l)	if (len != l) break
61112a08440Sreyk 
61212a08440Sreyk 		switch (*frm) {
61312a08440Sreyk 		case IEEE80211_ELEMID_SSID:
61412a08440Sreyk 			printf(", ssid");
61512a08440Sreyk 			ieee80211_print_essid(data, len);
61612a08440Sreyk 			break;
61712a08440Sreyk 		case IEEE80211_ELEMID_RATES:
61812a08440Sreyk 			printf(", rates");
61912a08440Sreyk 			if (!vflag)
62012a08440Sreyk 				break;
62112a08440Sreyk 			for (i = len; i > 0; i--, data++)
62212a08440Sreyk 				printf(" %uM",
62312a08440Sreyk 				    (data[0] & IEEE80211_RATE_VAL) / 2);
62412a08440Sreyk 			break;
62512a08440Sreyk 		case IEEE80211_ELEMID_FHPARMS:
62612a08440Sreyk 			ELEM_CHECK(5);
62712a08440Sreyk 			printf(", fh (dwell %u, chan %u, index %u)",
62812a08440Sreyk 			    (data[1] << 8) | data[0],
62912a08440Sreyk 			    (data[2] - 1) * 80 + data[3],	/* FH_CHAN */
63012a08440Sreyk 			    data[4]);
63112a08440Sreyk 			break;
63212a08440Sreyk 		case IEEE80211_ELEMID_DSPARMS:
63312a08440Sreyk 			ELEM_CHECK(1);
63412a08440Sreyk 			printf(", ds");
63512a08440Sreyk 			if (vflag)
63612a08440Sreyk 				printf(" (chan %u)", data[0]);
63712a08440Sreyk 			break;
63812a08440Sreyk 		case IEEE80211_ELEMID_CFPARMS:
63912a08440Sreyk 			printf(", cf");
64012a08440Sreyk 			if (vflag)
64112a08440Sreyk 				ieee80211_print_element(data, len);
64212a08440Sreyk 			break;
64312a08440Sreyk 		case IEEE80211_ELEMID_TIM:
64412a08440Sreyk 			printf(", tim");
64512a08440Sreyk 			if (vflag)
64612a08440Sreyk 				ieee80211_print_element(data, len);
64712a08440Sreyk 			break;
64812a08440Sreyk 		case IEEE80211_ELEMID_IBSSPARMS:
64912a08440Sreyk 			printf(", ibss");
65012a08440Sreyk 			if (vflag)
65112a08440Sreyk 				ieee80211_print_element(data, len);
65212a08440Sreyk 			break;
65312a08440Sreyk 		case IEEE80211_ELEMID_COUNTRY:
65412a08440Sreyk 			printf(", country");
65577734011Sstsp 			ieee80211_print_country(data, len);
65612a08440Sreyk 			break;
65712a08440Sreyk 		case IEEE80211_ELEMID_CHALLENGE:
65812a08440Sreyk 			printf(", challenge");
65912a08440Sreyk 			if (vflag)
66012a08440Sreyk 				ieee80211_print_element(data, len);
66112a08440Sreyk 			break;
662738acb58Ssthen 		case IEEE80211_ELEMID_CSA:
663958b7e45Ssthen 			ELEM_CHECK(3);
664738acb58Ssthen 			printf(", csa (chan %u count %u%s)", data[1], data[2],
665738acb58Ssthen 			    (data[0] == 1) ? " noTX" : "");
666738acb58Ssthen 			break;
66712a08440Sreyk 		case IEEE80211_ELEMID_ERP:
66812a08440Sreyk 			printf(", erp");
66912a08440Sreyk 			if (vflag)
67012a08440Sreyk 				ieee80211_print_element(data, len);
67112a08440Sreyk 			break;
67212a08440Sreyk 		case IEEE80211_ELEMID_RSN:
67312a08440Sreyk 			printf(", rsn");
67412a08440Sreyk 			if (vflag)
67512a08440Sreyk 				ieee80211_print_element(data, len);
67612a08440Sreyk 			break;
67712a08440Sreyk 		case IEEE80211_ELEMID_XRATES:
67812a08440Sreyk 			printf(", xrates");
67912a08440Sreyk 			if (!vflag)
68012a08440Sreyk 				break;
68112a08440Sreyk 			for (i = len; i > 0; i--, data++)
68212a08440Sreyk 				printf(" %uM",
68312a08440Sreyk 				    (data[0] & IEEE80211_RATE_VAL) / 2);
68412a08440Sreyk 			break;
6858138faddSstsp 		case IEEE80211_ELEMID_TPC_REPORT:
6868138faddSstsp 			printf(", tpcreport");
68712a08440Sreyk 			if (vflag)
68812a08440Sreyk 				ieee80211_print_element(data, len);
68912a08440Sreyk 			break;
6908138faddSstsp 		case IEEE80211_ELEMID_TPC_REQUEST:
6918138faddSstsp 			printf(", tpcrequest");
69212a08440Sreyk 			if (vflag)
69312a08440Sreyk 				ieee80211_print_element(data, len);
69412a08440Sreyk 			break;
6951e3bf20aSstsp 		case IEEE80211_ELEMID_HTCAPS:
6961e3bf20aSstsp 			printf(", htcaps");
6971e3bf20aSstsp 			if (vflag)
6981e3bf20aSstsp 				ieee80211_print_htcaps(data, len);
6991e3bf20aSstsp 			break;
70036fa064cSstsp 		case IEEE80211_ELEMID_HTOP:
70136fa064cSstsp 			printf(", htop");
70236fa064cSstsp 			if (vflag)
70336fa064cSstsp 				ieee80211_print_htop(data, len);
70436fa064cSstsp 			break;
70577734011Sstsp 		case IEEE80211_ELEMID_POWER_CONSTRAINT:
70677734011Sstsp 			ELEM_CHECK(1);
70777734011Sstsp 			printf(", power constraint %udB", data[0]);
70877734011Sstsp 			break;
7098f0a8537Sstsp 		case IEEE80211_ELEMID_QBSS_LOAD:
7108f0a8537Sstsp 			ELEM_CHECK(5);
7118f0a8537Sstsp 			printf(", %u stations, %d%% utilization, "
7128f0a8537Sstsp 			    "admission capacity %uus/s",
7138f0a8537Sstsp 			    (data[0] | data[1] << 8),
7148f0a8537Sstsp 			    (data[2] * 100) / 255,
7158f0a8537Sstsp 			    (data[3] | data[4] << 8) / 32);
7168f0a8537Sstsp 			break;
71712a08440Sreyk 		case IEEE80211_ELEMID_VENDOR:
71812a08440Sreyk 			printf(", vendor");
71912a08440Sreyk 			if (vflag)
72012a08440Sreyk 				ieee80211_print_element(data, len);
72112a08440Sreyk 			break;
72212a08440Sreyk 		default:
72312a08440Sreyk 			printf(", %u:%u", (u_int) *frm, len);
72412a08440Sreyk 			if (vflag)
72512a08440Sreyk 				ieee80211_print_element(data, len);
72612a08440Sreyk 			break;
72712a08440Sreyk 		}
72812a08440Sreyk 		frm += len + 2;
72912a08440Sreyk 
73012a08440Sreyk 		if (frm >= snapend)
73112a08440Sreyk 			break;
73212a08440Sreyk 	}
73312a08440Sreyk 
73412a08440Sreyk #undef ELEM_CHECK
73512a08440Sreyk 
73612a08440Sreyk 	return (0);
73712a08440Sreyk 
73812a08440Sreyk  trunc:
73912a08440Sreyk 	/* Truncated elements in frame */
74012a08440Sreyk 	return (1);
74112a08440Sreyk }
74212a08440Sreyk 
74312a08440Sreyk int
74412a08440Sreyk ieee80211_frame(struct ieee80211_frame *wh, u_int len)
74512a08440Sreyk {
74612a08440Sreyk 	u_int8_t subtype, type, *frm;
74712a08440Sreyk 
74812a08440Sreyk 	TCARR(wh->i_fc);
74912a08440Sreyk 
75012a08440Sreyk 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
75112a08440Sreyk 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
75212a08440Sreyk 
75312a08440Sreyk 	frm = (u_int8_t *)&wh[1];
75412a08440Sreyk 
755ab7e388eSclaudio 	if (vflag)
756ab7e388eSclaudio 		printb(" flags", wh->i_fc[1], IEEE80211_FC1_BITS);
757ab7e388eSclaudio 
75812a08440Sreyk 	switch (type) {
75912a08440Sreyk 	case IEEE80211_FC0_TYPE_DATA:
760ab7e388eSclaudio 		printf(": %s: ", ieee80211_data_subtype_name[
761ab7e388eSclaudio 		    subtype >> IEEE80211_FC0_SUBTYPE_SHIFT]);
762b72abdefSreyk 		ieee80211_data(wh, len);
76312a08440Sreyk 		break;
76412a08440Sreyk 	case IEEE80211_FC0_TYPE_MGT:
76512a08440Sreyk 		printf(": %s", ieee80211_mgt_subtype_name[
76612a08440Sreyk 		    subtype >> IEEE80211_FC0_SUBTYPE_SHIFT]);
76712a08440Sreyk 		switch (subtype) {
76812a08440Sreyk 		case IEEE80211_FC0_SUBTYPE_BEACON:
76912a08440Sreyk 		case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
77012a08440Sreyk 			if (ieee80211_elements(wh, len) != 0)
77112a08440Sreyk 				goto trunc;
77212a08440Sreyk 			break;
77312a08440Sreyk 		case IEEE80211_FC0_SUBTYPE_AUTH:
77412a08440Sreyk 			TCHECK2(*frm, 2);		/* Auth Algorithm */
77512a08440Sreyk 			switch (IEEE80211_AUTH_ALGORITHM(frm)) {
77612a08440Sreyk 			case IEEE80211_AUTH_ALG_OPEN:
77712a08440Sreyk 				TCHECK2(*frm, 4);	/* Auth Transaction */
77812a08440Sreyk 				switch (IEEE80211_AUTH_TRANSACTION(frm)) {
77912a08440Sreyk 				case IEEE80211_AUTH_OPEN_REQUEST:
78012a08440Sreyk 					printf(" request");
78112a08440Sreyk 					break;
78212a08440Sreyk 				case IEEE80211_AUTH_OPEN_RESPONSE:
78312a08440Sreyk 					printf(" response");
78412a08440Sreyk 					break;
78512a08440Sreyk 				}
78612a08440Sreyk 				break;
78712a08440Sreyk 			case IEEE80211_AUTH_ALG_SHARED:
78812a08440Sreyk 				TCHECK2(*frm, 4);	/* Auth Transaction */
78912a08440Sreyk 				switch (IEEE80211_AUTH_TRANSACTION(frm)) {
79012a08440Sreyk 				case IEEE80211_AUTH_SHARED_REQUEST:
79112a08440Sreyk 					printf(" request");
79212a08440Sreyk 					break;
79312a08440Sreyk 				case IEEE80211_AUTH_SHARED_CHALLENGE:
79412a08440Sreyk 					printf(" challenge");
79512a08440Sreyk 					break;
79612a08440Sreyk 				case IEEE80211_AUTH_SHARED_RESPONSE:
79712a08440Sreyk 					printf(" response");
79812a08440Sreyk 					break;
79912a08440Sreyk 				case IEEE80211_AUTH_SHARED_PASS:
80012a08440Sreyk 					printf(" pass");
80112a08440Sreyk 					break;
80212a08440Sreyk 				}
80312a08440Sreyk 				break;
80412a08440Sreyk 			case IEEE80211_AUTH_ALG_LEAP:
80512a08440Sreyk 				printf(" (leap)");
80612a08440Sreyk 				break;
80712a08440Sreyk 			}
80812a08440Sreyk 			break;
80918786664Sclaudio 		case IEEE80211_FC0_SUBTYPE_DEAUTH:
81018786664Sclaudio 		case IEEE80211_FC0_SUBTYPE_DISASSOC:
81118786664Sclaudio 			TCHECK2(*frm, 2);		/* Reason Code */
81218786664Sclaudio 			ieee80211_reason(frm[0] | (frm[1] << 8));
81318786664Sclaudio 			break;
81412a08440Sreyk 		}
81512a08440Sreyk 		break;
81612a08440Sreyk 	default:
81712a08440Sreyk 		printf(": type#%d", type);
81812a08440Sreyk 		break;
81912a08440Sreyk 	}
82012a08440Sreyk 
82112a08440Sreyk 	return (0);
82212a08440Sreyk 
82312a08440Sreyk  trunc:
82412a08440Sreyk 	/* Truncated 802.11 frame */
82512a08440Sreyk 	return (1);
82612a08440Sreyk }
82712a08440Sreyk 
82812a08440Sreyk u_int
82912a08440Sreyk ieee80211_any2ieee(u_int freq, u_int flags)
83012a08440Sreyk {
83112a08440Sreyk 	if (flags & IEEE80211_CHAN_2GHZ) {
83212a08440Sreyk 		if (freq == 2484)
83312a08440Sreyk 			return 14;
83412a08440Sreyk 		if (freq < 2484)
83512a08440Sreyk 			return (freq - 2407) / 5;
83612a08440Sreyk 		else
83712a08440Sreyk 			return 15 + ((freq - 2512) / 20);
83812a08440Sreyk 	} else if (flags & IEEE80211_CHAN_5GHZ) {
83912a08440Sreyk 		return (freq - 5000) / 5;
84012a08440Sreyk 	} else {
84112a08440Sreyk 		/* Assume channel is already an IEEE number */
84212a08440Sreyk 		return (freq);
84312a08440Sreyk 	}
84412a08440Sreyk }
84512a08440Sreyk 
84612a08440Sreyk int
84712a08440Sreyk ieee80211_print(struct ieee80211_frame *wh, u_int len)
84812a08440Sreyk {
84912a08440Sreyk 	if (eflag)
85012a08440Sreyk 		if (ieee80211_hdr(wh))
85112a08440Sreyk 			return (1);
85212a08440Sreyk 
85312a08440Sreyk 	printf("802.11");
85412a08440Sreyk 
85512a08440Sreyk 	return (ieee80211_frame(wh, len));
85612a08440Sreyk }
85712a08440Sreyk 
85812a08440Sreyk void
85912a08440Sreyk ieee802_11_if_print(u_char *user, const struct pcap_pkthdr *h,
86012a08440Sreyk     const u_char *p)
86112a08440Sreyk {
86212a08440Sreyk 	struct ieee80211_frame *wh = (struct ieee80211_frame*)p;
86312a08440Sreyk 
864c79cf170Sreyk 	if (!ieee80211_encap)
86512a08440Sreyk 		ts_print(&h->ts);
86612a08440Sreyk 
86712a08440Sreyk 	packetp = p;
86812a08440Sreyk 	snapend = p + h->caplen;
86912a08440Sreyk 
870a0774ae9Smglocker 	if (ieee80211_print(wh, (u_int)h->len) != 0)
87112a08440Sreyk 		printf("[|802.11]");
87212a08440Sreyk 
873c79cf170Sreyk 	if (!ieee80211_encap) {
87412a08440Sreyk 		if (xflag)
875a0774ae9Smglocker 			default_print(p, (u_int)h->len);
87612a08440Sreyk 		putchar('\n');
87712a08440Sreyk 	}
878c79cf170Sreyk }
87912a08440Sreyk 
88012a08440Sreyk void
88112a08440Sreyk ieee802_11_radio_if_print(u_char *user, const struct pcap_pkthdr *h,
88212a08440Sreyk     const u_char *p)
88312a08440Sreyk {
88412a08440Sreyk 	struct ieee80211_radiotap_header *rh =
88512a08440Sreyk 	    (struct ieee80211_radiotap_header*)p;
88612a08440Sreyk 	struct ieee80211_frame *wh;
88712a08440Sreyk 	u_int8_t *t;
88812a08440Sreyk 	u_int32_t present;
88912a08440Sreyk 	u_int len, rh_len;
890cce1ba42Sclaudio 	u_int16_t tmp;
89112a08440Sreyk 
892c79cf170Sreyk 	if (!ieee80211_encap)
89312a08440Sreyk 		ts_print(&h->ts);
89412a08440Sreyk 
89512a08440Sreyk 	packetp = p;
89612a08440Sreyk 	snapend = p + h->caplen;
89712a08440Sreyk 
89812a08440Sreyk 	TCHECK(*rh);
89912a08440Sreyk 
900a0774ae9Smglocker 	len = h->len;
90112a08440Sreyk 	rh_len = letoh16(rh->it_len);
90212a08440Sreyk 	if (rh->it_version != 0) {
903c79cf170Sreyk 		printf("[?radiotap + 802.11 v:%u]", rh->it_version);
90412a08440Sreyk 		goto out;
90512a08440Sreyk 	}
90612a08440Sreyk 
90712a08440Sreyk 	wh = (struct ieee80211_frame *)(p + rh_len);
90812a08440Sreyk 	if (len <= rh_len || ieee80211_print(wh, len - rh_len))
90912a08440Sreyk 		printf("[|802.11]");
91012a08440Sreyk 
91112a08440Sreyk 	t = (u_int8_t*)p + sizeof(struct ieee80211_radiotap_header);
91212a08440Sreyk 
91312a08440Sreyk 	if ((present = letoh32(rh->it_present)) == 0)
91412a08440Sreyk 		goto out;
91512a08440Sreyk 
91612a08440Sreyk 	printf(", <radiotap v%u", rh->it_version);
91712a08440Sreyk 
91812a08440Sreyk #define RADIOTAP(_x)	\
91912a08440Sreyk 	(present & (1 << IEEE80211_RADIOTAP_##_x))
92012a08440Sreyk 
92112a08440Sreyk 	if (RADIOTAP(TSFT)) {
922cce1ba42Sclaudio 		u_int64_t tsf;
923cce1ba42Sclaudio 
92412a08440Sreyk 		TCHECK2(*t, 8);
925cce1ba42Sclaudio 		bcopy(t, &tsf, sizeof(u_int64_t));
92612a08440Sreyk 		if (vflag > 1)
927cce1ba42Sclaudio 			printf(", tsf %llu", letoh64(tsf));
92812a08440Sreyk 		t += 8;
92912a08440Sreyk 	}
93012a08440Sreyk 
93112a08440Sreyk 	if (RADIOTAP(FLAGS)) {
93212a08440Sreyk 		u_int8_t flags = *(u_int8_t*)t;
933026ca542Sderaadt 		TCHECK2(*t, 1);
934026ca542Sderaadt 
93512a08440Sreyk 		if (flags & IEEE80211_RADIOTAP_F_CFP)
93612a08440Sreyk 			printf(", CFP");
93712a08440Sreyk 		if (flags & IEEE80211_RADIOTAP_F_SHORTPRE)
93812a08440Sreyk 			printf(", SHORTPRE");
93912a08440Sreyk 		if (flags & IEEE80211_RADIOTAP_F_WEP)
94012a08440Sreyk 			printf(", WEP");
94112a08440Sreyk 		if (flags & IEEE80211_RADIOTAP_F_FRAG)
94212a08440Sreyk 			printf(", FRAG");
94312a08440Sreyk 		t += 1;
94412a08440Sreyk 	}
94512a08440Sreyk 
94612a08440Sreyk 	if (RADIOTAP(RATE)) {
94712a08440Sreyk 		TCHECK2(*t, 1);
94812a08440Sreyk 		if (vflag)
94912a08440Sreyk 			printf(", %uMbit/s", (*(u_int8_t*)t) / 2);
95012a08440Sreyk 		t += 1;
95112a08440Sreyk 	}
95212a08440Sreyk 
95312a08440Sreyk 	if (RADIOTAP(CHANNEL)) {
95412a08440Sreyk 		u_int16_t freq, flags;
95512a08440Sreyk 		TCHECK2(*t, 2);
95612a08440Sreyk 
957cce1ba42Sclaudio 		bcopy(t, &freq, sizeof(u_int16_t));
958cce1ba42Sclaudio 		freq = letoh16(freq);
95912a08440Sreyk 		t += 2;
96012a08440Sreyk 		TCHECK2(*t, 2);
961cce1ba42Sclaudio 		bcopy(t, &flags, sizeof(u_int16_t));
962cce1ba42Sclaudio 		flags = letoh16(flags);
96312a08440Sreyk 		t += 2;
96412a08440Sreyk 
96512a08440Sreyk 		printf(", chan %u", ieee80211_any2ieee(freq, flags));
96612a08440Sreyk 
96712a08440Sreyk 		if (flags & IEEE80211_CHAN_DYN &&
96812a08440Sreyk 		    flags & IEEE80211_CHAN_2GHZ)
96912a08440Sreyk 			printf(", 11g");
97012a08440Sreyk 		else if (flags & IEEE80211_CHAN_CCK &&
97112a08440Sreyk 		    flags & IEEE80211_CHAN_2GHZ)
97212a08440Sreyk 			printf(", 11b");
97312a08440Sreyk 		else if (flags & IEEE80211_CHAN_OFDM &&
97412a08440Sreyk 		    flags & IEEE80211_CHAN_2GHZ)
97512a08440Sreyk 			printf(", 11G");
97612a08440Sreyk 		else if (flags & IEEE80211_CHAN_OFDM &&
97712a08440Sreyk 		    flags & IEEE80211_CHAN_5GHZ)
97812a08440Sreyk 			printf(", 11a");
97912a08440Sreyk 
98012a08440Sreyk 		if (flags & IEEE80211_CHAN_XR)
98112a08440Sreyk 			printf(", XR");
98212a08440Sreyk 	}
98312a08440Sreyk 
98412a08440Sreyk 	if (RADIOTAP(FHSS)) {
98512a08440Sreyk 		TCHECK2(*t, 2);
98612a08440Sreyk 		printf(", fhss %u/%u", *(u_int8_t*)t, *(u_int8_t*)t + 1);
98712a08440Sreyk 		t += 2;
98812a08440Sreyk 	}
98912a08440Sreyk 
99012a08440Sreyk 	if (RADIOTAP(DBM_ANTSIGNAL)) {
99112a08440Sreyk 		TCHECK(*t);
99212a08440Sreyk 		printf(", sig %ddBm", *(int8_t*)t);
99312a08440Sreyk 		t += 1;
99412a08440Sreyk 	}
99512a08440Sreyk 
99612a08440Sreyk 	if (RADIOTAP(DBM_ANTNOISE)) {
99712a08440Sreyk 		TCHECK(*t);
99812a08440Sreyk 		printf(", noise %ddBm", *(int8_t*)t);
99912a08440Sreyk 		t += 1;
100012a08440Sreyk 	}
100112a08440Sreyk 
100212a08440Sreyk 	if (RADIOTAP(LOCK_QUALITY)) {
100312a08440Sreyk 		TCHECK2(*t, 2);
1004cce1ba42Sclaudio 		if (vflag) {
1005cce1ba42Sclaudio 			bcopy(t, &tmp, sizeof(u_int16_t));
1006cce1ba42Sclaudio 			printf(", quality %u", letoh16(tmp));
1007cce1ba42Sclaudio 		}
100812a08440Sreyk 		t += 2;
100912a08440Sreyk 	}
101012a08440Sreyk 
101112a08440Sreyk 	if (RADIOTAP(TX_ATTENUATION)) {
101212a08440Sreyk 		TCHECK2(*t, 2);
1013cce1ba42Sclaudio 		if (vflag) {
1014cce1ba42Sclaudio 			bcopy(t, &tmp, sizeof(u_int16_t));
1015cce1ba42Sclaudio 			printf(", txatt %u", letoh16(tmp));
1016cce1ba42Sclaudio 		}
101712a08440Sreyk 		t += 2;
101812a08440Sreyk 	}
101912a08440Sreyk 
102012a08440Sreyk 	if (RADIOTAP(DB_TX_ATTENUATION)) {
102112a08440Sreyk 		TCHECK2(*t, 2);
1022cce1ba42Sclaudio 		if (vflag) {
1023cce1ba42Sclaudio 			bcopy(t, &tmp, sizeof(u_int16_t));
1024cce1ba42Sclaudio 			printf(", txatt %udB", letoh16(tmp));
1025cce1ba42Sclaudio 		}
102612a08440Sreyk 		t += 2;
102712a08440Sreyk 	}
102812a08440Sreyk 
102912a08440Sreyk 	if (RADIOTAP(DBM_TX_POWER)) {
103012a08440Sreyk 		TCHECK(*t);
103112a08440Sreyk 		printf(", txpower %ddBm", *(int8_t*)t);
103212a08440Sreyk 		t += 1;
103312a08440Sreyk 	}
103412a08440Sreyk 
103512a08440Sreyk 	if (RADIOTAP(ANTENNA)) {
103612a08440Sreyk 		TCHECK(*t);
103712a08440Sreyk 		if (vflag)
103812a08440Sreyk 			printf(", antenna %u", *(u_int8_t*)t);
103912a08440Sreyk 		t += 1;
104012a08440Sreyk 	}
104112a08440Sreyk 
104212a08440Sreyk 	if (RADIOTAP(DB_ANTSIGNAL)) {
104312a08440Sreyk 		TCHECK(*t);
104412a08440Sreyk 		printf(", signal %udB", *(u_int8_t*)t);
104512a08440Sreyk 		t += 1;
104612a08440Sreyk 	}
104712a08440Sreyk 
104812a08440Sreyk 	if (RADIOTAP(DB_ANTNOISE)) {
104912a08440Sreyk 		TCHECK(*t);
105012a08440Sreyk 		printf(", noise %udB", *(u_int8_t*)t);
105112a08440Sreyk 		t += 1;
105212a08440Sreyk 	}
105312a08440Sreyk 
105412a08440Sreyk 	if (RADIOTAP(FCS)) {
105512a08440Sreyk 		TCHECK2(*t, 4);
1056cce1ba42Sclaudio 		if (vflag) {
1057cce1ba42Sclaudio 			u_int32_t fcs;
1058cce1ba42Sclaudio 			bcopy(t, &fcs, sizeof(u_int32_t));
1059cce1ba42Sclaudio 			printf(", fcs %08x", letoh32(fcs));
1060cce1ba42Sclaudio 		}
106112a08440Sreyk 		t += 4;
106212a08440Sreyk 	}
106312a08440Sreyk 
10646fc94ee3Sreyk 	if (RADIOTAP(RSSI)) {
10656fc94ee3Sreyk 		u_int8_t rssi, max_rssi;
10666fc94ee3Sreyk 		TCHECK(*t);
10676fc94ee3Sreyk 		rssi = *(u_int8_t*)t;
10686fc94ee3Sreyk 		t += 1;
10696fc94ee3Sreyk 		TCHECK(*t);
10706fc94ee3Sreyk 		max_rssi = *(u_int8_t*)t;
10716fc94ee3Sreyk 		t += 1;
10726fc94ee3Sreyk 
10736fc94ee3Sreyk 		printf(", rssi %u/%u", rssi, max_rssi);
10746fc94ee3Sreyk 	}
10756fc94ee3Sreyk 
107612a08440Sreyk #undef RADIOTAP
107712a08440Sreyk 
107812a08440Sreyk 	putchar('>');
107912a08440Sreyk 	goto out;
108012a08440Sreyk 
108112a08440Sreyk  trunc:
108212a08440Sreyk 	/* Truncated frame */
108312a08440Sreyk 	printf("[|radiotap + 802.11]");
108412a08440Sreyk 
108512a08440Sreyk  out:
1086c79cf170Sreyk 	if (!ieee80211_encap) {
108712a08440Sreyk 		if (xflag)
1088a0774ae9Smglocker 			default_print(p, h->len);
108912a08440Sreyk 		putchar('\n');
109012a08440Sreyk 	}
1091c79cf170Sreyk }
109218786664Sclaudio 
109318786664Sclaudio void
109418786664Sclaudio ieee80211_reason(u_int16_t reason)
109518786664Sclaudio {
109618786664Sclaudio 	if (!vflag)
109718786664Sclaudio 		return;
109818786664Sclaudio 
109918786664Sclaudio 	switch (reason) {
110018786664Sclaudio 	case IEEE80211_REASON_UNSPECIFIED:
110118786664Sclaudio 		printf(", unspecified failure");
110218786664Sclaudio 		break;
110318786664Sclaudio 	case IEEE80211_REASON_AUTH_EXPIRE:
110418786664Sclaudio 		printf(", authentication expired");
110518786664Sclaudio 		break;
110618786664Sclaudio 	case IEEE80211_REASON_AUTH_LEAVE:
110718786664Sclaudio 		printf(", deauth - station left");
110818786664Sclaudio 		break;
110918786664Sclaudio 	case IEEE80211_REASON_ASSOC_EXPIRE:
111018786664Sclaudio 		printf(", association expired");
111118786664Sclaudio 		break;
111218786664Sclaudio 	case IEEE80211_REASON_ASSOC_TOOMANY:
111318786664Sclaudio 		printf(", too many associated stations");
111418786664Sclaudio 		break;
111518786664Sclaudio 	case IEEE80211_REASON_NOT_AUTHED:
111618786664Sclaudio 		printf(", not authenticated");
111718786664Sclaudio 		break;
111818786664Sclaudio 	case IEEE80211_REASON_NOT_ASSOCED:
111918786664Sclaudio 		printf(", not associated");
112018786664Sclaudio 		break;
112118786664Sclaudio 	case IEEE80211_REASON_ASSOC_LEAVE:
112218786664Sclaudio 		printf(", disassociated - station left");
112318786664Sclaudio 		break;
112418786664Sclaudio 	case IEEE80211_REASON_ASSOC_NOT_AUTHED:
112518786664Sclaudio 		printf(", association but not authenticated");
112618786664Sclaudio 		break;
112718786664Sclaudio 	case IEEE80211_REASON_RSN_REQUIRED:
112818786664Sclaudio 		printf(", rsn required");
112918786664Sclaudio 		break;
113018786664Sclaudio 	case IEEE80211_REASON_RSN_INCONSISTENT:
113118786664Sclaudio 		printf(", rsn inconsistent");
113218786664Sclaudio 		break;
113318786664Sclaudio 	case IEEE80211_REASON_IE_INVALID:
113418786664Sclaudio 		printf(", ie invalid");
113518786664Sclaudio 		break;
113618786664Sclaudio 	case IEEE80211_REASON_MIC_FAILURE:
113718786664Sclaudio 		printf(", mic failure");
113818786664Sclaudio 		break;
113918786664Sclaudio 	default:
114018786664Sclaudio 		printf(", unknown reason %u", reason);
114118786664Sclaudio 	}
114218786664Sclaudio }
1143