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 * $FreeBSD: head/sys/net80211/ieee80211_radiotap.c 193761 2009-06-08 21:16:06Z sam $ 26 * $DragonFly$ 27 */ 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 #include <net/route.h> 48 49 #include <netproto/802_11/ieee80211_var.h> 50 51 static int radiotap_offset(struct ieee80211_radiotap_header *, int); 52 53 void 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 #define B(_v) (1<<(_v)) 59 int off; 60 61 th->it_len = htole16(roundup2(tlen, sizeof(uint32_t))); 62 th->it_present = htole32(tx_radiotap); 63 ic->ic_th = th; 64 /* calculate offset to channel data */ 65 off = -1; 66 if (tx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) 67 off = radiotap_offset(th, IEEE80211_RADIOTAP_CHANNEL); 68 else if (tx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) 69 off = radiotap_offset(th, IEEE80211_RADIOTAP_XCHANNEL); 70 if (off == -1) { 71 if_printf(ic->ic_ifp, "%s: no tx channel, radiotap 0x%x", 72 __func__, tx_radiotap); 73 /* NB: we handle this case but data will have no chan spec */ 74 } else 75 ic->ic_txchan = ((uint8_t *) th) + off; 76 77 rh->it_len = htole16(roundup2(rlen, sizeof(uint32_t))); 78 rh->it_present = htole32(rx_radiotap); 79 ic->ic_rh = rh; 80 /* calculate offset to channel data */ 81 off = -1; 82 if (rx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) 83 off = radiotap_offset(rh, IEEE80211_RADIOTAP_CHANNEL); 84 else if (rx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) 85 off = radiotap_offset(rh, IEEE80211_RADIOTAP_XCHANNEL); 86 if (off == -1) { 87 if_printf(ic->ic_ifp, "%s: no rx channel, radiotap 0x%x", 88 __func__, rx_radiotap); 89 /* NB: we handle this case but data will have no chan spec */ 90 } else 91 ic->ic_rxchan = ((uint8_t *) rh) + off; 92 #undef B 93 } 94 95 void 96 ieee80211_radiotap_detach(struct ieee80211com *ic) 97 { 98 } 99 100 void 101 ieee80211_radiotap_vattach(struct ieee80211vap *vap) 102 { 103 struct ieee80211com *ic = vap->iv_ic; 104 struct ieee80211_radiotap_header *th = ic->ic_th; 105 106 if (th != NULL && ic->ic_rh != NULL) { 107 /* radiotap DLT for raw 802.11 frames */ 108 bpfattach_dlt(vap->iv_ifp, DLT_IEEE802_11_RADIO, 109 sizeof(struct ieee80211_frame) + le16toh(th->it_len), 110 &vap->iv_rawbpf); 111 } 112 } 113 114 void 115 ieee80211_radiotap_vdetach(struct ieee80211vap *vap) 116 { 117 /* NB: bpfattach is called by ether_ifdetach and claims all taps */ 118 } 119 120 static void 121 set_channel(void *p, const struct ieee80211_channel *c) 122 { 123 struct { 124 uint16_t freq; 125 uint16_t flags; 126 } *rc = p; 127 128 rc->freq = htole16(c->ic_freq); 129 rc->flags = htole16(c->ic_flags); 130 } 131 132 static void 133 set_xchannel(void *p, const struct ieee80211_channel *c) 134 { 135 struct { 136 uint32_t flags; 137 uint16_t freq; 138 uint8_t ieee; 139 uint8_t maxpow; 140 } *rc = p; 141 142 rc->flags = htole32(c->ic_flags); 143 rc->freq = htole16(c->ic_freq); 144 rc->ieee = c->ic_ieee; 145 rc->maxpow = c->ic_maxregpower; 146 } 147 148 /* 149 * Update radiotap state on channel change. 150 */ 151 void 152 ieee80211_radiotap_chan_change(struct ieee80211com *ic) 153 { 154 if (ic->ic_rxchan != NULL) { 155 struct ieee80211_radiotap_header *rh = ic->ic_rh; 156 157 if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL)) 158 set_xchannel(ic->ic_rxchan, ic->ic_curchan); 159 else if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL)) 160 set_channel(ic->ic_rxchan, ic->ic_curchan); 161 } 162 if (ic->ic_txchan != NULL) { 163 struct ieee80211_radiotap_header *th = ic->ic_th; 164 165 if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL)) 166 set_xchannel(ic->ic_txchan, ic->ic_curchan); 167 else if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL)) 168 set_channel(ic->ic_txchan, ic->ic_curchan); 169 } 170 } 171 172 /* 173 * Distribute radiotap data (+packet) to all monitor mode 174 * vaps with an active tap other than vap0. 175 */ 176 static void 177 spam_vaps(struct ieee80211vap *vap0, struct mbuf *m, 178 struct ieee80211_radiotap_header *rh, int len) 179 { 180 struct ieee80211com *ic = vap0->iv_ic; 181 struct ieee80211vap *vap; 182 183 TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 184 if (vap != vap0 && 185 vap->iv_opmode == IEEE80211_M_MONITOR && 186 (vap->iv_flags_ext & IEEE80211_FEXT_BPF) && 187 vap->iv_state != IEEE80211_S_INIT) 188 bpf_ptap(vap->iv_rawbpf, m, rh, len); 189 } 190 } 191 192 /* 193 * Dispatch radiotap data for transmitted packet. 194 */ 195 void 196 ieee80211_radiotap_tx(struct ieee80211vap *vap0, struct mbuf *m) 197 { 198 struct ieee80211com *ic = vap0->iv_ic; 199 struct ieee80211_radiotap_header *th = ic->ic_th; 200 int len; 201 202 KASSERT(th != NULL, ("no tx radiotap header")); 203 len = le16toh(th->it_len); 204 205 if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF) 206 bpf_ptap(vap0->iv_rawbpf, m, th, len); 207 /* 208 * Spam monitor mode vaps. 209 */ 210 if (ic->ic_montaps != 0) 211 spam_vaps(vap0, m, th, len); 212 } 213 214 /* 215 * Dispatch radiotap data for received packet. 216 */ 217 void 218 ieee80211_radiotap_rx(struct ieee80211vap *vap0, struct mbuf *m) 219 { 220 struct ieee80211com *ic = vap0->iv_ic; 221 struct ieee80211_radiotap_header *rh = ic->ic_rh; 222 int len; 223 224 KASSERT(rh != NULL, ("no rx radiotap header")); 225 len = le16toh(rh->it_len); 226 227 if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF) 228 bpf_ptap(vap0->iv_rawbpf, m, rh, len); 229 /* 230 * Spam monitor mode vaps with unicast frames. Multicast 231 * frames are handled by passing through ieee80211_input_all 232 * which distributes copies to the monitor mode vaps. 233 */ 234 if (ic->ic_montaps != 0 && (m->m_flags & M_BCAST) == 0) 235 spam_vaps(vap0, m, rh, len); 236 } 237 238 /* 239 * Dispatch radiotap data for a packet received outside the normal 240 * rx processing path; this is used, for example, to handle frames 241 * received with errors that would otherwise be dropped. 242 */ 243 void 244 ieee80211_radiotap_rx_all(struct ieee80211com *ic, struct mbuf *m) 245 { 246 struct ieee80211_radiotap_header *rh = ic->ic_rh; 247 int len = le16toh(rh->it_len); 248 struct ieee80211vap *vap; 249 250 /* XXX locking? */ 251 TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 252 if (ieee80211_radiotap_active_vap(vap) && 253 vap->iv_state != IEEE80211_S_INIT) 254 bpf_ptap(vap->iv_rawbpf, m, rh, len); 255 } 256 } 257 258 /* 259 * Return the offset of the specified item in the radiotap 260 * header description. If the item is not present or is not 261 * known -1 is returned. 262 */ 263 static int 264 radiotap_offset(struct ieee80211_radiotap_header *rh, int item) 265 { 266 static const struct { 267 size_t align, width; 268 } items[] = { 269 [IEEE80211_RADIOTAP_TSFT] = { 270 .align = sizeof(uint64_t), 271 .width = sizeof(uint64_t), 272 }, 273 [IEEE80211_RADIOTAP_FLAGS] = { 274 .align = sizeof(uint8_t), 275 .width = sizeof(uint8_t), 276 }, 277 [IEEE80211_RADIOTAP_RATE] = { 278 .align = sizeof(uint8_t), 279 .width = sizeof(uint8_t), 280 }, 281 [IEEE80211_RADIOTAP_CHANNEL] = { 282 .align = sizeof(uint16_t), 283 .width = 2*sizeof(uint16_t), 284 }, 285 [IEEE80211_RADIOTAP_FHSS] = { 286 .align = sizeof(uint16_t), 287 .width = sizeof(uint16_t), 288 }, 289 [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { 290 .align = sizeof(uint8_t), 291 .width = sizeof(uint8_t), 292 }, 293 [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { 294 .align = sizeof(uint8_t), 295 .width = sizeof(uint8_t), 296 }, 297 [IEEE80211_RADIOTAP_LOCK_QUALITY] = { 298 .align = sizeof(uint16_t), 299 .width = sizeof(uint16_t), 300 }, 301 [IEEE80211_RADIOTAP_TX_ATTENUATION] = { 302 .align = sizeof(uint16_t), 303 .width = sizeof(uint16_t), 304 }, 305 [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { 306 .align = sizeof(uint16_t), 307 .width = sizeof(uint16_t), 308 }, 309 [IEEE80211_RADIOTAP_DBM_TX_POWER] = { 310 .align = sizeof(uint8_t), 311 .width = sizeof(uint8_t), 312 }, 313 [IEEE80211_RADIOTAP_ANTENNA] = { 314 .align = sizeof(uint8_t), 315 .width = sizeof(uint8_t), 316 }, 317 [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { 318 .align = sizeof(uint8_t), 319 .width = sizeof(uint8_t), 320 }, 321 [IEEE80211_RADIOTAP_DB_ANTNOISE] = { 322 .align = sizeof(uint8_t), 323 .width = sizeof(uint8_t), 324 }, 325 [IEEE80211_RADIOTAP_XCHANNEL] = { 326 .align = sizeof(uint32_t), 327 .width = 2*sizeof(uint32_t), 328 }, 329 }; 330 uint32_t present = le32toh(rh->it_present); 331 int off, i; 332 333 off = sizeof(struct ieee80211_radiotap_header); 334 for (i = 0; i < IEEE80211_RADIOTAP_EXT; i++) { 335 if ((present & (1<<i)) == 0) 336 continue; 337 if (items[i].align == 0) { 338 /* NB: unidentified element, don't guess */ 339 kprintf("%s: unknown item %d\n", __func__, i); 340 return -1; 341 } 342 off = roundup2(off, items[i].align); 343 if (i == item) { 344 if (off + items[i].width > le16toh(rh->it_len)) { 345 /* NB: item does not fit in header data */ 346 kprintf("%s: item %d not in header data, " 347 "off %d width %zu len %d\n", __func__, i, 348 off, items[i].width, le16toh(rh->it_len)); 349 return -1; 350 } 351 return off; 352 } 353 off += items[i].width; 354 } 355 return -1; 356 } 357