2 * Copyright (c) 2009 Sam Leffler, Errno Consulting
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
34 #include <sys/param.h>
35 #include <sys/systm.h>
37 #include <sys/malloc.h>
38 #include <sys/endian.h>
39 #include <sys/kernel.h>
41 #include <sys/socket.h>
45 #include <net/if_var.h>
46 #include <net/if_media.h>
47 #include <net/ethernet.h>
49 #include <netproto/802_11/ieee80211_var.h>
51 #if defined(__DragonFly__)
52 #define bpf_mtap2(rawbpf, rh, len, m) \
56 bpf_ptap(rawbpf, m, rh, len); \
61 static int radiotap_offset(struct ieee80211_radiotap_header
*, int, int);
64 ieee80211_radiotap_attach(struct ieee80211com
*ic
,
65 struct ieee80211_radiotap_header
*th
, int tlen
, uint32_t tx_radiotap
,
66 struct ieee80211_radiotap_header
*rh
, int rlen
, uint32_t rx_radiotap
)
68 ieee80211_radiotap_attachv(ic
, th
, tlen
, 0, tx_radiotap
,
69 rh
, rlen
, 0, rx_radiotap
);
73 ieee80211_radiotap_attachv(struct ieee80211com
*ic
,
74 struct ieee80211_radiotap_header
*th
,
75 int tlen
, int n_tx_v
, uint32_t tx_radiotap
,
76 struct ieee80211_radiotap_header
*rh
,
77 int rlen
, int n_rx_v
, uint32_t rx_radiotap
)
79 #define B(_v) (1<<(_v))
82 th
->it_len
= htole16(roundup2(tlen
, sizeof(uint32_t)));
83 th
->it_present
= htole32(tx_radiotap
);
85 /* calculate offset to channel data */
87 if (tx_radiotap
& B(IEEE80211_RADIOTAP_CHANNEL
))
88 off
= radiotap_offset(th
, n_tx_v
, IEEE80211_RADIOTAP_CHANNEL
);
89 else if (tx_radiotap
& B(IEEE80211_RADIOTAP_XCHANNEL
))
90 off
= radiotap_offset(th
, n_tx_v
, IEEE80211_RADIOTAP_XCHANNEL
);
92 ic_printf(ic
, "%s: no tx channel, radiotap 0x%x\n", __func__
,
94 /* NB: we handle this case but data will have no chan spec */
96 ic
->ic_txchan
= ((uint8_t *) th
) + off
;
98 rh
->it_len
= htole16(roundup2(rlen
, sizeof(uint32_t)));
99 rh
->it_present
= htole32(rx_radiotap
);
101 /* calculate offset to channel data */
103 if (rx_radiotap
& B(IEEE80211_RADIOTAP_CHANNEL
))
104 off
= radiotap_offset(rh
, n_rx_v
, IEEE80211_RADIOTAP_CHANNEL
);
105 else if (rx_radiotap
& B(IEEE80211_RADIOTAP_XCHANNEL
))
106 off
= radiotap_offset(rh
, n_rx_v
, IEEE80211_RADIOTAP_XCHANNEL
);
108 ic_printf(ic
, "%s: no rx channel, radiotap 0x%x\n", __func__
,
110 /* NB: we handle this case but data will have no chan spec */
112 ic
->ic_rxchan
= ((uint8_t *) rh
) + off
;
117 ieee80211_radiotap_detach(struct ieee80211com
*ic
)
122 ieee80211_radiotap_vattach(struct ieee80211vap
*vap
)
124 struct ieee80211com
*ic
= vap
->iv_ic
;
125 struct ieee80211_radiotap_header
*th
= ic
->ic_th
;
127 if (th
!= NULL
&& ic
->ic_rh
!= NULL
) {
128 #if defined(__DragonFly__)
129 bpfattach_dlt(vap
->iv_ifp
, DLT_IEEE802_11_RADIO
,
130 sizeof(struct ieee80211_frame
) +
134 /* radiotap DLT for raw 802.11 frames */
135 bpfattach2(vap
->iv_ifp
, DLT_IEEE802_11_RADIO
,
136 sizeof(struct ieee80211_frame
) + le16toh(th
->it_len
),
143 ieee80211_radiotap_vdetach(struct ieee80211vap
*vap
)
145 /* NB: bpfattach is called by ether_ifdetach and claims all taps */
149 set_channel(void *p
, const struct ieee80211_channel
*c
)
156 rc
->freq
= htole16(c
->ic_freq
);
157 rc
->flags
= htole16(c
->ic_flags
);
161 set_xchannel(void *p
, const struct ieee80211_channel
*c
)
170 rc
->flags
= htole32(c
->ic_flags
);
171 rc
->freq
= htole16(c
->ic_freq
);
172 rc
->ieee
= c
->ic_ieee
;
173 rc
->maxpow
= c
->ic_maxregpower
;
177 * Update radiotap state on channel change.
180 ieee80211_radiotap_chan_change(struct ieee80211com
*ic
)
182 if (ic
->ic_rxchan
!= NULL
) {
183 struct ieee80211_radiotap_header
*rh
= ic
->ic_rh
;
185 if (rh
->it_present
& htole32(1<<IEEE80211_RADIOTAP_XCHANNEL
))
186 set_xchannel(ic
->ic_rxchan
, ic
->ic_curchan
);
187 else if (rh
->it_present
& htole32(1<<IEEE80211_RADIOTAP_CHANNEL
))
188 set_channel(ic
->ic_rxchan
, ic
->ic_curchan
);
190 if (ic
->ic_txchan
!= NULL
) {
191 struct ieee80211_radiotap_header
*th
= ic
->ic_th
;
193 if (th
->it_present
& htole32(1<<IEEE80211_RADIOTAP_XCHANNEL
))
194 set_xchannel(ic
->ic_txchan
, ic
->ic_curchan
);
195 else if (th
->it_present
& htole32(1<<IEEE80211_RADIOTAP_CHANNEL
))
196 set_channel(ic
->ic_txchan
, ic
->ic_curchan
);
201 * Distribute radiotap data (+packet) to all monitor mode
202 * vaps with an active tap other than vap0.
205 spam_vaps(struct ieee80211vap
*vap0
, struct mbuf
*m
,
206 struct ieee80211_radiotap_header
*rh
, int len
)
208 struct ieee80211com
*ic
= vap0
->iv_ic
;
209 struct ieee80211vap
*vap
;
211 TAILQ_FOREACH(vap
, &ic
->ic_vaps
, iv_next
) {
213 vap
->iv_opmode
== IEEE80211_M_MONITOR
&&
214 (vap
->iv_flags_ext
& IEEE80211_FEXT_BPF
) &&
215 vap
->iv_state
!= IEEE80211_S_INIT
)
216 bpf_mtap2(vap
->iv_rawbpf
, rh
, len
, m
);
221 * Dispatch radiotap data for transmitted packet.
224 ieee80211_radiotap_tx(struct ieee80211vap
*vap0
, struct mbuf
*m
)
226 struct ieee80211com
*ic
= vap0
->iv_ic
;
227 struct ieee80211_radiotap_header
*th
= ic
->ic_th
;
230 KASSERT(th
!= NULL
, ("no tx radiotap header"));
231 len
= le16toh(th
->it_len
);
233 if (vap0
->iv_flags_ext
& IEEE80211_FEXT_BPF
)
234 bpf_mtap2(vap0
->iv_rawbpf
, th
, len
, m
);
236 * Spam monitor mode vaps.
238 if (ic
->ic_montaps
!= 0)
239 spam_vaps(vap0
, m
, th
, len
);
243 * Dispatch radiotap data for received packet.
246 ieee80211_radiotap_rx(struct ieee80211vap
*vap0
, struct mbuf
*m
)
248 struct ieee80211com
*ic
= vap0
->iv_ic
;
249 struct ieee80211_radiotap_header
*rh
= ic
->ic_rh
;
252 KASSERT(rh
!= NULL
, ("no rx radiotap header"));
253 len
= le16toh(rh
->it_len
);
255 if (vap0
->iv_flags_ext
& IEEE80211_FEXT_BPF
)
256 bpf_mtap2(vap0
->iv_rawbpf
, rh
, len
, m
);
258 * Spam monitor mode vaps with unicast frames. Multicast
259 * frames are handled by passing through ieee80211_input_all
260 * which distributes copies to the monitor mode vaps.
262 if (ic
->ic_montaps
!= 0 && (m
->m_flags
& M_BCAST
) == 0)
263 spam_vaps(vap0
, m
, rh
, len
);
267 * Dispatch radiotap data for a packet received outside the normal
268 * rx processing path; this is used, for example, to handle frames
269 * received with errors that would otherwise be dropped.
272 ieee80211_radiotap_rx_all(struct ieee80211com
*ic
, struct mbuf
*m
)
274 struct ieee80211_radiotap_header
*rh
= ic
->ic_rh
;
275 int len
= le16toh(rh
->it_len
);
276 struct ieee80211vap
*vap
;
279 TAILQ_FOREACH(vap
, &ic
->ic_vaps
, iv_next
) {
280 if (ieee80211_radiotap_active_vap(vap
) &&
281 vap
->iv_state
!= IEEE80211_S_INIT
)
282 bpf_mtap2(vap
->iv_rawbpf
, rh
, len
, m
);
287 * Return the offset of the specified item in the radiotap
288 * header description. If the item is not present or is not
289 * known -1 is returned.
292 radiotap_offset(struct ieee80211_radiotap_header
*rh
,
293 int n_vendor_attributes
, int item
)
295 static const struct {
298 [IEEE80211_RADIOTAP_TSFT
] = {
299 .align
= sizeof(uint64_t),
300 .width
= sizeof(uint64_t),
302 [IEEE80211_RADIOTAP_FLAGS
] = {
303 .align
= sizeof(uint8_t),
304 .width
= sizeof(uint8_t),
306 [IEEE80211_RADIOTAP_RATE
] = {
307 .align
= sizeof(uint8_t),
308 .width
= sizeof(uint8_t),
310 [IEEE80211_RADIOTAP_CHANNEL
] = {
311 .align
= sizeof(uint16_t),
312 .width
= 2*sizeof(uint16_t),
314 [IEEE80211_RADIOTAP_FHSS
] = {
315 .align
= sizeof(uint16_t),
316 .width
= sizeof(uint16_t),
318 [IEEE80211_RADIOTAP_DBM_ANTSIGNAL
] = {
319 .align
= sizeof(uint8_t),
320 .width
= sizeof(uint8_t),
322 [IEEE80211_RADIOTAP_DBM_ANTNOISE
] = {
323 .align
= sizeof(uint8_t),
324 .width
= sizeof(uint8_t),
326 [IEEE80211_RADIOTAP_LOCK_QUALITY
] = {
327 .align
= sizeof(uint16_t),
328 .width
= sizeof(uint16_t),
330 [IEEE80211_RADIOTAP_TX_ATTENUATION
] = {
331 .align
= sizeof(uint16_t),
332 .width
= sizeof(uint16_t),
334 [IEEE80211_RADIOTAP_DB_TX_ATTENUATION
] = {
335 .align
= sizeof(uint16_t),
336 .width
= sizeof(uint16_t),
338 [IEEE80211_RADIOTAP_DBM_TX_POWER
] = {
339 .align
= sizeof(uint8_t),
340 .width
= sizeof(uint8_t),
342 [IEEE80211_RADIOTAP_ANTENNA
] = {
343 .align
= sizeof(uint8_t),
344 .width
= sizeof(uint8_t),
346 [IEEE80211_RADIOTAP_DB_ANTSIGNAL
] = {
347 .align
= sizeof(uint8_t),
348 .width
= sizeof(uint8_t),
350 [IEEE80211_RADIOTAP_DB_ANTNOISE
] = {
351 .align
= sizeof(uint8_t),
352 .width
= sizeof(uint8_t),
354 [IEEE80211_RADIOTAP_XCHANNEL
] = {
355 .align
= sizeof(uint32_t),
356 .width
= 2*sizeof(uint32_t),
358 [IEEE80211_RADIOTAP_MCS
] = {
359 .align
= sizeof(uint8_t),
360 .width
= 3*sizeof(uint8_t),
363 uint32_t present
= le32toh(rh
->it_present
);
366 off
= sizeof(struct ieee80211_radiotap_header
);
367 off
+= n_vendor_attributes
* (sizeof(uint32_t));
369 for (i
= 0; i
< IEEE80211_RADIOTAP_EXT
; i
++) {
370 if ((present
& (1<<i
)) == 0)
372 if (items
[i
].align
== 0) {
373 /* NB: unidentified element, don't guess */
374 kprintf("%s: unknown item %d\n", __func__
, i
);
377 off
= roundup2(off
, items
[i
].align
);
379 if (off
+ items
[i
].width
> le16toh(rh
->it_len
)) {
380 /* NB: item does not fit in header data */
381 kprintf("%s: item %d not in header data, "
382 "off %d width %zu len %d\n", __func__
, i
,
383 off
, items
[i
].width
, le16toh(rh
->it_len
));
388 off
+= items
[i
].width
;