1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
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 /*
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
51 static int radiotap_offset(struct ieee80211_radiotap_header *, int, int);
52
53 void
ieee80211_radiotap_attach(struct ieee80211com * ic,struct ieee80211_radiotap_header * th,int tlen,uint32_t tx_radiotap,struct ieee80211_radiotap_header * rh,int rlen,uint32_t rx_radiotap)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)
57 {
58 ieee80211_radiotap_attachv(ic, th, tlen, 0, tx_radiotap,
59 rh, rlen, 0, rx_radiotap);
60 }
61
62 void
ieee80211_radiotap_attachv(struct ieee80211com * ic,struct ieee80211_radiotap_header * th,int tlen,int n_tx_v,uint32_t tx_radiotap,struct ieee80211_radiotap_header * rh,int rlen,int n_rx_v,uint32_t rx_radiotap)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)
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
106 void
ieee80211_radiotap_detach(struct ieee80211com * ic)107 ieee80211_radiotap_detach(struct ieee80211com *ic)
108 {
109 }
110
111 void
ieee80211_radiotap_vattach(struct ieee80211vap * vap)112 ieee80211_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
125 void
ieee80211_radiotap_vdetach(struct ieee80211vap * vap)126 ieee80211_radiotap_vdetach(struct ieee80211vap *vap)
127 {
128 /* NB: bpfdetach is called by ether_ifdetach and claims all taps */
129 }
130
131 static void
set_channel(void * p,const struct ieee80211_channel * c)132 set_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
143 static void
set_xchannel(void * p,const struct ieee80211_channel * c)144 set_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 */
162 void
ieee80211_radiotap_chan_change(struct ieee80211com * ic)163 ieee80211_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 */
187 static void
spam_vaps(struct ieee80211vap * vap0,struct mbuf * m,struct ieee80211_radiotap_header * rh,int len)188 spam_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 */
206 void
ieee80211_radiotap_tx(struct ieee80211vap * vap0,struct mbuf * m)207 ieee80211_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 */
228 void
ieee80211_radiotap_rx(struct ieee80211vap * vap0,struct mbuf * m)229 ieee80211_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 */
254 void
ieee80211_radiotap_rx_all(struct ieee80211com * ic,struct mbuf * m)255 ieee80211_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 */
274 static int
radiotap_offset(struct ieee80211_radiotap_header * rh,int n_vendor_attributes,int item)275 radiotap_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