ieee80211_radiotap.c revision 193292
123571Sbde/*- 223571Sbde * Copyright (c) 2009 Sam Leffler, Errno Consulting 323571Sbde * All rights reserved. 423571Sbde * 523571Sbde * Redistribution and use in source and binary forms, with or without 623571Sbde * modification, are permitted provided that the following conditions 723571Sbde * are met: 823571Sbde * 1. Redistributions of source code must retain the above copyright 923571Sbde * notice, this list of conditions and the following disclaimer. 1023571Sbde * 2. Redistributions in binary form must reproduce the above copyright 1123571Sbde * notice, this list of conditions and the following disclaimer in the 1223571Sbde * documentation and/or other materials provided with the distribution. 1323571Sbde * 1423571Sbde * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1523571Sbde * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1623571Sbde * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1723571Sbde * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1823571Sbde * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1923571Sbde * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2023571Sbde * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2123571Sbde * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2223571Sbde * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2323571Sbde * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2423571Sbde */ 2523571Sbde 2623571Sbde#include <sys/cdefs.h> 2723571Sbde__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_radiotap.c 193292 2009-06-02 00:33:28Z sam $"); 2823571Sbde 2923571Sbde/* 3023571Sbde * IEEE 802.11 radiotap support. 3123571Sbde */ 3223571Sbde#include "opt_wlan.h" 3350477Speter 3423571Sbde#include <sys/param.h> 3523571Sbde#include <sys/systm.h> 3625111Sbde#include <sys/mbuf.h> 3725111Sbde#include <sys/malloc.h> 3825111Sbde#include <sys/endian.h> 3923571Sbde#include <sys/kernel.h> 4023571Sbde 4123571Sbde#include <sys/socket.h> 4223571Sbde 4323571Sbde#include <net/bpf.h> 4423571Sbde#include <net/if.h> 4523571Sbde#include <net/if_llc.h> 4623571Sbde#include <net/if_media.h> 4723571Sbde 4823571Sbde#include <net80211/ieee80211_var.h> 4923571Sbde 5023571Sbdestatic int radiotap_offset(struct ieee80211_radiotap_header *, int); 5123571Sbde 5223571Sbdevoid 5323571Sbdeieee80211_radiotap_attach(struct ieee80211com *ic, 5423571Sbde struct ieee80211_radiotap_header *th, int tlen, uint32_t tx_radiotap, 5523571Sbde struct ieee80211_radiotap_header *rh, int rlen, uint32_t rx_radiotap) 5623571Sbde{ 5723571Sbde#define B(_v) (1<<(_v)) 5823571Sbde int off; 5923571Sbde 6023571Sbde th->it_len = htole16(roundup2(tlen, sizeof(uint32_t))); 6123571Sbde th->it_present = htole32(tx_radiotap); 6223571Sbde ic->ic_th = th; 6323571Sbde /* calculate offset to channel data */ 6423571Sbde off = -1; 6523571Sbde if (tx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) 6623571Sbde off = radiotap_offset(th, IEEE80211_RADIOTAP_CHANNEL); 67115671Sobrien else if (tx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) 6823571Sbde off = radiotap_offset(th, IEEE80211_RADIOTAP_XCHANNEL); 6925111Sbde if (off == -1) { 7025111Sbde if_printf(ic->ic_ifp, "%s: no tx channel, radiotap 0x%x", 7123571Sbde __func__, tx_radiotap); 7225111Sbde /* NB: we handle this case but data will have no chan spec */ 7325111Sbde } else 74171914Sjkoshy ic->ic_txchan = ((uint8_t *) th) + off; 7525111Sbde 7625111Sbde rh->it_len = htole16(roundup2(rlen, sizeof(uint32_t))); 7756581Sbde rh->it_present = htole32(rx_radiotap); 7825111Sbde ic->ic_rh = rh; 7925111Sbde /* calculate offset to channel data */ 8056581Sbde off = -1; 8156581Sbde if (rx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) 8256581Sbde off = radiotap_offset(rh, IEEE80211_RADIOTAP_CHANNEL); 8356581Sbde else if (rx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) 8456581Sbde off = radiotap_offset(rh, IEEE80211_RADIOTAP_XCHANNEL); 8556581Sbde if (off == -1) { 8656581Sbde if_printf(ic->ic_ifp, "%s: no rx channel, radiotap 0x%x", 8723571Sbde __func__, rx_radiotap); 8856581Sbde /* NB: we handle this case but data will have no chan spec */ 8925111Sbde } else 9023571Sbde ic->ic_rxchan = ((uint8_t *) rh) + off; 9123571Sbde#undef B 9225111Sbde} 9323571Sbde 9492998Sobrienvoid 9592998Sobrienieee80211_radiotap_detach(struct ieee80211com *ic) 9692998Sobrien{ 9792998Sobrien} 9892998Sobrien 9992998Sobrienvoid 10092998Sobrienieee80211_radiotap_vattach(struct ieee80211vap *vap) 10125111Sbde{ 102 struct ieee80211com *ic = vap->iv_ic; 103 struct ieee80211_radiotap_header *th = ic->ic_th; 104 105 if (th != NULL && ic->ic_rh != NULL) { 106 /* radiotap DLT for raw 802.11 frames */ 107 bpfattach2(vap->iv_ifp, DLT_IEEE802_11_RADIO, 108 sizeof(struct ieee80211_frame) + le16toh(th->it_len), 109 &vap->iv_rawbpf); 110 } 111} 112 113void 114ieee80211_radiotap_vdetach(struct ieee80211vap *vap) 115{ 116 /* NB: bpfattach is called by ether_ifdetach and claims all taps */ 117} 118 119static void 120set_channel(void *p, const struct ieee80211_channel *c) 121{ 122 struct { 123 uint16_t freq; 124 uint16_t flags; 125 } *rc = p; 126 127 rc->freq = htole16(c->ic_freq); 128 rc->flags = htole16(c->ic_flags); 129} 130 131static void 132set_xchannel(void *p, const struct ieee80211_channel *c) 133{ 134 struct { 135 uint32_t flags; 136 uint16_t freq; 137 uint8_t ieee; 138 uint8_t maxpow; 139 } *rc = p; 140 141 rc->flags = htole32(c->ic_flags); 142 rc->freq = htole16(c->ic_freq); 143 rc->ieee = c->ic_ieee; 144 rc->maxpow = c->ic_maxregpower; 145} 146 147/* 148 * Update radiotap state on channel change. 149 */ 150void 151ieee80211_radiotap_chan_change(struct ieee80211com *ic) 152{ 153 if (ic->ic_rxchan != NULL) { 154 struct ieee80211_radiotap_header *rh = ic->ic_rh; 155 156 if (rh->it_present & (1<<IEEE80211_RADIOTAP_XCHANNEL)) 157 set_xchannel(ic->ic_rxchan, ic->ic_curchan); 158 else if (rh->it_present & (1<<IEEE80211_RADIOTAP_CHANNEL)) 159 set_channel(ic->ic_rxchan, ic->ic_curchan); 160 } 161 if (ic->ic_txchan != NULL) { 162 struct ieee80211_radiotap_header *th = ic->ic_th; 163 164 if (th->it_present & (1<<IEEE80211_RADIOTAP_XCHANNEL)) 165 set_xchannel(ic->ic_txchan, ic->ic_curchan); 166 else if (th->it_present & (1<<IEEE80211_RADIOTAP_CHANNEL)) 167 set_channel(ic->ic_txchan, ic->ic_curchan); 168 } 169} 170 171#if 0 172static void 173dispatch_radiotap(struct ieee80211vap *vap0, struct mbuf *m, 174 struct ieee80211_radiotap_header *rh) 175{ 176 struct ieee80211com *ic = vap0->iv_ic; 177 int len = le16toh(rh->it_len); 178 179 if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF) 180 bpf_mtap2(vap0->iv_rawbpf, rh, len, m); 181 /* 182 * Spam monitor mode vaps with unicast frames. Multicast 183 * frames are handled by passing through ieee80211_input_all 184 * which distributes copies to the monitor mode vaps to be 185 * processed above. 186 */ 187 if (ic->ic_montaps != 0 && (m->m_flags & M_BCAST) == 0) { 188 struct ieee80211vap *vap; 189 TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 190 if (vap != vap0 && 191 vap->iv_opmode == IEEE80211_M_MONITOR && 192 (vap->iv_flags_ext & IEEE80211_FEXT_BPF) && 193 vap->iv_state != IEEE80211_S_INIT) 194 bpf_mtap2(vap->iv_rawbpf, rh, len, m); 195 } 196 } 197} 198#endif 199 200/* 201 * Distribute radiotap data (+packet) to all monitor mode 202 * vaps with an active tap other than vap0. 203 */ 204static void 205spam_vaps(struct ieee80211vap *vap0, struct mbuf *m, 206 struct ieee80211_radiotap_header *rh, int len) 207{ 208 struct ieee80211com *ic = vap0->iv_ic; 209 struct ieee80211vap *vap; 210 211 TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 212 if (vap != vap0 && 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); 217 } 218} 219 220/* 221 * Dispatch radiotap data for transmitted packet. 222 */ 223void 224ieee80211_radiotap_tx(struct ieee80211vap *vap0, struct mbuf *m) 225{ 226 struct ieee80211com *ic = vap0->iv_ic; 227 struct ieee80211_radiotap_header *th = ic->ic_th; 228 int len; 229 230 KASSERT(th != NULL, ("no tx radiotap header")); 231 len = le16toh(th->it_len); 232 233 if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF) 234 bpf_mtap2(vap0->iv_rawbpf, th, len, m); 235 /* 236 * Spam monitor mode vaps. 237 */ 238 if (ic->ic_montaps != 0) 239 spam_vaps(vap0, m, th, len); 240} 241 242/* 243 * Dispatch radiotap data for received packet. 244 */ 245void 246ieee80211_radiotap_rx(struct ieee80211vap *vap0, struct mbuf *m) 247{ 248 struct ieee80211com *ic = vap0->iv_ic; 249 struct ieee80211_radiotap_header *rh = ic->ic_rh; 250 int len; 251 252 KASSERT(rh != NULL, ("no rx radiotap header")); 253 len = le16toh(rh->it_len); 254 255 if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF) 256 bpf_mtap2(vap0->iv_rawbpf, rh, len, m); 257 /* 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. 261 */ 262 if (ic->ic_montaps != 0 && (m->m_flags & M_BCAST) == 0) 263 spam_vaps(vap0, m, rh, len); 264} 265 266/* 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. 270 */ 271void 272ieee80211_radiotap_rx_all(struct ieee80211com *ic, struct mbuf *m) 273{ 274 struct ieee80211_radiotap_header *rh = ic->ic_rh; 275 int len = le16toh(rh->it_len); 276 struct ieee80211vap *vap; 277 278 /* XXX locking? */ 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); 283 } 284} 285 286/* 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. 290 */ 291static int 292radiotap_offset(struct ieee80211_radiotap_header *rh, int item) 293{ 294 static const struct { 295 size_t align, width; 296 } items[] = { 297 [IEEE80211_RADIOTAP_TSFT] = { 298 .align = sizeof(uint64_t), 299 .width = sizeof(uint64_t), 300 }, 301 [IEEE80211_RADIOTAP_FLAGS] = { 302 .align = sizeof(uint8_t), 303 .width = sizeof(uint8_t), 304 }, 305 [IEEE80211_RADIOTAP_RATE] = { 306 .align = sizeof(uint8_t), 307 .width = sizeof(uint8_t), 308 }, 309 [IEEE80211_RADIOTAP_CHANNEL] = { 310 .align = sizeof(uint16_t), 311 .width = 2*sizeof(uint16_t), 312 }, 313 [IEEE80211_RADIOTAP_FHSS] = { 314 .align = sizeof(uint16_t), 315 .width = sizeof(uint16_t), 316 }, 317 [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { 318 .align = sizeof(uint8_t), 319 .width = sizeof(uint8_t), 320 }, 321 [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { 322 .align = sizeof(uint8_t), 323 .width = sizeof(uint8_t), 324 }, 325 [IEEE80211_RADIOTAP_LOCK_QUALITY] = { 326 .align = sizeof(uint16_t), 327 .width = sizeof(uint16_t), 328 }, 329 [IEEE80211_RADIOTAP_TX_ATTENUATION] = { 330 .align = sizeof(uint16_t), 331 .width = sizeof(uint16_t), 332 }, 333 [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { 334 .align = sizeof(uint16_t), 335 .width = sizeof(uint16_t), 336 }, 337 [IEEE80211_RADIOTAP_DBM_TX_POWER] = { 338 .align = sizeof(uint8_t), 339 .width = sizeof(uint8_t), 340 }, 341 [IEEE80211_RADIOTAP_ANTENNA] = { 342 .align = sizeof(uint8_t), 343 .width = sizeof(uint8_t), 344 }, 345 [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { 346 .align = sizeof(uint8_t), 347 .width = sizeof(uint8_t), 348 }, 349 [IEEE80211_RADIOTAP_DB_ANTNOISE] = { 350 .align = sizeof(uint8_t), 351 .width = sizeof(uint8_t), 352 }, 353 [IEEE80211_RADIOTAP_XCHANNEL] = { 354 .align = sizeof(uint32_t), 355 .width = 2*sizeof(uint32_t), 356 }, 357 }; 358 uint32_t present = le32toh(rh->it_present); 359 int off, i; 360 361 off = sizeof(struct ieee80211_radiotap_header); 362 for (i = 0; i < IEEE80211_RADIOTAP_EXT; i++) { 363 if ((present & (1<<i)) == 0) 364 continue; 365 if (items[i].align == 0) { 366 /* NB: unidentified element, don't guess */ 367 printf("%s: unknown item %d\n", __func__, i); 368 return -1; 369 } 370 off = roundup2(off, items[i].align); 371 if (i == item) { 372 if (off + items[i].width > le16toh(rh->it_len)) { 373 /* NB: item does not fit in header data */ 374 printf("%s: item %d not in header data, " 375 "off %d width %zu len %d\n", __func__, i, 376 off, items[i].width, le16toh(rh->it_len)); 377 return -1; 378 } 379 return off; 380 } 381 off += items[i].width; 382 } 383 return -1; 384} 385