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