ieee80211_radiotap.c revision 192765
1/*- 2 * Copyright (c) 2009 Sam Leffler, Errno Consulting 3 * All rights reserved. 4 * 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. 13 * 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. 24 */ 25 26#include <sys/cdefs.h> 27__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_radiotap.c 192765 2009-05-25 16:38:47Z sam $"); 28 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_llc.h> 46#include <net/if_media.h> 47 48#include <net80211/ieee80211_var.h> 49 50static int radiotap_offset(struct ieee80211_radiotap_header *, int); 51 52void 53ieee80211_radiotap_attach(struct ieee80211com *ic, 54 struct ieee80211_radiotap_header *th, int tlen, uint32_t tx_radiotap, 55 struct ieee80211_radiotap_header *rh, int rlen, uint32_t rx_radiotap) 56{ 57#define B(_v) (1<<(_v)) 58 int off; 59 60 th->it_len = htole16(roundup2(tlen, sizeof(uint32_t))); 61 th->it_present = htole32(tx_radiotap); 62 ic->ic_th = th; 63 /* calculate offset to channel data */ 64 off = -1; 65 if (tx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) 66 off = radiotap_offset(th, IEEE80211_RADIOTAP_CHANNEL); 67 else if (tx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) 68 off = radiotap_offset(th, IEEE80211_RADIOTAP_XCHANNEL); 69 if (off == -1) { 70 if_printf(ic->ic_ifp, "%s: no tx channel, radiotap 0x%x", 71 __func__, tx_radiotap); 72 /* NB: we handle this case but data will have no chan spec */ 73 } else 74 ic->ic_txchan = ((uint8_t *) th) + off; 75 76 rh->it_len = htole16(roundup2(rlen, sizeof(uint32_t))); 77 rh->it_present = htole32(rx_radiotap); 78 ic->ic_rh = rh; 79 /* calculate offset to channel data */ 80 off = -1; 81 if (rx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) 82 off = radiotap_offset(rh, IEEE80211_RADIOTAP_CHANNEL); 83 else if (rx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) 84 off = radiotap_offset(rh, IEEE80211_RADIOTAP_XCHANNEL); 85 if (off == -1) { 86 if_printf(ic->ic_ifp, "%s: no rx channel, radiotap 0x%x", 87 __func__, rx_radiotap); 88 /* NB: we handle this case but data will have no chan spec */ 89 } else 90 ic->ic_rxchan = ((uint8_t *) rh) + off; 91#undef B 92} 93 94void 95ieee80211_radiotap_detach(struct ieee80211com *ic) 96{ 97} 98 99void 100ieee80211_radiotap_vattach(struct ieee80211vap *vap) 101{ 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 171static void 172dispatch_radiotap(struct ieee80211vap *vap0, struct mbuf *m, 173 struct ieee80211_radiotap_header *rh) 174{ 175 struct ieee80211com *ic = vap0->iv_ic; 176 int len = le16toh(rh->it_len); 177 178 if (ieee80211_radiotap_active_vap(vap0)) 179 bpf_mtap2(vap0->iv_rawbpf, rh, len, m); 180 if (ic->ic_monvaps) { 181 struct ieee80211vap *vap; 182 TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 183 if (vap->iv_opmode == IEEE80211_M_MONITOR && 184 vap != vap0 && ieee80211_radiotap_active_vap(vap)) 185 bpf_mtap2(vap->iv_rawbpf, rh, len, m); 186 } 187 } 188} 189 190/* 191 * Dispatch radiotap data for transmitted packet. 192 */ 193void 194ieee80211_radiotap_tx(struct ieee80211vap *vap0, struct mbuf *m) 195{ 196 KASSERT(vap0->iv_ic->ic_th != NULL, ("no tx radiotap header")); 197 dispatch_radiotap(vap0, m, vap0->iv_ic->ic_th); 198} 199 200/* 201 * Dispatch radiotap data for received packet. 202 */ 203void 204ieee80211_radiotap_rx(struct ieee80211vap *vap0, struct mbuf *m) 205{ 206 KASSERT(vap0->iv_ic->ic_rh != NULL, ("no rx radiotap header")); 207 dispatch_radiotap(vap0, m, vap0->iv_ic->ic_rh); 208} 209 210/* 211 * Dispatch radiotap data for a packet received outside the normal 212 * rx processing path; this is used, for example, to handle frames 213 * received with errors that would otherwise be dropped. 214 */ 215void 216ieee80211_radiotap_rx_all(struct ieee80211com *ic, struct mbuf *m) 217{ 218 struct ieee80211_radiotap_header *rh = ic->ic_rh; 219 int len = le16toh(rh->it_len); 220 struct ieee80211vap *vap; 221 222 /* XXX locking? */ 223 TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 224 if (ieee80211_radiotap_active_vap(vap)) 225 bpf_mtap2(vap->iv_rawbpf, rh, len, m); 226 } 227} 228 229/* 230 * Return the offset of the specified item in the radiotap 231 * header description. If the item is not present or is not 232 * known -1 is returned. 233 */ 234static int 235radiotap_offset(struct ieee80211_radiotap_header *rh, int item) 236{ 237 static const struct { 238 size_t align, width; 239 } items[] = { 240 [IEEE80211_RADIOTAP_TSFT] = { 241 .align = sizeof(uint64_t), 242 .width = sizeof(uint64_t), 243 }, 244 [IEEE80211_RADIOTAP_FLAGS] = { 245 .align = sizeof(uint8_t), 246 .width = sizeof(uint8_t), 247 }, 248 [IEEE80211_RADIOTAP_RATE] = { 249 .align = sizeof(uint8_t), 250 .width = sizeof(uint8_t), 251 }, 252 [IEEE80211_RADIOTAP_CHANNEL] = { 253 .align = sizeof(uint16_t), 254 .width = 2*sizeof(uint16_t), 255 }, 256 [IEEE80211_RADIOTAP_FHSS] = { 257 .align = sizeof(uint16_t), 258 .width = sizeof(uint16_t), 259 }, 260 [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { 261 .align = sizeof(uint8_t), 262 .width = sizeof(uint8_t), 263 }, 264 [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { 265 .align = sizeof(uint8_t), 266 .width = sizeof(uint8_t), 267 }, 268 [IEEE80211_RADIOTAP_LOCK_QUALITY] = { 269 .align = sizeof(uint16_t), 270 .width = sizeof(uint16_t), 271 }, 272 [IEEE80211_RADIOTAP_TX_ATTENUATION] = { 273 .align = sizeof(uint16_t), 274 .width = sizeof(uint16_t), 275 }, 276 [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { 277 .align = sizeof(uint16_t), 278 .width = sizeof(uint16_t), 279 }, 280 [IEEE80211_RADIOTAP_DBM_TX_POWER] = { 281 .align = sizeof(uint8_t), 282 .width = sizeof(uint8_t), 283 }, 284 [IEEE80211_RADIOTAP_ANTENNA] = { 285 .align = sizeof(uint8_t), 286 .width = sizeof(uint8_t), 287 }, 288 [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { 289 .align = sizeof(uint8_t), 290 .width = sizeof(uint8_t), 291 }, 292 [IEEE80211_RADIOTAP_DB_ANTNOISE] = { 293 .align = sizeof(uint8_t), 294 .width = sizeof(uint8_t), 295 }, 296 [IEEE80211_RADIOTAP_XCHANNEL] = { 297 .align = sizeof(uint32_t), 298 .width = 2*sizeof(uint32_t), 299 }, 300 }; 301 uint32_t present = le32toh(rh->it_present); 302 int off, i; 303 304 off = sizeof(struct ieee80211_radiotap_header); 305 for (i = 0; i < IEEE80211_RADIOTAP_EXT; i++) { 306 if ((present & (1<<i)) == 0) 307 continue; 308 if (items[i].align == 0) { 309 /* NB: unidentified element, don't guess */ 310 printf("%s: unknown item %d\n", __func__, i); 311 return -1; 312 } 313 off = roundup2(off, items[i].align); 314 if (i == item) { 315 if (off + items[i].width > le16toh(rh->it_len)) { 316 /* NB: item does not fit in header data */ 317 printf("%s: item %d not in header data, " 318 "off %d width %zu len %d\n", __func__, i, 319 off, items[i].width, le16toh(rh->it_len)); 320 return -1; 321 } 322 return off; 323 } 324 off += items[i].width; 325 } 326 return -1; 327} 328