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_var.h>
46#include <net/if_media.h>
47#include <net/ethernet.h>
48
49#include <net80211/ieee80211_var.h>
50
51static int radiotap_offset(struct ieee80211_radiotap_header *, int, int);
52
53void
54ieee80211_radiotap_attach(struct ieee80211com *ic,
55	struct ieee80211_radiotap_header *th, int tlen, uint32_t tx_radiotap,
56	struct ieee80211_radiotap_header *rh, int rlen, uint32_t rx_radiotap)
57{
58	ieee80211_radiotap_attachv(ic, th, tlen, 0, tx_radiotap,
59	    rh, rlen, 0, rx_radiotap);
60}
61
62void
63ieee80211_radiotap_attachv(struct ieee80211com *ic,
64	struct ieee80211_radiotap_header *th,
65	int tlen, int n_tx_v, uint32_t tx_radiotap,
66	struct ieee80211_radiotap_header *rh,
67	int rlen, int n_rx_v, uint32_t rx_radiotap)
68{
69#define	B(_v)	(1<<(_v))
70	int off;
71
72	th->it_len = htole16(roundup2(tlen, sizeof(uint32_t)));
73	th->it_present = htole32(tx_radiotap);
74	ic->ic_th = th;
75	/* calculate offset to channel data */
76	off = -1;
77	if (tx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL))
78		off = radiotap_offset(th, n_tx_v, IEEE80211_RADIOTAP_CHANNEL);
79	else if (tx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL))
80		off = radiotap_offset(th, n_tx_v, IEEE80211_RADIOTAP_XCHANNEL);
81	if (off == -1) {
82		ic_printf(ic, "%s: no tx channel, radiotap 0x%x\n", __func__,
83		    tx_radiotap);
84		/* NB: we handle this case but data will have no chan spec */
85	} else
86		ic->ic_txchan = ((uint8_t *) th) + off;
87
88	rh->it_len = htole16(roundup2(rlen, sizeof(uint32_t)));
89	rh->it_present = htole32(rx_radiotap);
90	ic->ic_rh = rh;
91	/* calculate offset to channel data */
92	off = -1;
93	if (rx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL))
94		off = radiotap_offset(rh, n_rx_v, IEEE80211_RADIOTAP_CHANNEL);
95	else if (rx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL))
96		off = radiotap_offset(rh, n_rx_v, IEEE80211_RADIOTAP_XCHANNEL);
97	if (off == -1) {
98		ic_printf(ic, "%s: no rx channel, radiotap 0x%x\n", __func__,
99		    rx_radiotap);
100		/* NB: we handle this case but data will have no chan spec */
101	} else
102		ic->ic_rxchan = ((uint8_t *) rh) + off;
103#undef B
104}
105
106void
107ieee80211_radiotap_detach(struct ieee80211com *ic)
108{
109}
110
111void
112ieee80211_radiotap_vattach(struct ieee80211vap *vap)
113{
114	struct ieee80211com *ic = vap->iv_ic;
115	struct ieee80211_radiotap_header *th = ic->ic_th;
116
117	if (th != NULL && ic->ic_rh != NULL) {
118		/* radiotap DLT for raw 802.11 frames */
119		bpfattach2(vap->iv_ifp, DLT_IEEE802_11_RADIO,
120		    sizeof(struct ieee80211_frame) + le16toh(th->it_len),
121		    &vap->iv_rawbpf);
122	}
123}
124
125void
126ieee80211_radiotap_vdetach(struct ieee80211vap *vap)
127{
128	/* NB: bpfattach is called by ether_ifdetach and claims all taps */
129}
130
131static void
132set_channel(void *p, const struct ieee80211_channel *c)
133{
134	struct {
135		uint16_t	freq;
136		uint16_t	flags;
137	} *rc = p;
138
139	rc->freq = htole16(c->ic_freq);
140	rc->flags = htole16(c->ic_flags);
141}
142
143static void
144set_xchannel(void *p, const struct ieee80211_channel *c)
145{
146	struct {
147		uint32_t	flags;
148		uint16_t	freq;
149		uint8_t		ieee;
150		uint8_t		maxpow;
151	} *rc = p;
152
153	rc->flags = htole32(c->ic_flags);
154	rc->freq = htole16(c->ic_freq);
155	rc->ieee = c->ic_ieee;
156	rc->maxpow = c->ic_maxregpower;
157}
158
159/*
160 * Update radiotap state on channel change.
161 */
162void
163ieee80211_radiotap_chan_change(struct ieee80211com *ic)
164{
165	if (ic->ic_rxchan != NULL) {
166		struct ieee80211_radiotap_header *rh = ic->ic_rh;
167
168		if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL))
169			set_xchannel(ic->ic_rxchan, ic->ic_curchan);
170		else if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL))
171			set_channel(ic->ic_rxchan, ic->ic_curchan);
172	}
173	if (ic->ic_txchan != NULL) {
174		struct ieee80211_radiotap_header *th = ic->ic_th;
175
176		if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL))
177			set_xchannel(ic->ic_txchan, ic->ic_curchan);
178		else if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL))
179			set_channel(ic->ic_txchan, ic->ic_curchan);
180	}
181}
182
183/*
184 * Distribute radiotap data (+packet) to all monitor mode
185 * vaps with an active tap other than vap0.
186 */
187static void
188spam_vaps(struct ieee80211vap *vap0, struct mbuf *m,
189	struct ieee80211_radiotap_header *rh, int len)
190{
191	struct ieee80211com *ic = vap0->iv_ic;
192	struct ieee80211vap *vap;
193
194	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
195		if (vap != vap0 &&
196		    vap->iv_opmode == IEEE80211_M_MONITOR &&
197		    (vap->iv_flags_ext & IEEE80211_FEXT_BPF) &&
198		    vap->iv_state != IEEE80211_S_INIT)
199			bpf_mtap2(vap->iv_rawbpf, rh, len, m);
200	}
201}
202
203/*
204 * Dispatch radiotap data for transmitted packet.
205 */
206void
207ieee80211_radiotap_tx(struct ieee80211vap *vap0, struct mbuf *m)
208{
209	struct ieee80211com *ic = vap0->iv_ic;
210	struct ieee80211_radiotap_header *th = ic->ic_th;
211	int len;
212
213	KASSERT(th != NULL, ("no tx radiotap header"));
214	len = le16toh(th->it_len);
215
216	if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF)
217		bpf_mtap2(vap0->iv_rawbpf, th, len, m);
218	/*
219	 * Spam monitor mode vaps.
220	 */
221	if (ic->ic_montaps != 0)
222		spam_vaps(vap0, m, th, len);
223}
224
225/*
226 * Dispatch radiotap data for received packet.
227 */
228void
229ieee80211_radiotap_rx(struct ieee80211vap *vap0, struct mbuf *m)
230{
231	struct ieee80211com *ic = vap0->iv_ic;
232	struct ieee80211_radiotap_header *rh = ic->ic_rh;
233	int len;
234
235	KASSERT(rh != NULL, ("no rx radiotap header"));
236	len = le16toh(rh->it_len);
237
238	if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF)
239		bpf_mtap2(vap0->iv_rawbpf, rh, len, m);
240	/*
241	 * Spam monitor mode vaps with unicast frames.  Multicast
242	 * frames are handled by passing through ieee80211_input_all
243	 * which distributes copies to the monitor mode vaps.
244	 */
245	if (ic->ic_montaps != 0 && (m->m_flags & M_BCAST) == 0)
246		spam_vaps(vap0, m, rh, len);
247}
248
249/*
250 * Dispatch radiotap data for a packet received outside the normal
251 * rx processing path; this is used, for example, to handle frames
252 * received with errors that would otherwise be dropped.
253 */
254void
255ieee80211_radiotap_rx_all(struct ieee80211com *ic, struct mbuf *m)
256{
257	struct ieee80211_radiotap_header *rh = ic->ic_rh;
258	int len = le16toh(rh->it_len);
259	struct ieee80211vap *vap;
260
261	/* XXX locking? */
262	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
263		if (ieee80211_radiotap_active_vap(vap) &&
264		    vap->iv_state != IEEE80211_S_INIT)
265			bpf_mtap2(vap->iv_rawbpf, rh, len, m);
266	}
267}
268
269/*
270 * Return the offset of the specified item in the radiotap
271 * header description.  If the item is not present or is not
272 * known -1 is returned.
273 */
274static int
275radiotap_offset(struct ieee80211_radiotap_header *rh,
276    int n_vendor_attributes, int item)
277{
278	static const struct {
279		size_t	align, width;
280	} items[] = {
281		[IEEE80211_RADIOTAP_TSFT] = {
282		    .align	= sizeof(uint64_t),
283		    .width	= sizeof(uint64_t),
284		},
285		[IEEE80211_RADIOTAP_FLAGS] = {
286		    .align	= sizeof(uint8_t),
287		    .width	= sizeof(uint8_t),
288		},
289		[IEEE80211_RADIOTAP_RATE] = {
290		    .align	= sizeof(uint8_t),
291		    .width	= sizeof(uint8_t),
292		},
293		[IEEE80211_RADIOTAP_CHANNEL] = {
294		    .align	= sizeof(uint16_t),
295		    .width	= 2*sizeof(uint16_t),
296		},
297		[IEEE80211_RADIOTAP_FHSS] = {
298		    .align	= sizeof(uint16_t),
299		    .width	= sizeof(uint16_t),
300		},
301		[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = {
302		    .align	= sizeof(uint8_t),
303		    .width	= sizeof(uint8_t),
304		},
305		[IEEE80211_RADIOTAP_DBM_ANTNOISE] = {
306		    .align	= sizeof(uint8_t),
307		    .width	= sizeof(uint8_t),
308		},
309		[IEEE80211_RADIOTAP_LOCK_QUALITY] = {
310		    .align	= sizeof(uint16_t),
311		    .width	= sizeof(uint16_t),
312		},
313		[IEEE80211_RADIOTAP_TX_ATTENUATION] = {
314		    .align	= sizeof(uint16_t),
315		    .width	= sizeof(uint16_t),
316		},
317		[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = {
318		    .align	= sizeof(uint16_t),
319		    .width	= sizeof(uint16_t),
320		},
321		[IEEE80211_RADIOTAP_DBM_TX_POWER] = {
322		    .align	= sizeof(uint8_t),
323		    .width	= sizeof(uint8_t),
324		},
325		[IEEE80211_RADIOTAP_ANTENNA] = {
326		    .align	= sizeof(uint8_t),
327		    .width	= sizeof(uint8_t),
328		},
329		[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = {
330		    .align	= sizeof(uint8_t),
331		    .width	= sizeof(uint8_t),
332		},
333		[IEEE80211_RADIOTAP_DB_ANTNOISE] = {
334		    .align	= sizeof(uint8_t),
335		    .width	= sizeof(uint8_t),
336		},
337		[IEEE80211_RADIOTAP_XCHANNEL] = {
338		    .align	= sizeof(uint32_t),
339		    .width	= 2*sizeof(uint32_t),
340		},
341		[IEEE80211_RADIOTAP_MCS] = {
342		    .align	= sizeof(uint8_t),
343		    .width	= 3*sizeof(uint8_t),
344		},
345	};
346	uint32_t present = le32toh(rh->it_present);
347	int off, i;
348
349	off = sizeof(struct ieee80211_radiotap_header);
350	off += n_vendor_attributes * (sizeof(uint32_t));
351
352	for (i = 0; i < IEEE80211_RADIOTAP_EXT; i++) {
353		if ((present & (1<<i)) == 0)
354			continue;
355		if (items[i].align == 0) {
356			/* NB: unidentified element, don't guess */
357			printf("%s: unknown item %d\n", __func__, i);
358			return -1;
359		}
360		off = roundup2(off, items[i].align);
361		if (i == item) {
362			if (off + items[i].width > le16toh(rh->it_len)) {
363				/* NB: item does not fit in header data */
364				printf("%s: item %d not in header data, "
365				    "off %d width %zu len %d\n", __func__, i,
366				    off, items[i].width, le16toh(rh->it_len));
367				return -1;
368			}
369			return off;
370		}
371		off += items[i].width;
372	}
373	return -1;
374}
375