| Commit | Line | Data |
|---|---|---|
| 32176cfd | 1 | /*- |
| f186073c | 2 | * Copyright (c) 2001 Atsushi Onoe |
| 32176cfd | 3 | * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting |
| f186073c JS |
4 | * All rights reserved. |
| 5 | * | |
| 6 | * Redistribution and use in source and binary forms, with or without | |
| 7 | * modification, are permitted provided that the following conditions | |
| 8 | * are met: | |
| 9 | * 1. Redistributions of source code must retain the above copyright | |
| 10 | * notice, this list of conditions and the following disclaimer. | |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer in the | |
| 13 | * documentation and/or other materials provided with the distribution. | |
| f186073c JS |
14 | * |
| 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
| 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
| 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
| 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
| 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
| 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 25 | * | |
| 4028af95 | 26 | * $FreeBSD: head/sys/net80211/ieee80211_input.c 205986 2010-03-31 16:07:36Z rpaulo $ |
| 32176cfd | 27 | * $DragonFly$ |
| f186073c JS |
28 | */ |
| 29 | ||
| 32176cfd RP |
30 | #include "opt_wlan.h" |
| 31 | ||
| f186073c | 32 | #include <sys/param.h> |
| 841ab66c | 33 | #include <sys/systm.h> |
| f186073c JS |
34 | #include <sys/mbuf.h> |
| 35 | #include <sys/malloc.h> | |
| 841ab66c | 36 | #include <sys/endian.h> |
| f186073c | 37 | #include <sys/kernel.h> |
| 841ab66c | 38 | |
| f186073c | 39 | #include <sys/socket.h> |
| 32176cfd | 40 | |
| f186073c | 41 | #include <net/ethernet.h> |
| 32176cfd | 42 | #include <net/if.h> |
| f186073c | 43 | #include <net/if_llc.h> |
| 32176cfd RP |
44 | #include <net/if_media.h> |
| 45 | #include <net/route.h> | |
| f186073c JS |
46 | |
| 47 | #include <netproto/802_11/ieee80211_var.h> | |
| 32176cfd RP |
48 | #include <netproto/802_11/ieee80211_input.h> |
| 49 | #ifdef IEEE80211_SUPPORT_MESH | |
| 50 | #include <netproto/802_11/ieee80211_mesh.h> | |
| 51 | #endif | |
| f186073c JS |
52 | |
| 53 | #include <net/bpf.h> | |
| 54 | ||
| 32176cfd RP |
55 | #ifdef INET |
| 56 | #include <netinet/in.h> | |
| 57 | #include <net/ethernet.h> | |
| 58 | #endif | |
| 6bd66811 SZ |
59 | |
| 60 | int | |
| 32176cfd | 61 | ieee80211_input_all(struct ieee80211com *ic, struct mbuf *m, int rssi, int nf) |
| 6bd66811 | 62 | { |
| 32176cfd RP |
63 | struct ieee80211vap *vap; |
| 64 | int type = -1; | |
| 841ab66c | 65 | |
| 32176cfd | 66 | m->m_flags |= M_BCAST; /* NB: mark for bpf tap'ing */ |
| 841ab66c | 67 | |
| 32176cfd RP |
68 | /* XXX locking */ |
| 69 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { | |
| 70 | struct ieee80211_node *ni; | |
| 71 | struct mbuf *mcopy; | |
| 1f8e62c9 | 72 | |
| 32176cfd RP |
73 | /* NB: could check for IFF_UP but this is cheaper */ |
| 74 | if (vap->iv_state == IEEE80211_S_INIT) | |
| 75 | continue; | |
| 841ab66c | 76 | /* |
| 32176cfd RP |
77 | * WDS vap's only receive directed traffic from the |
| 78 | * station at the ``far end''. That traffic should | |
| 79 | * be passed through the AP vap the station is associated | |
| 80 | * to--so don't spam them with mcast frames. | |
| 841ab66c | 81 | */ |
| 32176cfd RP |
82 | if (vap->iv_opmode == IEEE80211_M_WDS) |
| 83 | continue; | |
| 84 | if (TAILQ_NEXT(vap, iv_next) != NULL) { | |
| 841ab66c | 85 | /* |
| 32176cfd RP |
86 | * Packet contents are changed by ieee80211_decap |
| 87 | * so do a deep copy of the packet. | |
| 841ab66c | 88 | */ |
| 32176cfd RP |
89 | mcopy = m_dup(m, MB_DONTWAIT); |
| 90 | if (mcopy == NULL) { | |
| 91 | /* XXX stat+msg */ | |
| 92 | continue; | |
| f186073c | 93 | } |
| 841ab66c | 94 | } else { |
| 32176cfd RP |
95 | mcopy = m; |
| 96 | m = NULL; | |
| 841ab66c | 97 | } |
| 32176cfd RP |
98 | ni = ieee80211_ref_node(vap->iv_bss); |
| 99 | type = ieee80211_input(ni, mcopy, rssi, nf); | |
| 100 | ieee80211_free_node(ni); | |
| f186073c | 101 | } |
| 32176cfd | 102 | if (m != NULL) /* no vaps, reclaim mbuf */ |
| 841ab66c | 103 | m_freem(m); |
| 841ab66c | 104 | return type; |
| 841ab66c SZ |
105 | } |
| 106 | ||
| 107 | /* | |
| 32176cfd RP |
108 | * This function reassembles fragments. |
| 109 | * | |
| 110 | * XXX should handle 3 concurrent reassemblies per-spec. | |
| 841ab66c | 111 | */ |
| 32176cfd RP |
112 | struct mbuf * |
| 113 | ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace) | |
| 841ab66c | 114 | { |
| 32176cfd | 115 | struct ieee80211vap *vap = ni->ni_vap; |
| 841ab66c SZ |
116 | struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); |
| 117 | struct ieee80211_frame *lwh; | |
| 118 | uint16_t rxseq; | |
| 119 | uint8_t fragno; | |
| 120 | uint8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG; | |
| 121 | struct mbuf *mfrag; | |
| 122 | ||
| 123 | KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?")); | |
| 124 | ||
| 125 | rxseq = le16toh(*(uint16_t *)wh->i_seq); | |
| 126 | fragno = rxseq & IEEE80211_SEQ_FRAG_MASK; | |
| 127 | ||
| 128 | /* Quick way out, if there's nothing to defragment */ | |
| 129 | if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL) | |
| 130 | return m; | |
| 131 | ||
| 132 | /* | |
| 133 | * Remove frag to insure it doesn't get reaped by timer. | |
| 134 | */ | |
| 135 | if (ni->ni_table == NULL) { | |
| 136 | /* | |
| 137 | * Should never happen. If the node is orphaned (not in | |
| 138 | * the table) then input packets should not reach here. | |
| 139 | * Otherwise, a concurrent request that yanks the table | |
| 140 | * should be blocked by other interlocking and/or by first | |
| 141 | * shutting the driver down. Regardless, be defensive | |
| 142 | * here and just bail | |
| 143 | */ | |
| 144 | /* XXX need msg+stat */ | |
| 145 | m_freem(m); | |
| 146 | return NULL; | |
| 147 | } | |
| 148 | mfrag = ni->ni_rxfrag[0]; | |
| 149 | ni->ni_rxfrag[0] = NULL; | |
| 150 | ||
| 151 | /* | |
| 152 | * Validate new fragment is in order and | |
| 153 | * related to the previous ones. | |
| 154 | */ | |
| 155 | if (mfrag != NULL) { | |
| 156 | uint16_t last_rxseq; | |
| 157 | ||
| 158 | lwh = mtod(mfrag, struct ieee80211_frame *); | |
| 159 | last_rxseq = le16toh(*(uint16_t *)lwh->i_seq); | |
| 160 | /* NB: check seq # and frag together */ | |
| 161 | if (rxseq != last_rxseq+1 || | |
| 162 | !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) || | |
| 163 | !IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) { | |
| 164 | /* | |
| 165 | * Unrelated fragment or no space for it, | |
| 166 | * clear current fragments. | |
| 167 | */ | |
| 168 | m_freem(mfrag); | |
| 169 | mfrag = NULL; | |
| 170 | } | |
| 171 | } | |
| 172 | ||
| 173 | if (mfrag == NULL) { | |
| 174 | if (fragno != 0) { /* !first fragment, discard */ | |
| 32176cfd | 175 | vap->iv_stats.is_rx_defrag++; |
| 841ab66c SZ |
176 | IEEE80211_NODE_STAT(ni, rx_defrag); |
| 177 | m_freem(m); | |
| 178 | return NULL; | |
| 179 | } | |
| 180 | mfrag = m; | |
| 181 | } else { /* concatenate */ | |
| 182 | m_adj(m, hdrspace); /* strip header */ | |
| 183 | m_cat(mfrag, m); | |
| 184 | /* NB: m_cat doesn't update the packet header */ | |
| 185 | mfrag->m_pkthdr.len += m->m_pkthdr.len; | |
| 186 | /* track last seqnum and fragno */ | |
| 187 | lwh = mtod(mfrag, struct ieee80211_frame *); | |
| 188 | *(uint16_t *) lwh->i_seq = *(uint16_t *) wh->i_seq; | |
| 189 | } | |
| 190 | if (more_frag) { /* more to come, save */ | |
| 191 | ni->ni_rxfragstamp = ticks; | |
| 192 | ni->ni_rxfrag[0] = mfrag; | |
| 193 | mfrag = NULL; | |
| 194 | } | |
| 195 | return mfrag; | |
| 196 | } | |
| 197 | ||
| 32176cfd RP |
198 | void |
| 199 | ieee80211_deliver_data(struct ieee80211vap *vap, | |
| 841ab66c SZ |
200 | struct ieee80211_node *ni, struct mbuf *m) |
| 201 | { | |
| 202 | struct ether_header *eh = mtod(m, struct ether_header *); | |
| 32176cfd | 203 | struct ifnet *ifp = vap->iv_ifp; |
| 841ab66c | 204 | |
| 32176cfd RP |
205 | /* clear driver/net80211 flags before passing up */ |
| 206 | m->m_flags &= ~(M_80211_RX | M_MCAST | M_BCAST); | |
| 207 | ||
| 208 | /* NB: see hostap_deliver_data, this path doesn't handle hostap */ | |
| 209 | KASSERT(vap->iv_opmode != IEEE80211_M_HOSTAP, ("gack, hostap")); | |
| ffd45d58 SZ |
210 | /* |
| 211 | * Do accounting. | |
| 212 | */ | |
| 213 | ifp->if_ipackets++; | |
| 214 | IEEE80211_NODE_STAT(ni, rx_data); | |
| 215 | IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len); | |
| 216 | if (ETHER_IS_MULTICAST(eh->ether_dhost)) { | |
| 32176cfd | 217 | m->m_flags |= M_MCAST; /* XXX M_BCAST? */ |
| ffd45d58 | 218 | IEEE80211_NODE_STAT(ni, rx_mcast); |
| 32176cfd | 219 | } else |
| ffd45d58 | 220 | IEEE80211_NODE_STAT(ni, rx_ucast); |
| 32176cfd | 221 | m->m_pkthdr.rcvif = ifp; |
| 841ab66c | 222 | |
| 32176cfd RP |
223 | if (ni->ni_vlan != 0) { |
| 224 | /* attach vlan tag */ | |
| 225 | m->m_pkthdr.ether_vlantag = ni->ni_vlan; | |
| 226 | m->m_flags |= M_VLANTAG; | |
| f186073c | 227 | } |
| 32176cfd | 228 | ifp->if_input(ifp, m); |
| f186073c JS |
229 | } |
| 230 | ||
| 32176cfd RP |
231 | struct mbuf * |
| 232 | ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen) | |
| f186073c | 233 | { |
| 32176cfd | 234 | struct ieee80211_qosframe_addr4 wh; |
| f186073c | 235 | struct ether_header *eh; |
| f186073c JS |
236 | struct llc *llc; |
| 237 | ||
| 32176cfd RP |
238 | KASSERT(hdrlen <= sizeof(wh), |
| 239 | ("hdrlen %d > max %zd", hdrlen, sizeof(wh))); | |
| 240 | ||
| 841ab66c SZ |
241 | if (m->m_len < hdrlen + sizeof(*llc) && |
| 242 | (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) { | |
| 32176cfd RP |
243 | vap->iv_stats.is_rx_tooshort++; |
| 244 | /* XXX msg */ | |
| 841ab66c | 245 | return NULL; |
| f186073c | 246 | } |
| 841ab66c SZ |
247 | memcpy(&wh, mtod(m, caddr_t), hdrlen); |
| 248 | llc = (struct llc *)(mtod(m, caddr_t) + hdrlen); | |
| f186073c JS |
249 | if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP && |
| 250 | llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 && | |
| 32176cfd RP |
251 | llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0 && |
| 252 | /* NB: preserve AppleTalk frames that have a native SNAP hdr */ | |
| 253 | !(llc->llc_snap.ether_type == htons(ETHERTYPE_AARP) || | |
| 254 | llc->llc_snap.ether_type == htons(ETHERTYPE_IPX))) { | |
| 841ab66c | 255 | m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh)); |
| f186073c JS |
256 | llc = NULL; |
| 257 | } else { | |
| 841ab66c | 258 | m_adj(m, hdrlen - sizeof(*eh)); |
| f186073c JS |
259 | } |
| 260 | eh = mtod(m, struct ether_header *); | |
| 261 | switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) { | |
| 262 | case IEEE80211_FC1_DIR_NODS: | |
| 263 | IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); | |
| 264 | IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); | |
| 265 | break; | |
| 266 | case IEEE80211_FC1_DIR_TODS: | |
| 267 | IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3); | |
| 268 | IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); | |
| 269 | break; | |
| 270 | case IEEE80211_FC1_DIR_FROMDS: | |
| 271 | IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); | |
| 272 | IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3); | |
| 273 | break; | |
| 274 | case IEEE80211_FC1_DIR_DSTODS: | |
| 841ab66c SZ |
275 | IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3); |
| 276 | IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr4); | |
| 277 | break; | |
| f186073c JS |
278 | } |
| 279 | #ifdef ALIGNED_POINTER | |
| 280 | if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) { | |
| 32176cfd RP |
281 | m = ieee80211_realign(vap, m, sizeof(*eh)); |
| 282 | if (m == NULL) | |
| 283 | return NULL; | |
| f186073c JS |
284 | } |
| 285 | #endif /* ALIGNED_POINTER */ | |
| 286 | if (llc != NULL) { | |
| 287 | eh = mtod(m, struct ether_header *); | |
| 288 | eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh)); | |
| 289 | } | |
| 290 | return m; | |
| 291 | } | |
| 292 | ||
| 293 | /* | |
| 32176cfd RP |
294 | * Decap a frame encapsulated in a fast-frame/A-MSDU. |
| 295 | */ | |
| 296 | struct mbuf * | |
| 297 | ieee80211_decap1(struct mbuf *m, int *framelen) | |
| 298 | { | |
| 299 | #define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) | |
| 300 | struct ether_header *eh; | |
| 301 | struct llc *llc; | |
| 302 | ||
| 303 | /* | |
| 304 | * The frame has an 802.3 header followed by an 802.2 | |
| 305 | * LLC header. The encapsulated frame length is in the | |
| 306 | * first header type field; save that and overwrite it | |
| 307 | * with the true type field found in the second. Then | |
| 308 | * copy the 802.3 header up to where it belongs and | |
| 309 | * adjust the mbuf contents to remove the void. | |
| 310 | */ | |
| 311 | if (m->m_len < FF_LLC_SIZE && (m = m_pullup(m, FF_LLC_SIZE)) == NULL) | |
| 312 | return NULL; | |
| 313 | eh = mtod(m, struct ether_header *); /* 802.3 header is first */ | |
| 314 | llc = (struct llc *)&eh[1]; /* 802.2 header follows */ | |
| 315 | *framelen = ntohs(eh->ether_type) /* encap'd frame size */ | |
| 316 | + sizeof(struct ether_header) - sizeof(struct llc); | |
| 317 | eh->ether_type = llc->llc_un.type_snap.ether_type; | |
| 318 | ovbcopy(eh, mtod(m, uint8_t *) + sizeof(struct llc), | |
| 319 | sizeof(struct ether_header)); | |
| 320 | m_adj(m, sizeof(struct llc)); | |
| 321 | return m; | |
| 322 | #undef FF_LLC_SIZE | |
| 323 | } | |
| 324 | ||
| 325 | /* | |
| f186073c JS |
326 | * Install received rate set information in the node's state block. |
| 327 | */ | |
| 841ab66c SZ |
328 | int |
| 329 | ieee80211_setup_rates(struct ieee80211_node *ni, | |
| 32176cfd | 330 | const uint8_t *rates, const uint8_t *xrates, int flags) |
| f186073c | 331 | { |
| 32176cfd | 332 | struct ieee80211vap *vap = ni->ni_vap; |
| f186073c JS |
333 | struct ieee80211_rateset *rs = &ni->ni_rates; |
| 334 | ||
| 335 | memset(rs, 0, sizeof(*rs)); | |
| 336 | rs->rs_nrates = rates[1]; | |
| 337 | memcpy(rs->rs_rates, rates + 2, rs->rs_nrates); | |
| 338 | if (xrates != NULL) { | |
| 339 | uint8_t nxrates; | |
| 340 | /* | |
| 341 | * Tack on 11g extended supported rate element. | |
| 342 | */ | |
| 343 | nxrates = xrates[1]; | |
| 344 | if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) { | |
| 345 | nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates; | |
| 32176cfd RP |
346 | IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE, ni, |
| 347 | "extended rate set too large; only using " | |
| 348 | "%u of %u rates", nxrates, xrates[1]); | |
| 349 | vap->iv_stats.is_rx_rstoobig++; | |
| f186073c JS |
350 | } |
| 351 | memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates); | |
| 352 | rs->rs_nrates += nxrates; | |
| 353 | } | |
| 32176cfd | 354 | return ieee80211_fix_rate(ni, rs, flags); |
| 841ab66c SZ |
355 | } |
| 356 | ||
| 357 | /* | |
| 358 | * Send a management frame error response to the specified | |
| 359 | * station. If ni is associated with the station then use | |
| 360 | * it; otherwise allocate a temporary node suitable for | |
| 361 | * transmitting the frame and then free the reference so | |
| 362 | * it will go away as soon as the frame has been transmitted. | |
| 363 | */ | |
| 32176cfd RP |
364 | void |
| 365 | ieee80211_send_error(struct ieee80211_node *ni, | |
| 366 | const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg) | |
| 841ab66c | 367 | { |
| 32176cfd | 368 | struct ieee80211vap *vap = ni->ni_vap; |
| 841ab66c SZ |
369 | int istmp; |
| 370 | ||
| 32176cfd RP |
371 | if (ni == vap->iv_bss) { |
| 372 | if (vap->iv_state != IEEE80211_S_RUN) { | |
| 373 | /* | |
| 374 | * XXX hack until we get rid of this routine. | |
| 375 | * We can be called prior to the vap reaching | |
| 376 | * run state under certain conditions in which | |
| 377 | * case iv_bss->ni_chan will not be setup. | |
| 378 | * Check for this explicitly and and just ignore | |
| 379 | * the request. | |
| 380 | */ | |
| 381 | return; | |
| 382 | } | |
| 383 | ni = ieee80211_tmp_node(vap, mac); | |
| 841ab66c SZ |
384 | if (ni == NULL) { |
| 385 | /* XXX msg */ | |
| 386 | return; | |
| 387 | } | |
| 388 | istmp = 1; | |
| 389 | } else | |
| 390 | istmp = 0; | |
| 32176cfd | 391 | IEEE80211_SEND_MGMT(ni, subtype, arg); |
| 841ab66c SZ |
392 | if (istmp) |
| 393 | ieee80211_free_node(ni); | |
| 394 | } | |
| 395 | ||
| 32176cfd RP |
396 | int |
| 397 | ieee80211_alloc_challenge(struct ieee80211_node *ni) | |
| 841ab66c SZ |
398 | { |
| 399 | if (ni->ni_challenge == NULL) | |
| 32176cfd | 400 | ni->ni_challenge = (uint32_t *) kmalloc(IEEE80211_CHALLENGE_LEN, |
| fcaa651d | 401 | M_80211_NODE, M_INTWAIT); |
| 841ab66c | 402 | if (ni->ni_challenge == NULL) { |
| 32176cfd RP |
403 | IEEE80211_NOTE(ni->ni_vap, |
| 404 | IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni, | |
| 405 | "%s", "shared key challenge alloc failed"); | |
| 841ab66c SZ |
406 | /* XXX statistic */ |
| 407 | } | |
| 408 | return (ni->ni_challenge != NULL); | |
| 409 | } | |
| 410 | ||
| 32176cfd RP |
411 | /* |
| 412 | * Parse a Beacon or ProbeResponse frame and return the | |
| 413 | * useful information in an ieee80211_scanparams structure. | |
| 414 | * Status is set to 0 if no problems were found; otherwise | |
| 415 | * a bitmask of IEEE80211_BPARSE_* items is returned that | |
| 416 | * describes the problems detected. | |
| 417 | */ | |
| 418 | int | |
| 419 | ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m, | |
| 420 | struct ieee80211_scanparams *scan) | |
| 841ab66c | 421 | { |
| 32176cfd RP |
422 | struct ieee80211vap *vap = ni->ni_vap; |
| 423 | struct ieee80211com *ic = ni->ni_ic; | |
| 424 | struct ieee80211_frame *wh; | |
| 425 | uint8_t *frm, *efrm; | |
| 841ab66c | 426 | |
| 32176cfd RP |
427 | wh = mtod(m, struct ieee80211_frame *); |
| 428 | frm = (uint8_t *)&wh[1]; | |
| 429 | efrm = mtod(m, uint8_t *) + m->m_len; | |
| 430 | scan->status = 0; | |
| 841ab66c | 431 | /* |
| 32176cfd RP |
432 | * beacon/probe response frame format |
| 433 | * [8] time stamp | |
| 434 | * [2] beacon interval | |
| 435 | * [2] capability information | |
| 436 | * [tlv] ssid | |
| 437 | * [tlv] supported rates | |
| 438 | * [tlv] country information | |
| 439 | * [tlv] channel switch announcement (CSA) | |
| 440 | * [tlv] parameter set (FH/DS) | |
| 441 | * [tlv] erp information | |
| 442 | * [tlv] extended supported rates | |
| 443 | * [tlv] WME | |
| 444 | * [tlv] WPA or RSN | |
| 445 | * [tlv] HT capabilities | |
| 446 | * [tlv] HT information | |
| 447 | * [tlv] Atheros capabilities | |
| 448 | * [tlv] Mesh ID | |
| 449 | * [tlv] Mesh Configuration | |
| 841ab66c | 450 | */ |
| 32176cfd RP |
451 | IEEE80211_VERIFY_LENGTH(efrm - frm, 12, |
| 452 | return (scan->status = IEEE80211_BPARSE_BADIELEN)); | |
| 453 | memset(scan, 0, sizeof(*scan)); | |
| 454 | scan->tstamp = frm; frm += 8; | |
| 455 | scan->bintval = le16toh(*(uint16_t *)frm); frm += 2; | |
| 456 | scan->capinfo = le16toh(*(uint16_t *)frm); frm += 2; | |
| 457 | scan->bchan = ieee80211_chan2ieee(ic, ic->ic_curchan); | |
| 458 | scan->chan = scan->bchan; | |
| 459 | scan->ies = frm; | |
| 460 | scan->ies_len = efrm - frm; | |
| 461 | ||
| 462 | while (efrm - frm > 1) { | |
| 463 | IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, | |
| 464 | return (scan->status = IEEE80211_BPARSE_BADIELEN)); | |
| 465 | switch (*frm) { | |
| 466 | case IEEE80211_ELEMID_SSID: | |
| 467 | scan->ssid = frm; | |
| 468 | break; | |
| 469 | case IEEE80211_ELEMID_RATES: | |
| 470 | scan->rates = frm; | |
| 471 | break; | |
| 472 | case IEEE80211_ELEMID_COUNTRY: | |
| 473 | scan->country = frm; | |
| 474 | break; | |
| 475 | case IEEE80211_ELEMID_CSA: | |
| 476 | scan->csa = frm; | |
| 477 | break; | |
| 478 | case IEEE80211_ELEMID_FHPARMS: | |
| 479 | if (ic->ic_phytype == IEEE80211_T_FH) { | |
| 480 | scan->fhdwell = LE_READ_2(&frm[2]); | |
| 481 | scan->chan = IEEE80211_FH_CHAN(frm[4], frm[5]); | |
| 482 | scan->fhindex = frm[6]; | |
| 841ab66c | 483 | } |
| 32176cfd RP |
484 | break; |
| 485 | case IEEE80211_ELEMID_DSPARMS: | |
| 841ab66c | 486 | /* |
| 32176cfd RP |
487 | * XXX hack this since depending on phytype |
| 488 | * is problematic for multi-mode devices. | |
| 841ab66c | 489 | */ |
| 32176cfd RP |
490 | if (ic->ic_phytype != IEEE80211_T_FH) |
| 491 | scan->chan = frm[2]; | |
| 841ab66c | 492 | break; |
| 32176cfd RP |
493 | case IEEE80211_ELEMID_TIM: |
| 494 | /* XXX ATIM? */ | |
| 495 | scan->tim = frm; | |
| 496 | scan->timoff = frm - mtod(m, uint8_t *); | |
| 497 | break; | |
| 498 | case IEEE80211_ELEMID_IBSSPARMS: | |
| 499 | case IEEE80211_ELEMID_CFPARMS: | |
| 500 | case IEEE80211_ELEMID_PWRCNSTR: | |
| 501 | /* NB: avoid debugging complaints */ | |
| 502 | break; | |
| 503 | case IEEE80211_ELEMID_XRATES: | |
| 504 | scan->xrates = frm; | |
| 505 | break; | |
| 506 | case IEEE80211_ELEMID_ERP: | |
| 507 | if (frm[1] != 1) { | |
| 508 | IEEE80211_DISCARD_IE(vap, | |
| 509 | IEEE80211_MSG_ELEMID, wh, "ERP", | |
| 510 | "bad len %u", frm[1]); | |
| 511 | vap->iv_stats.is_rx_elem_toobig++; | |
| 512 | break; | |
| 841ab66c | 513 | } |
| 32176cfd RP |
514 | scan->erp = frm[2] | 0x100; |
| 515 | break; | |
| 516 | case IEEE80211_ELEMID_HTCAP: | |
| 517 | scan->htcap = frm; | |
| 518 | break; | |
| 519 | case IEEE80211_ELEMID_RSN: | |
| 520 | scan->rsn = frm; | |
| 521 | break; | |
| 522 | case IEEE80211_ELEMID_HTINFO: | |
| 523 | scan->htinfo = frm; | |
| 524 | break; | |
| 525 | #ifdef IEEE80211_SUPPORT_MESH | |
| 526 | case IEEE80211_ELEMID_MESHID: | |
| 527 | scan->meshid = frm; | |
| 528 | break; | |
| 529 | case IEEE80211_ELEMID_MESHCONF: | |
| 530 | scan->meshconf = frm; | |
| 531 | break; | |
| 532 | #endif | |
| 533 | case IEEE80211_ELEMID_VENDOR: | |
| 534 | if (iswpaoui(frm)) | |
| 535 | scan->wpa = frm; | |
| 536 | else if (iswmeparam(frm) || iswmeinfo(frm)) | |
| 537 | scan->wme = frm; | |
| 538 | #ifdef IEEE80211_SUPPORT_SUPERG | |
| 539 | else if (isatherosoui(frm)) | |
| 540 | scan->ath = frm; | |
| 541 | #endif | |
| 542 | #ifdef IEEE80211_SUPPORT_TDMA | |
| 543 | else if (istdmaoui(frm)) | |
| 544 | scan->tdma = frm; | |
| 545 | #endif | |
| 546 | else if (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) { | |
| 547 | /* | |
| 548 | * Accept pre-draft HT ie's if the | |
| 549 | * standard ones have not been seen. | |
| 550 | */ | |
| 551 | if (ishtcapoui(frm)) { | |
| 552 | if (scan->htcap == NULL) | |
| 553 | scan->htcap = frm; | |
| 554 | } else if (ishtinfooui(frm)) { | |
| 555 | if (scan->htinfo == NULL) | |
| 556 | scan->htcap = frm; | |
| 557 | } | |
| 841ab66c | 558 | } |
| 841ab66c SZ |
559 | break; |
| 560 | default: | |
| 32176cfd RP |
561 | IEEE80211_DISCARD_IE(vap, IEEE80211_MSG_ELEMID, |
| 562 | wh, "unhandled", | |
| 563 | "id %u, len %u", *frm, frm[1]); | |
| 564 | vap->iv_stats.is_rx_elem_unknown++; | |
| 565 | break; | |
| 841ab66c | 566 | } |
| 32176cfd | 567 | frm += frm[1] + 2; |
| 841ab66c | 568 | } |
| 32176cfd RP |
569 | IEEE80211_VERIFY_ELEMENT(scan->rates, IEEE80211_RATE_MAXSIZE, |
| 570 | scan->status |= IEEE80211_BPARSE_RATES_INVALID); | |
| 571 | if (scan->rates != NULL && scan->xrates != NULL) { | |
| 841ab66c | 572 | /* |
| 32176cfd RP |
573 | * NB: don't process XRATES if RATES is missing. This |
| 574 | * avoids a potential null ptr deref and should be ok | |
| 575 | * as the return code will already note RATES is missing | |
| 576 | * (so callers shouldn't otherwise process the frame). | |
| 841ab66c | 577 | */ |
| 32176cfd RP |
578 | IEEE80211_VERIFY_ELEMENT(scan->xrates, |
| 579 | IEEE80211_RATE_MAXSIZE - scan->rates[1], | |
| 580 | scan->status |= IEEE80211_BPARSE_XRATES_INVALID); | |
| 841ab66c | 581 | } |
| 32176cfd RP |
582 | IEEE80211_VERIFY_ELEMENT(scan->ssid, IEEE80211_NWID_LEN, |
| 583 | scan->status |= IEEE80211_BPARSE_SSID_INVALID); | |
| 584 | if (scan->chan != scan->bchan && ic->ic_phytype != IEEE80211_T_FH) { | |
| 585 | /* | |
| 586 | * Frame was received on a channel different from the | |
| 587 | * one indicated in the DS params element id; | |
| 588 | * silently discard it. | |
| 589 | * | |
| 590 | * NB: this can happen due to signal leakage. | |
| 591 | * But we should take it for FH phy because | |
| 592 | * the rssi value should be correct even for | |
| 593 | * different hop pattern in FH. | |
| 594 | */ | |
| 595 | IEEE80211_DISCARD(vap, | |
| 596 | IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, | |
| 597 | wh, NULL, "for off-channel %u", scan->chan); | |
| 598 | vap->iv_stats.is_rx_chanmismatch++; | |
| 599 | scan->status |= IEEE80211_BPARSE_OFFCHAN; | |
| 600 | } | |
| 601 | if (!(IEEE80211_BINTVAL_MIN <= scan->bintval && | |
| 602 | scan->bintval <= IEEE80211_BINTVAL_MAX)) { | |
| 603 | IEEE80211_DISCARD(vap, | |
| 604 | IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, | |
| 605 | wh, NULL, "bogus beacon interval", scan->bintval); | |
| 606 | vap->iv_stats.is_rx_badbintval++; | |
| 607 | scan->status |= IEEE80211_BPARSE_BINTVAL_INVALID; | |
| 608 | } | |
| 609 | if (scan->country != NULL) { | |
| 610 | /* | |
| 611 | * Validate we have at least enough data to extract | |
| 612 | * the country code. Not sure if we should return an | |
| 613 | * error instead of discarding the IE; consider this | |
| 614 | * being lenient as we don't depend on the data for | |
| 615 | * correct operation. | |
| 616 | */ | |
| 617 | IEEE80211_VERIFY_LENGTH(scan->country[1], 3 * sizeof(uint8_t), | |
| 618 | scan->country = NULL); | |
| 841ab66c | 619 | } |
| 32176cfd RP |
620 | if (scan->csa != NULL) { |
| 621 | /* | |
| 622 | * Validate Channel Switch Announcement; this must | |
| 623 | * be the correct length or we toss the frame. | |
| 624 | */ | |
| 625 | IEEE80211_VERIFY_LENGTH(scan->csa[1], 3 * sizeof(uint8_t), | |
| 626 | scan->status |= IEEE80211_BPARSE_CSA_INVALID); | |
| 841ab66c | 627 | } |
| 841ab66c | 628 | /* |
| 32176cfd RP |
629 | * Process HT ie's. This is complicated by our |
| 630 | * accepting both the standard ie's and the pre-draft | |
| 631 | * vendor OUI ie's that some vendors still use/require. | |
| 841ab66c | 632 | */ |
| 32176cfd RP |
633 | if (scan->htcap != NULL) { |
| 634 | IEEE80211_VERIFY_LENGTH(scan->htcap[1], | |
| 635 | scan->htcap[0] == IEEE80211_ELEMID_VENDOR ? | |
| 636 | 4 + sizeof(struct ieee80211_ie_htcap)-2 : | |
| 637 | sizeof(struct ieee80211_ie_htcap)-2, | |
| 638 | scan->htcap = NULL); | |
| 841ab66c | 639 | } |
| 32176cfd RP |
640 | if (scan->htinfo != NULL) { |
| 641 | IEEE80211_VERIFY_LENGTH(scan->htinfo[1], | |
| 642 | scan->htinfo[0] == IEEE80211_ELEMID_VENDOR ? | |
| 643 | 4 + sizeof(struct ieee80211_ie_htinfo)-2 : | |
| 644 | sizeof(struct ieee80211_ie_htinfo)-2, | |
| 645 | scan->htinfo = NULL); | |
| 841ab66c | 646 | } |
| 32176cfd | 647 | return scan->status; |
| 841ab66c SZ |
648 | } |
| 649 | ||
| 841ab66c | 650 | /* |
| 32176cfd | 651 | * Parse an Action frame. Return 0 on success, non-zero on failure. |
| 841ab66c | 652 | */ |
| 32176cfd RP |
653 | int |
| 654 | ieee80211_parse_action(struct ieee80211_node *ni, struct mbuf *m) | |
| 841ab66c | 655 | { |
| 32176cfd RP |
656 | struct ieee80211vap *vap = ni->ni_vap; |
| 657 | const struct ieee80211_action *ia; | |
| 841ab66c SZ |
658 | struct ieee80211_frame *wh; |
| 659 | uint8_t *frm, *efrm; | |
| 841ab66c | 660 | |
| 32176cfd RP |
661 | /* |
| 662 | * action frame format: | |
| 663 | * [1] category | |
| 664 | * [1] action | |
| 665 | * [tlv] parameters | |
| 666 | */ | |
| 667 | wh = mtod(m, struct ieee80211_frame *); | |
| 668 | frm = (u_int8_t *)&wh[1]; | |
| 669 | efrm = mtod(m, u_int8_t *) + m->m_len; | |
| 670 | IEEE80211_VERIFY_LENGTH(efrm - frm, | |
| 671 | sizeof(struct ieee80211_action), return EINVAL); | |
| 672 | ia = (const struct ieee80211_action *) frm; | |
| 673 | ||
| 674 | vap->iv_stats.is_rx_action++; | |
| 675 | IEEE80211_NODE_STAT(ni, rx_action); | |
| 676 | ||
| 677 | /* verify frame payloads but defer processing */ | |
| 678 | /* XXX maybe push this to method */ | |
| 679 | switch (ia->ia_category) { | |
| 680 | case IEEE80211_ACTION_CAT_BA: | |
| 681 | switch (ia->ia_action) { | |
| 682 | case IEEE80211_ACTION_BA_ADDBA_REQUEST: | |
| 683 | IEEE80211_VERIFY_LENGTH(efrm - frm, | |
| 684 | sizeof(struct ieee80211_action_ba_addbarequest), | |
| 685 | return EINVAL); | |
| f186073c | 686 | break; |
| 32176cfd RP |
687 | case IEEE80211_ACTION_BA_ADDBA_RESPONSE: |
| 688 | IEEE80211_VERIFY_LENGTH(efrm - frm, | |
| 689 | sizeof(struct ieee80211_action_ba_addbaresponse), | |
| 690 | return EINVAL); | |
| f186073c | 691 | break; |
| 32176cfd RP |
692 | case IEEE80211_ACTION_BA_DELBA: |
| 693 | IEEE80211_VERIFY_LENGTH(efrm - frm, | |
| 694 | sizeof(struct ieee80211_action_ba_delba), | |
| 695 | return EINVAL); | |
| f186073c JS |
696 | break; |
| 697 | } | |
| 698 | break; | |
| 32176cfd RP |
699 | case IEEE80211_ACTION_CAT_HT: |
| 700 | switch (ia->ia_action) { | |
| 701 | case IEEE80211_ACTION_HT_TXCHWIDTH: | |
| 702 | IEEE80211_VERIFY_LENGTH(efrm - frm, | |
| 703 | sizeof(struct ieee80211_action_ht_txchwidth), | |
| 704 | return EINVAL); | |
| f186073c | 705 | break; |
| 32176cfd RP |
706 | case IEEE80211_ACTION_HT_MIMOPWRSAVE: |
| 707 | IEEE80211_VERIFY_LENGTH(efrm - frm, | |
| 708 | sizeof(struct ieee80211_action_ht_mimopowersave), | |
| 709 | return EINVAL); | |
| f186073c JS |
710 | break; |
| 711 | } | |
| 712 | break; | |
| 713 | } | |
| 32176cfd | 714 | return 0; |
| 841ab66c SZ |
715 | } |
| 716 | ||
| 717 | #ifdef IEEE80211_DEBUG | |
| 718 | /* | |
| 719 | * Debugging support. | |
| 720 | */ | |
| 32176cfd RP |
721 | void |
| 722 | ieee80211_ssid_mismatch(struct ieee80211vap *vap, const char *tag, | |
| 723 | uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid) | |
| 724 | { | |
| 6168f72e RP |
725 | kprintf("[%6D] discard %s frame, ssid mismatch: ", |
| 726 | mac, ":", tag); | |
| 32176cfd | 727 | ieee80211_print_essid(ssid + 2, ssid[1]); |
| 6168f72e | 728 | kprintf("\n"); |
| 32176cfd | 729 | } |
| 841ab66c SZ |
730 | |
| 731 | /* | |
| 732 | * Return the bssid of a frame. | |
| 733 | */ | |
| 734 | static const uint8_t * | |
| 4028af95 | 735 | ieee80211_getbssid(const struct ieee80211vap *vap, const struct ieee80211_frame *wh) |
| 841ab66c | 736 | { |
| 32176cfd | 737 | if (vap->iv_opmode == IEEE80211_M_STA) |
| 841ab66c SZ |
738 | return wh->i_addr2; |
| 739 | if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS) | |
| 740 | return wh->i_addr1; | |
| 741 | if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL) | |
| 742 | return wh->i_addr1; | |
| 743 | return wh->i_addr3; | |
| 744 | } | |
| 745 | ||
| 32176cfd RP |
746 | #include <machine/stdarg.h> |
| 747 | ||
| 841ab66c | 748 | void |
| 4028af95 | 749 | ieee80211_note(const struct ieee80211vap *vap, const char *fmt, ...) |
| 841ab66c SZ |
750 | { |
| 751 | char buf[128]; /* XXX */ | |
| 6168f72e | 752 | __va_list ap; |
| 841ab66c | 753 | |
| 6168f72e | 754 | __va_start(ap, fmt); |
| 379210cb | 755 | kvsnprintf(buf, sizeof(buf), fmt, ap); |
| 6168f72e | 756 | __va_end(ap); |
| 841ab66c | 757 | |
| 32176cfd | 758 | if_printf(vap->iv_ifp, "%s", buf); /* NB: no \n */ |
| 841ab66c SZ |
759 | } |
| 760 | ||
| 761 | void | |
| 4028af95 | 762 | ieee80211_note_frame(const struct ieee80211vap *vap, |
| 841ab66c SZ |
763 | const struct ieee80211_frame *wh, |
| 764 | const char *fmt, ...) | |
| 765 | { | |
| 766 | char buf[128]; /* XXX */ | |
| 6168f72e | 767 | __va_list ap; |
| 841ab66c | 768 | |
| 6168f72e RP |
769 | __va_start(ap, fmt); |
| 770 | kvsnprintf(buf, sizeof(buf), fmt, ap); | |
| 771 | __va_end(ap); | |
| 772 | if_printf(vap->iv_ifp, "[%6D] %s\n", | |
| 773 | ieee80211_getbssid(vap, wh), ":", buf); | |
| 841ab66c SZ |
774 | } |
| 775 | ||
| 776 | void | |
| 4028af95 | 777 | ieee80211_note_mac(const struct ieee80211vap *vap, |
| 841ab66c SZ |
778 | const uint8_t mac[IEEE80211_ADDR_LEN], |
| 779 | const char *fmt, ...) | |
| 780 | { | |
| 781 | char buf[128]; /* XXX */ | |
| 6168f72e | 782 | __va_list ap; |
| 841ab66c | 783 | |
| 6168f72e RP |
784 | __va_start(ap, fmt); |
| 785 | kvsnprintf(buf, sizeof(buf), fmt, ap); | |
| 786 | __va_end(ap); | |
| 787 | if_printf(vap->iv_ifp, "[%6D] %s\n", mac, ":", buf); | |
| 841ab66c SZ |
788 | } |
| 789 | ||
| 32176cfd | 790 | void |
| 4028af95 | 791 | ieee80211_discard_frame(const struct ieee80211vap *vap, |
| 841ab66c SZ |
792 | const struct ieee80211_frame *wh, |
| 793 | const char *type, const char *fmt, ...) | |
| 794 | { | |
| 6168f72e | 795 | __va_list ap; |
| 841ab66c | 796 | |
| 6168f72e RP |
797 | if_printf(vap->iv_ifp, "[%6D] discard ", |
| 798 | ieee80211_getbssid(vap, wh), ":"); | |
| 32176cfd | 799 | if (type == NULL) { |
| 6168f72e | 800 | kprintf("%s frame, ", ieee80211_mgt_subtype_name[ |
| 32176cfd RP |
801 | (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) >> |
| 802 | IEEE80211_FC0_SUBTYPE_SHIFT]); | |
| 803 | } else | |
| 6168f72e RP |
804 | kprintf("%s frame, ", type); |
| 805 | __va_start(ap, fmt); | |
| 806 | kvprintf(fmt, ap); | |
| 807 | __va_end(ap); | |
| 808 | kprintf("\n"); | |
| 841ab66c SZ |
809 | } |
| 810 | ||
| 32176cfd | 811 | void |
| 4028af95 | 812 | ieee80211_discard_ie(const struct ieee80211vap *vap, |
| 841ab66c SZ |
813 | const struct ieee80211_frame *wh, |
| 814 | const char *type, const char *fmt, ...) | |
| 815 | { | |
| 6168f72e | 816 | __va_list ap; |
| 841ab66c | 817 | |
| 6168f72e RP |
818 | if_printf(vap->iv_ifp, "[%6D] discard ", |
| 819 | ieee80211_getbssid(vap, wh), ":"); | |
| 841ab66c | 820 | if (type != NULL) |
| 6168f72e | 821 | kprintf("%s information element, ", type); |
| 841ab66c | 822 | else |
| 6168f72e RP |
823 | kprintf("information element, "); |
| 824 | __va_start(ap, fmt); | |
| 825 | kvprintf(fmt, ap); | |
| 826 | __va_end(ap); | |
| 827 | kprintf("\n"); | |
| 841ab66c SZ |
828 | } |
| 829 | ||
| 32176cfd | 830 | void |
| 4028af95 | 831 | ieee80211_discard_mac(const struct ieee80211vap *vap, |
| 841ab66c SZ |
832 | const uint8_t mac[IEEE80211_ADDR_LEN], |
| 833 | const char *type, const char *fmt, ...) | |
| 834 | { | |
| 6168f72e | 835 | __va_list ap; |
| 841ab66c | 836 | |
| 6168f72e | 837 | if_printf(vap->iv_ifp, "[%6D] discard ", mac, ":"); |
| 841ab66c | 838 | if (type != NULL) |
| 6168f72e | 839 | kprintf("%s frame, ", type); |
| 841ab66c | 840 | else |
| 6168f72e RP |
841 | kprintf("frame, "); |
| 842 | __va_start(ap, fmt); | |
| 843 | kvprintf(fmt, ap); | |
| 844 | __va_end(ap); | |
| 845 | kprintf("\n"); | |
| 841ab66c SZ |
846 | } |
| 847 | #endif /* IEEE80211_DEBUG */ |