1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2009 Sam Leffler, Errno Consulting
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: releng/12.0/sys/net80211/ieee80211_radiotap.c 326272 2017-11-27 15:23:17Z pfg $");
30
31/*
32 * IEEE 802.11 radiotap support.
33 */
34#include "opt_wlan.h"
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/mbuf.h>
39#include <sys/malloc.h>
40#include <sys/endian.h>
41#include <sys/kernel.h>
42
43#include <sys/socket.h>
44
45#include <net/bpf.h>
46#include <net/if.h>
47#include <net/if_var.h>
48#include <net/if_media.h>
49#include <net/ethernet.h>
50
51#include <net80211/ieee80211_var.h>
52
53static int radiotap_offset(struct ieee80211_radiotap_header *, int, int);
54
55void
56ieee80211_radiotap_attach(struct ieee80211com *ic,
57	struct ieee80211_radiotap_header *th, int tlen, uint32_t tx_radiotap,
58	struct ieee80211_radiotap_header *rh, int rlen, uint32_t rx_radiotap)
59{
60	ieee80211_radiotap_attachv(ic, th, tlen, 0, tx_radiotap,
61	    rh, rlen, 0, rx_radiotap);
62}
63
64void
65ieee80211_radiotap_attachv(struct ieee80211com *ic,
66	struct ieee80211_radiotap_header *th,
67	int tlen, int n_tx_v, uint32_t tx_radiotap,
68	struct ieee80211_radiotap_header *rh,
69	int rlen, int n_rx_v, uint32_t rx_radiotap)
70{
71#define	B(_v)	(1<<(_v))
72	int off;
73
74	th->it_len = htole16(roundup2(tlen, sizeof(uint32_t)));
75	th->it_present = htole32(tx_radiotap);
76	ic->ic_th = th;
77	/* calculate offset to channel data */
78	off = -1;
79	if (tx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL))
80		off = radiotap_offset(th, n_tx_v, IEEE80211_RADIOTAP_CHANNEL);
81	else if (tx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL))
82		off = radiotap_offset(th, n_tx_v, IEEE80211_RADIOTAP_XCHANNEL);
83	if (off == -1) {
84		ic_printf(ic, "%s: no tx channel, radiotap 0x%x\n", __func__,
85		    tx_radiotap);
86		/* NB: we handle this case but data will have no chan spec */
87	} else
88		ic->ic_txchan = ((uint8_t *) th) + off;
89
90	rh->it_len = htole16(roundup2(rlen, sizeof(uint32_t)));
91	rh->it_present = htole32(rx_radiotap);
92	ic->ic_rh = rh;
93	/* calculate offset to channel data */
94	off = -1;
95	if (rx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL))
96		off = radiotap_offset(rh, n_rx_v, IEEE80211_RADIOTAP_CHANNEL);
97	else if (rx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL))
98		off = radiotap_offset(rh, n_rx_v, IEEE80211_RADIOTAP_XCHANNEL);
99	if (off == -1) {
100		ic_printf(ic, "%s: no rx channel, radiotap 0x%x\n", __func__,
101		    rx_radiotap);
102		/* NB: we handle this case but data will have no chan spec */
103	} else
104		ic->ic_rxchan = ((uint8_t *) rh) + off;
105#undef B
106}
107
108void
109ieee80211_radiotap_detach(struct ieee80211com *ic)
110{
111}
112
113void
114ieee80211_radiotap_vattach(struct ieee80211vap *vap)
115{
116	struct ieee80211com *ic = vap->iv_ic;
117	struct ieee80211_radiotap_header *th = ic->ic_th;
118
119	if (th != NULL && ic->ic_rh != NULL) {
120		/* radiotap DLT for raw 802.11 frames */
121		bpfattach2(vap->iv_ifp, DLT_IEEE802_11_RADIO,
122		    sizeof(struct ieee80211_frame) + le16toh(th->it_len),
123		    &vap->iv_rawbpf);
124	}
125}
126
127void
128ieee80211_radiotap_vdetach(struct ieee80211vap *vap)
129{
130	/* NB: bpfattach is called by ether_ifdetach and claims all taps */
131}
132
133static void
134set_channel(void *p, const struct ieee80211_channel *c)
135{
136	struct {
137		uint16_t	freq;
138		uint16_t	flags;
139	} *rc = p;
140
141	rc->freq = htole16(c->ic_freq);
142	rc->flags = htole16(c->ic_flags);
143}
144
145static void
146set_xchannel(void *p, const struct ieee80211_channel *c)
147{
148	struct {
149		uint32_t	flags;
150		uint16_t	freq;
151		uint8_t		ieee;
152		uint8_t		maxpow;
153	} *rc = p;
154
155	rc->flags = htole32(c->ic_flags);
156	rc->freq = htole16(c->ic_freq);
157	rc->ieee = c->ic_ieee;
158	rc->maxpow = c->ic_maxregpower;
159}
160
161/*
162 * Update radiotap state on channel change.
163 */
164void
165ieee80211_radiotap_chan_change(struct ieee80211com *ic)
166{
167	if (ic->ic_rxchan != NULL) {
168		struct ieee80211_radiotap_header *rh = ic->ic_rh;
169
170		if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL))
171			set_xchannel(ic->ic_rxchan, ic->ic_curchan);
172		else if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL))
173			set_channel(ic->ic_rxchan, ic->ic_curchan);
174	}
175	if (ic->ic_txchan != NULL) {
176		struct ieee80211_radiotap_header *th = ic->ic_th;
177
178		if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL))
179			set_xchannel(ic->ic_txchan, ic->ic_curchan);
180		else if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL))
181			set_channel(ic->ic_txchan, ic->ic_curchan);
182	}
183}
184
185/*
186 * Distribute radiotap data (+packet) to all monitor mode
187 * vaps with an active tap other than vap0.
188 */
189static void
190spam_vaps(struct ieee80211vap *vap0, struct mbuf *m,
191	struct ieee80211_radiotap_header *rh, int len)
192{
193	struct ieee80211com *ic = vap0->iv_ic;
194	struct ieee80211vap *vap;
195
196	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
197		if (vap != vap0 &&
198		    vap->iv_opmode == IEEE80211_M_MONITOR &&
199		    (vap->iv_flags_ext & IEEE80211_FEXT_BPF) &&
200		    vap->iv_state != IEEE80211_S_INIT)
201			bpf_mtap2(vap->iv_rawbpf, rh, len, m);
202	}
203}
204
205/*
206 * Dispatch radiotap data for transmitted packet.
207 */
208void
209ieee80211_radiotap_tx(struct ieee80211vap *vap0, struct mbuf *m)
210{
211	struct ieee80211com *ic = vap0->iv_ic;
212	struct ieee80211_radiotap_header *th = ic->ic_th;
213	int len;
214
215	KASSERT(th != NULL, ("no tx radiotap header"));
216	len = le16toh(th->it_len);
217
218	if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF)
219		bpf_mtap2(vap0->iv_rawbpf, th, len, m);
220	/*
221	 * Spam monitor mode vaps.
222	 */
223	if (ic->ic_montaps != 0)
224		spam_vaps(vap0, m, th, len);
225}
226
227/*
228 * Dispatch radiotap data for received packet.
229 */
230void
231ieee80211_radiotap_rx(struct ieee80211vap *vap0, struct mbuf *m)
232{
233	struct ieee80211com *ic = vap0->iv_ic;
234	struct ieee80211_radiotap_header *rh = ic->ic_rh;
235	int len;
236
237	KASSERT(rh != NULL, ("no rx radiotap header"));
238	len = le16toh(rh->it_len);
239
240	if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF)
241		bpf_mtap2(vap0->iv_rawbpf, rh, len, m);
242	/*
243	 * Spam monitor mode vaps with unicast frames.  Multicast
244	 * frames are handled by passing through ieee80211_input_all
245	 * which distributes copies to the monitor mode vaps.
246	 */
247	if (ic->ic_montaps != 0 && (m->m_flags & M_BCAST) == 0)
248		spam_vaps(vap0, m, rh, len);
249}
250
251/*
252 * Dispatch radiotap data for a packet received outside the normal
253 * rx processing path; this is used, for example, to handle frames
254 * received with errors that would otherwise be dropped.
255 */
256void
257ieee80211_radiotap_rx_all(struct ieee80211com *ic, struct mbuf *m)
258{
259	struct ieee80211_radiotap_header *rh = ic->ic_rh;
260	int len = le16toh(rh->it_len);
261	struct ieee80211vap *vap;
262
263	/* XXX locking? */
264	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
265		if (ieee80211_radiotap_active_vap(vap) &&
266		    vap->iv_state != IEEE80211_S_INIT)
267			bpf_mtap2(vap->iv_rawbpf, rh, len, m);
268	}
269}
270
271/*
272 * Return the offset of the specified item in the radiotap
273 * header description.  If the item is not present or is not
274 * known -1 is returned.
275 */
276static int
277radiotap_offset(struct ieee80211_radiotap_header *rh,
278    int n_vendor_attributes, int item)
279{
280	static const struct {
281		size_t	align, width;
282	} items[] = {
283		[IEEE80211_RADIOTAP_TSFT] = {
284		    .align	= sizeof(uint64_t),
285		    .width	= sizeof(uint64_t),
286		},
287		[IEEE80211_RADIOTAP_FLAGS] = {
288		    .align	= sizeof(uint8_t),
289		    .width	= sizeof(uint8_t),
290		},
291		[IEEE80211_RADIOTAP_RATE] = {
292		    .align	= sizeof(uint8_t),
293		    .width	= sizeof(uint8_t),
294		},
295		[IEEE80211_RADIOTAP_CHANNEL] = {
296		    .align	= sizeof(uint16_t),
297		    .width	= 2*sizeof(uint16_t),
298		},
299		[IEEE80211_RADIOTAP_FHSS] = {
300		    .align	= sizeof(uint16_t),
301		    .width	= sizeof(uint16_t),
302		},
303		[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = {
304		    .align	= sizeof(uint8_t),
305		    .width	= sizeof(uint8_t),
306		},
307		[IEEE80211_RADIOTAP_DBM_ANTNOISE] = {
308		    .align	= sizeof(uint8_t),
309		    .width	= sizeof(uint8_t),
310		},
311		[IEEE80211_RADIOTAP_LOCK_QUALITY] = {
312		    .align	= sizeof(uint16_t),
313		    .width	= sizeof(uint16_t),
314		},
315		[IEEE80211_RADIOTAP_TX_ATTENUATION] = {
316		    .align	= sizeof(uint16_t),
317		    .width	= sizeof(uint16_t),
318		},
319		[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = {
320		    .align	= sizeof(uint16_t),
321		    .width	= sizeof(uint16_t),
322		},
323		[IEEE80211_RADIOTAP_DBM_TX_POWER] = {
324		    .align	= sizeof(uint8_t),
325		    .width	= sizeof(uint8_t),
326		},
327		[IEEE80211_RADIOTAP_ANTENNA] = {
328		    .align	= sizeof(uint8_t),
329		    .width	= sizeof(uint8_t),
330		},
331		[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = {
332		    .align	= sizeof(uint8_t),
333		    .width	= sizeof(uint8_t),
334		},
335		[IEEE80211_RADIOTAP_DB_ANTNOISE] = {
336		    .align	= sizeof(uint8_t),
337		    .width	= sizeof(uint8_t),
338		},
339		[IEEE80211_RADIOTAP_XCHANNEL] = {
340		    .align	= sizeof(uint32_t),
341		    .width	= 2*sizeof(uint32_t),
342		},
343		[IEEE80211_RADIOTAP_MCS] = {
344		    .align	= sizeof(uint8_t),
345		    .width	= 3*sizeof(uint8_t),
346		},
347	};
348	uint32_t present = le32toh(rh->it_present);
349	int off, i;
350
351	off = sizeof(struct ieee80211_radiotap_header);
352	off += n_vendor_attributes * (sizeof(uint32_t));
353
354	for (i = 0; i < IEEE80211_RADIOTAP_EXT; i++) {
355		if ((present & (1<<i)) == 0)
356			continue;
357		if (items[i].align == 0) {
358			/* NB: unidentified element, don't guess */
359			printf("%s: unknown item %d\n", __func__, i);
360			return -1;
361		}
362		off = roundup2(off, items[i].align);
363		if (i == item) {
364			if (off + items[i].width > le16toh(rh->it_len)) {
365				/* NB: item does not fit in header data */
366				printf("%s: item %d not in header data, "
367				    "off %d width %zu len %d\n", __func__, i,
368				    off, items[i].width, le16toh(rh->it_len));
369				return -1;
370			}
371			return off;
372		}
373		off += items[i].width;
374	}
375	return -1;
376}
377