indent: Avoid using values of pointers that refer to deallocated space.
[freebsd-src.git] / sys / net80211 / ieee80211_radiotap.c
blobeb218dd4ffba7676a0c50cfc867614d645ed69ea
1 /*-
2 * Copyright (c) 2009 Sam Leffler, Errno Consulting
3 * All rights reserved.
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.
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.
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
30 * IEEE 802.11 radiotap support.
32 #include "opt_wlan.h"
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>
41 #include <sys/socket.h>
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>
49 #include <net80211/ieee80211_var.h>
51 static int radiotap_offset(struct ieee80211_radiotap_header *, int, int);
53 void
54 ieee80211_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)
58 ieee80211_radiotap_attachv(ic, th, tlen, 0, tx_radiotap,
59 rh, rlen, 0, rx_radiotap);
62 void
63 ieee80211_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)
69 #define B(_v) (1<<(_v))
70 int off;
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;
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
106 void
107 ieee80211_radiotap_detach(struct ieee80211com *ic)
111 void
112 ieee80211_radiotap_vattach(struct ieee80211vap *vap)
114 struct ieee80211com *ic = vap->iv_ic;
115 struct ieee80211_radiotap_header *th = ic->ic_th;
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);
125 void
126 ieee80211_radiotap_vdetach(struct ieee80211vap *vap)
128 /* NB: bpfattach is called by ether_ifdetach and claims all taps */
131 static void
132 set_channel(void *p, const struct ieee80211_channel *c)
134 struct {
135 uint16_t freq;
136 uint16_t flags;
137 } *rc = p;
139 rc->freq = htole16(c->ic_freq);
140 rc->flags = htole16(c->ic_flags);
143 static void
144 set_xchannel(void *p, const struct ieee80211_channel *c)
146 struct {
147 uint32_t flags;
148 uint16_t freq;
149 uint8_t ieee;
150 uint8_t maxpow;
151 } *rc = p;
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;
160 * Update radiotap state on channel change.
162 void
163 ieee80211_radiotap_chan_change(struct ieee80211com *ic)
165 if (ic->ic_rxchan != NULL) {
166 struct ieee80211_radiotap_header *rh = ic->ic_rh;
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);
173 if (ic->ic_txchan != NULL) {
174 struct ieee80211_radiotap_header *th = ic->ic_th;
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);
184 * Distribute radiotap data (+packet) to all monitor mode
185 * vaps with an active tap other than vap0.
187 static void
188 spam_vaps(struct ieee80211vap *vap0, struct mbuf *m,
189 struct ieee80211_radiotap_header *rh, int len)
191 struct ieee80211com *ic = vap0->iv_ic;
192 struct ieee80211vap *vap;
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);
204 * Dispatch radiotap data for transmitted packet.
206 void
207 ieee80211_radiotap_tx(struct ieee80211vap *vap0, struct mbuf *m)
209 struct ieee80211com *ic = vap0->iv_ic;
210 struct ieee80211_radiotap_header *th = ic->ic_th;
211 int len;
213 KASSERT(th != NULL, ("no tx radiotap header"));
214 len = le16toh(th->it_len);
216 if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF)
217 bpf_mtap2(vap0->iv_rawbpf, th, len, m);
219 * Spam monitor mode vaps.
221 if (ic->ic_montaps != 0)
222 spam_vaps(vap0, m, th, len);
226 * Dispatch radiotap data for received packet.
228 void
229 ieee80211_radiotap_rx(struct ieee80211vap *vap0, struct mbuf *m)
231 struct ieee80211com *ic = vap0->iv_ic;
232 struct ieee80211_radiotap_header *rh = ic->ic_rh;
233 int len;
235 KASSERT(rh != NULL, ("no rx radiotap header"));
236 len = le16toh(rh->it_len);
238 if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF)
239 bpf_mtap2(vap0->iv_rawbpf, rh, len, m);
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.
245 if (ic->ic_montaps != 0 && (m->m_flags & M_BCAST) == 0)
246 spam_vaps(vap0, m, rh, len);
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.
254 void
255 ieee80211_radiotap_rx_all(struct ieee80211com *ic, struct mbuf *m)
257 struct ieee80211_radiotap_header *rh = ic->ic_rh;
258 int len = le16toh(rh->it_len);
259 struct ieee80211vap *vap;
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);
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.
274 static int
275 radiotap_offset(struct ieee80211_radiotap_header *rh,
276 int n_vendor_attributes, int item)
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),
285 [IEEE80211_RADIOTAP_FLAGS] = {
286 .align = sizeof(uint8_t),
287 .width = sizeof(uint8_t),
289 [IEEE80211_RADIOTAP_RATE] = {
290 .align = sizeof(uint8_t),
291 .width = sizeof(uint8_t),
293 [IEEE80211_RADIOTAP_CHANNEL] = {
294 .align = sizeof(uint16_t),
295 .width = 2*sizeof(uint16_t),
297 [IEEE80211_RADIOTAP_FHSS] = {
298 .align = sizeof(uint16_t),
299 .width = sizeof(uint16_t),
301 [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = {
302 .align = sizeof(uint8_t),
303 .width = sizeof(uint8_t),
305 [IEEE80211_RADIOTAP_DBM_ANTNOISE] = {
306 .align = sizeof(uint8_t),
307 .width = sizeof(uint8_t),
309 [IEEE80211_RADIOTAP_LOCK_QUALITY] = {
310 .align = sizeof(uint16_t),
311 .width = sizeof(uint16_t),
313 [IEEE80211_RADIOTAP_TX_ATTENUATION] = {
314 .align = sizeof(uint16_t),
315 .width = sizeof(uint16_t),
317 [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = {
318 .align = sizeof(uint16_t),
319 .width = sizeof(uint16_t),
321 [IEEE80211_RADIOTAP_DBM_TX_POWER] = {
322 .align = sizeof(uint8_t),
323 .width = sizeof(uint8_t),
325 [IEEE80211_RADIOTAP_ANTENNA] = {
326 .align = sizeof(uint8_t),
327 .width = sizeof(uint8_t),
329 [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = {
330 .align = sizeof(uint8_t),
331 .width = sizeof(uint8_t),
333 [IEEE80211_RADIOTAP_DB_ANTNOISE] = {
334 .align = sizeof(uint8_t),
335 .width = sizeof(uint8_t),
337 [IEEE80211_RADIOTAP_XCHANNEL] = {
338 .align = sizeof(uint32_t),
339 .width = 2*sizeof(uint32_t),
341 [IEEE80211_RADIOTAP_MCS] = {
342 .align = sizeof(uint8_t),
343 .width = 3*sizeof(uint8_t),
346 uint32_t present = le32toh(rh->it_present);
347 int off, i;
349 off = sizeof(struct ieee80211_radiotap_header);
350 off += n_vendor_attributes * (sizeof(uint32_t));
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;
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;
369 return off;
371 off += items[i].width;
373 return -1;