ieee80211_radiotap.c revision 229952
1206360Sjoel/*- 2215800Szec * Copyright (c) 2009 Sam Leffler, Errno Consulting 3182734Sjulian * All rights reserved. 4182734Sjulian * 5182734Sjulian * Redistribution and use in source and binary forms, with or without 6182734Sjulian * modification, are permitted provided that the following conditions 7182734Sjulian * are met: 8182734Sjulian * 1. Redistributions of source code must retain the above copyright 9182734Sjulian * notice, this list of conditions and the following disclaimer. 10182734Sjulian * 2. Redistributions in binary form must reproduce the above copyright 11182734Sjulian * notice, this list of conditions and the following disclaimer in the 12182734Sjulian * documentation and/or other materials provided with the distribution. 13182734Sjulian * 14182734Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15182734Sjulian * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16182734Sjulian * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17182734Sjulian * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18182734Sjulian * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19182734Sjulian * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20182734Sjulian * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21182734Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22182734Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23182734Sjulian * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24182734Sjulian */ 25182734Sjulian 26182734Sjulian#include <sys/cdefs.h> 27182734Sjulian__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_radiotap.c 229952 2012-01-11 01:09:08Z adrian $"); 28182734Sjulian 29182734Sjulian/* 30182734Sjulian * IEEE 802.11 radiotap support. 31182734Sjulian */ 32182734Sjulian#include "opt_wlan.h" 33182734Sjulian 34182734Sjulian#include <sys/param.h> 35182734Sjulian#include <sys/systm.h> 36182734Sjulian#include <sys/mbuf.h> 37182734Sjulian#include <sys/malloc.h> 38182734Sjulian#include <sys/endian.h> 39182734Sjulian#include <sys/kernel.h> 40182734Sjulian 41182734Sjulian#include <sys/socket.h> 42182734Sjulian 43182734Sjulian#include <net/bpf.h> 44182734Sjulian#include <net/if.h> 45182734Sjulian#include <net/if_llc.h> 46182734Sjulian#include <net/if_media.h> 47182734Sjulian 48182734Sjulian#include <net80211/ieee80211_var.h> 49182734Sjulian 50182734Sjulianstatic int radiotap_offset(struct ieee80211_radiotap_header *, int); 51182734Sjulian 52196019Srwatsonvoid 53196019Srwatsonieee80211_radiotap_attach(struct ieee80211com *ic, 54182734Sjulian struct ieee80211_radiotap_header *th, int tlen, uint32_t tx_radiotap, 55182734Sjulian struct ieee80211_radiotap_header *rh, int rlen, uint32_t rx_radiotap) 56182734Sjulian{ 57182734Sjulian#define B(_v) (1<<(_v)) 58182734Sjulian int off; 59182734Sjulian 60182734Sjulian th->it_len = htole16(roundup2(tlen, sizeof(uint32_t))); 61182734Sjulian th->it_present = htole32(tx_radiotap); 62182734Sjulian ic->ic_th = th; 63182734Sjulian /* calculate offset to channel data */ 64182734Sjulian off = -1; 65182734Sjulian if (tx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) 66182734Sjulian off = radiotap_offset(th, IEEE80211_RADIOTAP_CHANNEL); 67182734Sjulian else if (tx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) 68182734Sjulian off = radiotap_offset(th, IEEE80211_RADIOTAP_XCHANNEL); 69182734Sjulian if (off == -1) { 70182734Sjulian if_printf(ic->ic_ifp, "%s: no tx channel, radiotap 0x%x", 71182734Sjulian __func__, tx_radiotap); 72182734Sjulian /* NB: we handle this case but data will have no chan spec */ 73182734Sjulian } else 74182734Sjulian ic->ic_txchan = ((uint8_t *) th) + off; 75182734Sjulian 76182734Sjulian rh->it_len = htole16(roundup2(rlen, sizeof(uint32_t))); 77182734Sjulian rh->it_present = htole32(rx_radiotap); 78182734Sjulian ic->ic_rh = rh; 79182734Sjulian /* calculate offset to channel data */ 80182734Sjulian off = -1; 81182734Sjulian if (rx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) 82182734Sjulian off = radiotap_offset(rh, IEEE80211_RADIOTAP_CHANNEL); 83182734Sjulian else if (rx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) 84182734Sjulian off = radiotap_offset(rh, IEEE80211_RADIOTAP_XCHANNEL); 85182734Sjulian if (off == -1) { 86182734Sjulian if_printf(ic->ic_ifp, "%s: no rx channel, radiotap 0x%x", 87182734Sjulian __func__, rx_radiotap); 88182734Sjulian /* NB: we handle this case but data will have no chan spec */ 89182734Sjulian } else 90182734Sjulian ic->ic_rxchan = ((uint8_t *) rh) + off; 91182734Sjulian#undef B 92182734Sjulian} 93182734Sjulian 94182734Sjulianvoid 95182734Sjulianieee80211_radiotap_detach(struct ieee80211com *ic) 96182734Sjulian{ 97182734Sjulian} 98182734Sjulian 99182734Sjulianvoid 100182734Sjulianieee80211_radiotap_vattach(struct ieee80211vap *vap) 101182734Sjulian{ 102182734Sjulian struct ieee80211com *ic = vap->iv_ic; 103215800Szec struct ieee80211_radiotap_header *th = ic->ic_th; 104215800Szec 105182734Sjulian if (th != NULL && ic->ic_rh != NULL) { 106182734Sjulian /* radiotap DLT for raw 802.11 frames */ 107182734Sjulian bpfattach2(vap->iv_ifp, DLT_IEEE802_11_RADIO, 108182734Sjulian sizeof(struct ieee80211_frame) + le16toh(th->it_len), 109182734Sjulian &vap->iv_rawbpf); 110182734Sjulian } 111182734Sjulian} 112182734Sjulian 113182734Sjulianvoid 114182734Sjulianieee80211_radiotap_vdetach(struct ieee80211vap *vap) 115182734Sjulian{ 116182734Sjulian /* NB: bpfattach is called by ether_ifdetach and claims all taps */ 117182734Sjulian} 118182734Sjulian 119182734Sjulianstatic void 120182734Sjulianset_channel(void *p, const struct ieee80211_channel *c) 121182734Sjulian{ 122182734Sjulian struct { 123182734Sjulian uint16_t freq; 124182734Sjulian uint16_t flags; 125182734Sjulian } *rc = p; 126182734Sjulian 127182734Sjulian rc->freq = htole16(c->ic_freq); 128182734Sjulian rc->flags = htole16(c->ic_flags); 129182734Sjulian} 130182734Sjulian 131182734Sjulianstatic void 132182734Sjulianset_xchannel(void *p, const struct ieee80211_channel *c) 133215800Szec{ 134182734Sjulian struct { 135182734Sjulian uint32_t flags; 136182734Sjulian uint16_t freq; 137182734Sjulian uint8_t ieee; 138182734Sjulian uint8_t maxpow; 139182734Sjulian } *rc = p; 140182734Sjulian 141182734Sjulian rc->flags = htole32(c->ic_flags); 142182734Sjulian rc->freq = htole16(c->ic_freq); 143182734Sjulian rc->ieee = c->ic_ieee; 144182734Sjulian rc->maxpow = c->ic_maxregpower; 145182734Sjulian} 146182734Sjulian 147182734Sjulian/* 148182734Sjulian * Update radiotap state on channel change. 149182734Sjulian */ 150182734Sjulianvoid 151182734Sjulianieee80211_radiotap_chan_change(struct ieee80211com *ic) 152182734Sjulian{ 153182734Sjulian if (ic->ic_rxchan != NULL) { 154182734Sjulian struct ieee80211_radiotap_header *rh = ic->ic_rh; 155182734Sjulian 156182734Sjulian if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL)) 157182734Sjulian set_xchannel(ic->ic_rxchan, ic->ic_curchan); 158182734Sjulian else if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL)) 159182734Sjulian set_channel(ic->ic_rxchan, ic->ic_curchan); 160182734Sjulian } 161182734Sjulian if (ic->ic_txchan != NULL) { 162182734Sjulian struct ieee80211_radiotap_header *th = ic->ic_th; 163182734Sjulian 164182734Sjulian if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL)) 165182734Sjulian set_xchannel(ic->ic_txchan, ic->ic_curchan); 166182734Sjulian else if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL)) 167182734Sjulian set_channel(ic->ic_txchan, ic->ic_curchan); 168182734Sjulian } 169182734Sjulian} 170182734Sjulian 171182734Sjulian/* 172182734Sjulian * Distribute radiotap data (+packet) to all monitor mode 173182734Sjulian * vaps with an active tap other than vap0. 174182734Sjulian */ 175182734Sjulianstatic void 176182734Sjulianspam_vaps(struct ieee80211vap *vap0, struct mbuf *m, 177182734Sjulian struct ieee80211_radiotap_header *rh, int len) 178182734Sjulian{ 179182734Sjulian struct ieee80211com *ic = vap0->iv_ic; 180182734Sjulian struct ieee80211vap *vap; 181182734Sjulian 182182734Sjulian TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 183182734Sjulian if (vap != vap0 && 184182734Sjulian vap->iv_opmode == IEEE80211_M_MONITOR && 185182734Sjulian (vap->iv_flags_ext & IEEE80211_FEXT_BPF) && 186182734Sjulian vap->iv_state != IEEE80211_S_INIT) 187182734Sjulian bpf_mtap2(vap->iv_rawbpf, rh, len, m); 188182734Sjulian } 189182734Sjulian} 190182734Sjulian 191182734Sjulian/* 192182734Sjulian * Dispatch radiotap data for transmitted packet. 193182734Sjulian */ 194182734Sjulianvoid 195182734Sjulianieee80211_radiotap_tx(struct ieee80211vap *vap0, struct mbuf *m) 196182734Sjulian{ 197182734Sjulian struct ieee80211com *ic = vap0->iv_ic; 198182734Sjulian struct ieee80211_radiotap_header *th = ic->ic_th; 199182734Sjulian int len; 200182734Sjulian 201182734Sjulian KASSERT(th != NULL, ("no tx radiotap header")); 202182734Sjulian len = le16toh(th->it_len); 203182734Sjulian 204182734Sjulian if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF) 205182734Sjulian bpf_mtap2(vap0->iv_rawbpf, th, len, m); 206182734Sjulian /* 207182734Sjulian * Spam monitor mode vaps. 208182734Sjulian */ 209182734Sjulian if (ic->ic_montaps != 0) 210182734Sjulian spam_vaps(vap0, m, th, len); 211182734Sjulian} 212182734Sjulian 213182734Sjulian/* 214182734Sjulian * Dispatch radiotap data for received packet. 215182734Sjulian */ 216182734Sjulianvoid 217182734Sjulianieee80211_radiotap_rx(struct ieee80211vap *vap0, struct mbuf *m) 218182734Sjulian{ 219182734Sjulian struct ieee80211com *ic = vap0->iv_ic; 220182734Sjulian struct ieee80211_radiotap_header *rh = ic->ic_rh; 221182734Sjulian int len; 222182734Sjulian 223182734Sjulian KASSERT(rh != NULL, ("no rx radiotap header")); 224182734Sjulian len = le16toh(rh->it_len); 225182734Sjulian 226182734Sjulian if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF) 227182734Sjulian bpf_mtap2(vap0->iv_rawbpf, rh, len, m); 228182734Sjulian /* 229182734Sjulian * Spam monitor mode vaps with unicast frames. Multicast 230182734Sjulian * frames are handled by passing through ieee80211_input_all 231182734Sjulian * which distributes copies to the monitor mode vaps. 232182734Sjulian */ 233182734Sjulian if (ic->ic_montaps != 0 && (m->m_flags & M_BCAST) == 0) 234182734Sjulian spam_vaps(vap0, m, rh, len); 235182734Sjulian} 236182734Sjulian 237182734Sjulian/* 238182734Sjulian * Dispatch radiotap data for a packet received outside the normal 239182734Sjulian * rx processing path; this is used, for example, to handle frames 240182734Sjulian * received with errors that would otherwise be dropped. 241182734Sjulian */ 242182734Sjulianvoid 243182734Sjulianieee80211_radiotap_rx_all(struct ieee80211com *ic, struct mbuf *m) 244182734Sjulian{ 245182734Sjulian struct ieee80211_radiotap_header *rh = ic->ic_rh; 246182734Sjulian int len = le16toh(rh->it_len); 247182734Sjulian struct ieee80211vap *vap; 248182734Sjulian 249182734Sjulian /* XXX locking? */ 250182734Sjulian TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 251182734Sjulian if (ieee80211_radiotap_active_vap(vap) && 252182734Sjulian vap->iv_state != IEEE80211_S_INIT) 253182734Sjulian bpf_mtap2(vap->iv_rawbpf, rh, len, m); 254182734Sjulian } 255182734Sjulian} 256220768Sglebius 257182734Sjulian/* 258182734Sjulian * Return the offset of the specified item in the radiotap 259215800Szec * header description. If the item is not present or is not 260215800Szec * known -1 is returned. 261215800Szec */ 262215800Szecstatic int 263215800Szecradiotap_offset(struct ieee80211_radiotap_header *rh, int item) 264182734Sjulian{ 265182734Sjulian static const struct { 266182734Sjulian size_t align, width; 267182734Sjulian } items[] = { 268182734Sjulian [IEEE80211_RADIOTAP_TSFT] = { 269182734Sjulian .align = sizeof(uint64_t), 270182734Sjulian .width = sizeof(uint64_t), 271182734Sjulian }, 272182734Sjulian [IEEE80211_RADIOTAP_FLAGS] = { 273182734Sjulian .align = sizeof(uint8_t), 274182734Sjulian .width = sizeof(uint8_t), 275182734Sjulian }, 276182734Sjulian [IEEE80211_RADIOTAP_RATE] = { 277182734Sjulian .align = sizeof(uint8_t), 278182734Sjulian .width = sizeof(uint8_t), 279182734Sjulian }, 280182734Sjulian [IEEE80211_RADIOTAP_CHANNEL] = { 281182734Sjulian .align = sizeof(uint16_t), 282182734Sjulian .width = 2*sizeof(uint16_t), 283182734Sjulian }, 284182734Sjulian [IEEE80211_RADIOTAP_FHSS] = { 285182734Sjulian .align = sizeof(uint16_t), 286182734Sjulian .width = sizeof(uint16_t), 287182734Sjulian }, 288182734Sjulian [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { 289182734Sjulian .align = sizeof(uint8_t), 290182734Sjulian .width = sizeof(uint8_t), 291182734Sjulian }, 292182734Sjulian [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { 293182734Sjulian .align = sizeof(uint8_t), 294182734Sjulian .width = sizeof(uint8_t), 295182734Sjulian }, 296182734Sjulian [IEEE80211_RADIOTAP_LOCK_QUALITY] = { 297182734Sjulian .align = sizeof(uint16_t), 298182734Sjulian .width = sizeof(uint16_t), 299182734Sjulian }, 300182734Sjulian [IEEE80211_RADIOTAP_TX_ATTENUATION] = { 301182734Sjulian .align = sizeof(uint16_t), 302182734Sjulian .width = sizeof(uint16_t), 303182734Sjulian }, 304182734Sjulian [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { 305182734Sjulian .align = sizeof(uint16_t), 306182734Sjulian .width = sizeof(uint16_t), 307182734Sjulian }, 308182734Sjulian [IEEE80211_RADIOTAP_DBM_TX_POWER] = { 309182734Sjulian .align = sizeof(uint8_t), 310182734Sjulian .width = sizeof(uint8_t), 311182734Sjulian }, 312182734Sjulian [IEEE80211_RADIOTAP_ANTENNA] = { 313182734Sjulian .align = sizeof(uint8_t), 314182734Sjulian .width = sizeof(uint8_t), 315182734Sjulian }, 316182734Sjulian [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { 317182734Sjulian .align = sizeof(uint8_t), 318182734Sjulian .width = sizeof(uint8_t), 319182734Sjulian }, 320182734Sjulian [IEEE80211_RADIOTAP_DB_ANTNOISE] = { 321215800Szec .align = sizeof(uint8_t), 322182734Sjulian .width = sizeof(uint8_t), 323182734Sjulian }, 324182734Sjulian [IEEE80211_RADIOTAP_XCHANNEL] = { 325182734Sjulian .align = sizeof(uint32_t), 326182734Sjulian .width = 2*sizeof(uint32_t), 327182734Sjulian }, 328182734Sjulian [IEEE80211_RADIOTAP_MCS] = { 329182734Sjulian .align = sizeof(uint8_t), 330182734Sjulian .width = 3*sizeof(uint8_t), 331182734Sjulian }, 332182734Sjulian }; 333182734Sjulian uint32_t present = le32toh(rh->it_present); 334182734Sjulian int off, i; 335182734Sjulian 336182734Sjulian off = sizeof(struct ieee80211_radiotap_header); 337182734Sjulian for (i = 0; i < IEEE80211_RADIOTAP_EXT; i++) { 338182734Sjulian if ((present & (1<<i)) == 0) 339182734Sjulian continue; 340215800Szec if (items[i].align == 0) { 341182734Sjulian /* NB: unidentified element, don't guess */ 342182734Sjulian printf("%s: unknown item %d\n", __func__, i); 343182734Sjulian return -1; 344182734Sjulian } 345182734Sjulian off = roundup2(off, items[i].align); 346182734Sjulian if (i == item) { 347182734Sjulian if (off + items[i].width > le16toh(rh->it_len)) { 348182734Sjulian /* NB: item does not fit in header data */ 349182734Sjulian printf("%s: item %d not in header data, " 350182734Sjulian "off %d width %zu len %d\n", __func__, i, 351182734Sjulian off, items[i].width, le16toh(rh->it_len)); 352215800Szec return -1; 353182734Sjulian } 354182734Sjulian return off; 355182734Sjulian } 356182734Sjulian off += items[i].width; 357182734Sjulian } 358182734Sjulian return -1; 359182734Sjulian} 360182734Sjulian