11a1e1d21SSam Leffler /*- 27535e66aSSam Leffler * Copyright (c) 2001 Atsushi Onoe 31a1e1d21SSam Leffler * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting 41a1e1d21SSam Leffler * All rights reserved. 51a1e1d21SSam Leffler * 61a1e1d21SSam Leffler * Redistribution and use in source and binary forms, with or without 71a1e1d21SSam Leffler * modification, are permitted provided that the following conditions 81a1e1d21SSam Leffler * are met: 91a1e1d21SSam Leffler * 1. Redistributions of source code must retain the above copyright 107535e66aSSam Leffler * notice, this list of conditions and the following disclaimer. 117535e66aSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright 127535e66aSSam Leffler * notice, this list of conditions and the following disclaimer in the 137535e66aSSam Leffler * documentation and/or other materials provided with the distribution. 147535e66aSSam Leffler * 3. The name of the author may not be used to endorse or promote products 157535e66aSSam Leffler * derived from this software without specific prior written permission. 161a1e1d21SSam Leffler * 171a1e1d21SSam Leffler * Alternatively, this software may be distributed under the terms of the 181a1e1d21SSam Leffler * GNU General Public License ("GPL") version 2 as published by the Free 191a1e1d21SSam Leffler * Software Foundation. 201a1e1d21SSam Leffler * 217535e66aSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 227535e66aSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 237535e66aSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 247535e66aSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 257535e66aSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 267535e66aSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 277535e66aSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 287535e66aSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 297535e66aSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 307535e66aSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 311a1e1d21SSam Leffler */ 321a1e1d21SSam Leffler 331a1e1d21SSam Leffler #include <sys/cdefs.h> 341a1e1d21SSam Leffler __FBSDID("$FreeBSD$"); 351a1e1d21SSam Leffler 361a1e1d21SSam Leffler #include "opt_inet.h" 371a1e1d21SSam Leffler 381a1e1d21SSam Leffler #include <sys/param.h> 391a1e1d21SSam Leffler #include <sys/systm.h> 401a1e1d21SSam Leffler #include <sys/mbuf.h> 411a1e1d21SSam Leffler #include <sys/malloc.h> 421a1e1d21SSam Leffler #include <sys/kernel.h> 431a1e1d21SSam Leffler #include <sys/socket.h> 441a1e1d21SSam Leffler #include <sys/sockio.h> 451a1e1d21SSam Leffler #include <sys/endian.h> 461a1e1d21SSam Leffler #include <sys/errno.h> 471a1e1d21SSam Leffler #include <sys/bus.h> 481a1e1d21SSam Leffler #include <sys/proc.h> 491a1e1d21SSam Leffler #include <sys/sysctl.h> 501a1e1d21SSam Leffler 511a1e1d21SSam Leffler #include <machine/atomic.h> 521a1e1d21SSam Leffler 531a1e1d21SSam Leffler #include <net/if.h> 541a1e1d21SSam Leffler #include <net/if_dl.h> 551a1e1d21SSam Leffler #include <net/if_media.h> 561a1e1d21SSam Leffler #include <net/if_arp.h> 571a1e1d21SSam Leffler #include <net/ethernet.h> 581a1e1d21SSam Leffler #include <net/if_llc.h> 591a1e1d21SSam Leffler 601a1e1d21SSam Leffler #include <net80211/ieee80211_var.h> 611a1e1d21SSam Leffler 621a1e1d21SSam Leffler #include <net/bpf.h> 631a1e1d21SSam Leffler 641a1e1d21SSam Leffler #ifdef INET 651a1e1d21SSam Leffler #include <netinet/in.h> 661a1e1d21SSam Leffler #include <netinet/if_ether.h> 671a1e1d21SSam Leffler #endif 681a1e1d21SSam Leffler 691a1e1d21SSam Leffler static struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *); 701a1e1d21SSam Leffler static void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *); 711a1e1d21SSam Leffler static void ieee80211_node_copy(struct ieee80211com *, 721a1e1d21SSam Leffler struct ieee80211_node *, const struct ieee80211_node *); 73d1e61976SSam Leffler static u_int8_t ieee80211_node_getrssi(struct ieee80211com *, 74d1e61976SSam Leffler struct ieee80211_node *); 75d1e61976SSam Leffler 761a1e1d21SSam Leffler static void ieee80211_setup_node(struct ieee80211com *ic, 771a1e1d21SSam Leffler struct ieee80211_node *ni, u_int8_t *macaddr); 781a1e1d21SSam Leffler static void _ieee80211_free_node(struct ieee80211com *, 791a1e1d21SSam Leffler struct ieee80211_node *); 801a1e1d21SSam Leffler 811a1e1d21SSam Leffler void 821a1e1d21SSam Leffler ieee80211_node_attach(struct ifnet *ifp) 831a1e1d21SSam Leffler { 841a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 851a1e1d21SSam Leffler 861a1e1d21SSam Leffler /* XXX need unit */ 871a1e1d21SSam Leffler mtx_init(&ic->ic_nodelock, ifp->if_name, "802.11 node table", MTX_DEF); 881a1e1d21SSam Leffler TAILQ_INIT(&ic->ic_node); 891a1e1d21SSam Leffler ic->ic_node_alloc = ieee80211_node_alloc; 901a1e1d21SSam Leffler ic->ic_node_free = ieee80211_node_free; 911a1e1d21SSam Leffler ic->ic_node_copy = ieee80211_node_copy; 92d1e61976SSam Leffler ic->ic_node_getrssi = ieee80211_node_getrssi; 932692bb26SSam Leffler } 942692bb26SSam Leffler 952692bb26SSam Leffler void 962692bb26SSam Leffler ieee80211_node_lateattach(struct ifnet *ifp) 972692bb26SSam Leffler { 982692bb26SSam Leffler struct ieee80211com *ic = (void *)ifp; 992692bb26SSam Leffler 1001a1e1d21SSam Leffler ic->ic_bss = (*ic->ic_node_alloc)(ic); 10158f40303SSam Leffler KASSERT(ic->ic_bss != NULL, ("unable to setup inital BSS node")); 10258f40303SSam Leffler ic->ic_bss->ni_chan = IEEE80211_CHAN_ANYC; 1031a1e1d21SSam Leffler } 1041a1e1d21SSam Leffler 1051a1e1d21SSam Leffler void 1061a1e1d21SSam Leffler ieee80211_node_detach(struct ifnet *ifp) 1071a1e1d21SSam Leffler { 1081a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 1091a1e1d21SSam Leffler 1101a1e1d21SSam Leffler if (ic->ic_bss != NULL) 1111a1e1d21SSam Leffler (*ic->ic_node_free)(ic, ic->ic_bss); 1121a1e1d21SSam Leffler ieee80211_free_allnodes(ic); 1131a1e1d21SSam Leffler mtx_destroy(&ic->ic_nodelock); 1141a1e1d21SSam Leffler } 1151a1e1d21SSam Leffler 1161a1e1d21SSam Leffler /* 1171a1e1d21SSam Leffler * AP scanning support. 1181a1e1d21SSam Leffler */ 1191a1e1d21SSam Leffler 1201a1e1d21SSam Leffler /* 1211a1e1d21SSam Leffler * Initialize the active channel set based on the set 1221a1e1d21SSam Leffler * of available channels and the current PHY mode. 1231a1e1d21SSam Leffler */ 124a11c9a5cSSam Leffler static void 1251a1e1d21SSam Leffler ieee80211_reset_scan(struct ifnet *ifp) 1261a1e1d21SSam Leffler { 1271a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 1281a1e1d21SSam Leffler 1291a1e1d21SSam Leffler memcpy(ic->ic_chan_scan, ic->ic_chan_active, 1301a1e1d21SSam Leffler sizeof(ic->ic_chan_active)); 131a11c9a5cSSam Leffler /* NB: hack, setup so next_scan starts with the first channel */ 132a11c9a5cSSam Leffler if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC) 133a11c9a5cSSam Leffler ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX]; 1341a1e1d21SSam Leffler } 1351a1e1d21SSam Leffler 1361a1e1d21SSam Leffler /* 1371a1e1d21SSam Leffler * Begin an active scan. 1381a1e1d21SSam Leffler */ 1391a1e1d21SSam Leffler void 140a11c9a5cSSam Leffler ieee80211_begin_scan(struct ifnet *ifp) 1411a1e1d21SSam Leffler { 1421a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 1431a1e1d21SSam Leffler 144a11c9a5cSSam Leffler /* 145a11c9a5cSSam Leffler * In all but hostap mode scanning starts off in 146a11c9a5cSSam Leffler * an active mode before switching to passive. 147a11c9a5cSSam Leffler */ 148a11c9a5cSSam Leffler if (ic->ic_opmode != IEEE80211_M_HOSTAP) 149a11c9a5cSSam Leffler ic->ic_flags |= IEEE80211_F_ASCAN; 1501a1e1d21SSam Leffler if (ifp->if_flags & IFF_DEBUG) 1511a1e1d21SSam Leffler if_printf(ifp, "begin %s scan\n", 152a11c9a5cSSam Leffler (ic->ic_flags & IEEE80211_F_ASCAN) ? 1531a1e1d21SSam Leffler "active" : "passive"); 1541a1e1d21SSam Leffler /* 155a11c9a5cSSam Leffler * Clear scan state and flush any previously seen 156a11c9a5cSSam Leffler * AP's. Note that the latter assumes we don't act 157a11c9a5cSSam Leffler * as both an AP and a station, otherwise we'll 158a11c9a5cSSam Leffler * potentially flush state of stations associated 159a11c9a5cSSam Leffler * with us. 1601a1e1d21SSam Leffler */ 161a11c9a5cSSam Leffler ieee80211_reset_scan(ifp); 1621a1e1d21SSam Leffler ieee80211_free_allnodes(ic); 1631a1e1d21SSam Leffler 164a11c9a5cSSam Leffler /* Scan the next channel. */ 165a11c9a5cSSam Leffler ieee80211_next_scan(ifp); 1661a1e1d21SSam Leffler } 1671a1e1d21SSam Leffler 1681a1e1d21SSam Leffler /* 1691a1e1d21SSam Leffler * Switch to the next channel marked for scanning. 1701a1e1d21SSam Leffler */ 1711a1e1d21SSam Leffler void 1721a1e1d21SSam Leffler ieee80211_next_scan(struct ifnet *ifp) 1731a1e1d21SSam Leffler { 1741a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 1751a1e1d21SSam Leffler struct ieee80211_channel *chan; 1761a1e1d21SSam Leffler 1771a1e1d21SSam Leffler chan = ic->ic_bss->ni_chan; 1781a1e1d21SSam Leffler for (;;) { 1791a1e1d21SSam Leffler if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX]) 1801a1e1d21SSam Leffler chan = &ic->ic_channels[0]; 1811a1e1d21SSam Leffler if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) { 1821a1e1d21SSam Leffler /* 1831a1e1d21SSam Leffler * Honor channels marked passive-only 1841a1e1d21SSam Leffler * during an active scan. 1851a1e1d21SSam Leffler */ 1861a1e1d21SSam Leffler if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 || 1871a1e1d21SSam Leffler (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) 1881a1e1d21SSam Leffler break; 1891a1e1d21SSam Leffler } 1901a1e1d21SSam Leffler if (chan == ic->ic_bss->ni_chan) { 1911a1e1d21SSam Leffler ieee80211_end_scan(ifp); 1921a1e1d21SSam Leffler return; 1931a1e1d21SSam Leffler } 1941a1e1d21SSam Leffler } 1951a1e1d21SSam Leffler clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan)); 1961a1e1d21SSam Leffler IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n", 1971a1e1d21SSam Leffler ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan), 1981a1e1d21SSam Leffler ieee80211_chan2ieee(ic, chan))); 1991a1e1d21SSam Leffler ic->ic_bss->ni_chan = chan; 200a11c9a5cSSam Leffler ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 2011a1e1d21SSam Leffler } 2021a1e1d21SSam Leffler 2031a1e1d21SSam Leffler void 2041a1e1d21SSam Leffler ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) 2051a1e1d21SSam Leffler { 2061a1e1d21SSam Leffler struct ieee80211_node *ni; 2071a1e1d21SSam Leffler struct ifnet *ifp = &ic->ic_if; 2081a1e1d21SSam Leffler 2091a1e1d21SSam Leffler ni = ic->ic_bss; 2101a1e1d21SSam Leffler if (ifp->if_flags & IFF_DEBUG) 2111a1e1d21SSam Leffler if_printf(ifp, "creating ibss\n"); 2121a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_SIBSS; 2131a1e1d21SSam Leffler ni->ni_chan = chan; 2141a1e1d21SSam Leffler ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; 2151a1e1d21SSam Leffler IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr); 2161a1e1d21SSam Leffler IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr); 2171a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) 2181a1e1d21SSam Leffler ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */ 2191a1e1d21SSam Leffler ni->ni_esslen = ic->ic_des_esslen; 2201a1e1d21SSam Leffler memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); 2211a1e1d21SSam Leffler ni->ni_rssi = 0; 2221a1e1d21SSam Leffler ni->ni_rstamp = 0; 2231a1e1d21SSam Leffler memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp)); 2241a1e1d21SSam Leffler ni->ni_intval = ic->ic_lintval; 2251a1e1d21SSam Leffler ni->ni_capinfo = IEEE80211_CAPINFO_IBSS; 2261a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_WEPON) 2271a1e1d21SSam Leffler ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; 2281a1e1d21SSam Leffler if (ic->ic_phytype == IEEE80211_T_FH) { 2291a1e1d21SSam Leffler ni->ni_fhdwell = 200; /* XXX */ 2301a1e1d21SSam Leffler ni->ni_fhindex = 1; 2311a1e1d21SSam Leffler } 232a11c9a5cSSam Leffler ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 2331a1e1d21SSam Leffler } 2341a1e1d21SSam Leffler 2351a1e1d21SSam Leffler /* 2361a1e1d21SSam Leffler * Complete a scan of potential channels. 2371a1e1d21SSam Leffler */ 2381a1e1d21SSam Leffler void 2391a1e1d21SSam Leffler ieee80211_end_scan(struct ifnet *ifp) 2401a1e1d21SSam Leffler { 2411a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 2421a1e1d21SSam Leffler struct ieee80211_node *ni, *nextbs, *selbs; 2431a1e1d21SSam Leffler u_int8_t rate; 2441a1e1d21SSam Leffler int i, fail; 2451a1e1d21SSam Leffler 2461a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_ASCAN; 2471a1e1d21SSam Leffler ni = TAILQ_FIRST(&ic->ic_node); 2481a1e1d21SSam Leffler 2491a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 2501a1e1d21SSam Leffler /* XXX off stack? */ 2511a1e1d21SSam Leffler u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)]; 2521a1e1d21SSam Leffler /* 2531a1e1d21SSam Leffler * The passive scan to look for existing AP's completed, 2541a1e1d21SSam Leffler * select a channel to camp on. Identify the channels 2551a1e1d21SSam Leffler * that already have one or more AP's and try to locate 2561a1e1d21SSam Leffler * an unnoccupied one. If that fails, pick a random 2571a1e1d21SSam Leffler * channel from the active set. 2581a1e1d21SSam Leffler */ 2591a1e1d21SSam Leffler for (; ni != NULL; ni = nextbs) { 2601a1e1d21SSam Leffler ieee80211_ref_node(ni); 2611a1e1d21SSam Leffler nextbs = TAILQ_NEXT(ni, ni_list); 2621a1e1d21SSam Leffler setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan)); 2631a1e1d21SSam Leffler ieee80211_free_node(ic, ni); 2641a1e1d21SSam Leffler } 2651a1e1d21SSam Leffler for (i = 0; i < IEEE80211_CHAN_MAX; i++) 2661a1e1d21SSam Leffler if (isset(ic->ic_chan_active, i) && isclr(occupied, i)) 2671a1e1d21SSam Leffler break; 2681a1e1d21SSam Leffler if (i == IEEE80211_CHAN_MAX) { 2691a1e1d21SSam Leffler fail = arc4random() & 3; /* random 0-3 */ 2701a1e1d21SSam Leffler for (i = 0; i < IEEE80211_CHAN_MAX; i++) 2711a1e1d21SSam Leffler if (isset(ic->ic_chan_active, i) && fail-- == 0) 2721a1e1d21SSam Leffler break; 2731a1e1d21SSam Leffler } 2741a1e1d21SSam Leffler ieee80211_create_ibss(ic, &ic->ic_channels[i]); 2751a1e1d21SSam Leffler return; 2761a1e1d21SSam Leffler } 2771a1e1d21SSam Leffler if (ni == NULL) { 2781a1e1d21SSam Leffler IEEE80211_DPRINTF(("%s: no scan candidate\n", __func__)); 2791a1e1d21SSam Leffler notfound: 2801a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && 2811a1e1d21SSam Leffler (ic->ic_flags & IEEE80211_F_IBSSON) && 2821a1e1d21SSam Leffler ic->ic_des_esslen != 0) { 2831a1e1d21SSam Leffler ieee80211_create_ibss(ic, ic->ic_ibss_chan); 2841a1e1d21SSam Leffler return; 2851a1e1d21SSam Leffler } 2861a1e1d21SSam Leffler /* 2871a1e1d21SSam Leffler * Reset the list of channels to scan and start again. 2881a1e1d21SSam Leffler */ 2891a1e1d21SSam Leffler ieee80211_reset_scan(ifp); 2901a1e1d21SSam Leffler ieee80211_next_scan(ifp); 2911a1e1d21SSam Leffler return; 2921a1e1d21SSam Leffler } 2931a1e1d21SSam Leffler selbs = NULL; 2941a1e1d21SSam Leffler if (ifp->if_flags & IFF_DEBUG) 2950a915fadSSam Leffler if_printf(ifp, "\tmacaddr bssid chan rssi rate flag wep essid\n"); 2961a1e1d21SSam Leffler for (; ni != NULL; ni = nextbs) { 2971a1e1d21SSam Leffler ieee80211_ref_node(ni); 2981a1e1d21SSam Leffler nextbs = TAILQ_NEXT(ni, ni_list); 2991a1e1d21SSam Leffler if (ni->ni_fails) { 3001a1e1d21SSam Leffler /* 3011a1e1d21SSam Leffler * The configuration of the access points may change 3021a1e1d21SSam Leffler * during my scan. So delete the entry for the AP 3031a1e1d21SSam Leffler * and retry to associate if there is another beacon. 3041a1e1d21SSam Leffler */ 3051a1e1d21SSam Leffler if (ni->ni_fails++ > 2) 3061a1e1d21SSam Leffler ieee80211_free_node(ic, ni); 3071a1e1d21SSam Leffler continue; 3081a1e1d21SSam Leffler } 3091a1e1d21SSam Leffler fail = 0; 3101a1e1d21SSam Leffler if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) 3111a1e1d21SSam Leffler fail |= 0x01; 3121a1e1d21SSam Leffler if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 3131a1e1d21SSam Leffler ni->ni_chan != ic->ic_des_chan) 3141a1e1d21SSam Leffler fail |= 0x01; 3151a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) { 3161a1e1d21SSam Leffler if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) 3171a1e1d21SSam Leffler fail |= 0x02; 3181a1e1d21SSam Leffler } else { 3191a1e1d21SSam Leffler if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) 3201a1e1d21SSam Leffler fail |= 0x02; 3211a1e1d21SSam Leffler } 3221a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_WEPON) { 3231a1e1d21SSam Leffler if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) 3241a1e1d21SSam Leffler fail |= 0x04; 3251a1e1d21SSam Leffler } else { 3261a1e1d21SSam Leffler if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) 3271a1e1d21SSam Leffler fail |= 0x04; 3281a1e1d21SSam Leffler } 3291a1e1d21SSam Leffler rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO); 3301a1e1d21SSam Leffler if (rate & IEEE80211_RATE_BASIC) 3311a1e1d21SSam Leffler fail |= 0x08; 3321a1e1d21SSam Leffler if (ic->ic_des_esslen != 0 && 3331a1e1d21SSam Leffler (ni->ni_esslen != ic->ic_des_esslen || 3341a1e1d21SSam Leffler memcmp(ni->ni_essid, ic->ic_des_essid, 3351a1e1d21SSam Leffler ic->ic_des_esslen != 0))) 3361a1e1d21SSam Leffler fail |= 0x10; 3371a1e1d21SSam Leffler if ((ic->ic_flags & IEEE80211_F_DESBSSID) && 3381a1e1d21SSam Leffler !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) 3391a1e1d21SSam Leffler fail |= 0x20; 3401a1e1d21SSam Leffler if (ifp->if_flags & IFF_DEBUG) { 3411a1e1d21SSam Leffler printf(" %c %s", fail ? '-' : '+', 3421a1e1d21SSam Leffler ether_sprintf(ni->ni_macaddr)); 3431a1e1d21SSam Leffler printf(" %s%c", ether_sprintf(ni->ni_bssid), 3441a1e1d21SSam Leffler fail & 0x20 ? '!' : ' '); 3451a1e1d21SSam Leffler printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan), 3461a1e1d21SSam Leffler fail & 0x01 ? '!' : ' '); 3471a1e1d21SSam Leffler printf(" %+4d", ni->ni_rssi); 3481a1e1d21SSam Leffler printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, 3491a1e1d21SSam Leffler fail & 0x08 ? '!' : ' '); 3501a1e1d21SSam Leffler printf(" %4s%c", 3511a1e1d21SSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : 3521a1e1d21SSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : 3531a1e1d21SSam Leffler "????", 3541a1e1d21SSam Leffler fail & 0x02 ? '!' : ' '); 3551a1e1d21SSam Leffler printf(" %3s%c ", 3561a1e1d21SSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? 3571a1e1d21SSam Leffler "wep" : "no", 3581a1e1d21SSam Leffler fail & 0x04 ? '!' : ' '); 3591a1e1d21SSam Leffler ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); 3601a1e1d21SSam Leffler printf("%s\n", fail & 0x10 ? "!" : ""); 3611a1e1d21SSam Leffler } 3621a1e1d21SSam Leffler if (!fail) { 3631a1e1d21SSam Leffler if (selbs == NULL) 3641a1e1d21SSam Leffler selbs = ni; 3651a1e1d21SSam Leffler else if (ni->ni_rssi > selbs->ni_rssi) { 3661a1e1d21SSam Leffler ieee80211_unref_node(&selbs); 3671a1e1d21SSam Leffler selbs = ni; 3681a1e1d21SSam Leffler } else 3691a1e1d21SSam Leffler ieee80211_unref_node(&ni); 3701a1e1d21SSam Leffler } else { 3711a1e1d21SSam Leffler ieee80211_unref_node(&ni); 3721a1e1d21SSam Leffler } 3731a1e1d21SSam Leffler } 3741a1e1d21SSam Leffler if (selbs == NULL) 3751a1e1d21SSam Leffler goto notfound; 3761a1e1d21SSam Leffler (*ic->ic_node_copy)(ic, ic->ic_bss, selbs); 3771a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) { 3781a1e1d21SSam Leffler ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE | 3791a1e1d21SSam Leffler IEEE80211_F_DONEGO | IEEE80211_F_DODEL); 3801a1e1d21SSam Leffler if (ic->ic_bss->ni_rates.rs_nrates == 0) { 3811a1e1d21SSam Leffler selbs->ni_fails++; 3821a1e1d21SSam Leffler ieee80211_unref_node(&selbs); 3831a1e1d21SSam Leffler goto notfound; 3841a1e1d21SSam Leffler } 3851a1e1d21SSam Leffler ieee80211_unref_node(&selbs); 386a11c9a5cSSam Leffler ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 3871a1e1d21SSam Leffler } else { 3881a1e1d21SSam Leffler ieee80211_unref_node(&selbs); 389a11c9a5cSSam Leffler ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); 3901a1e1d21SSam Leffler } 3911a1e1d21SSam Leffler } 3921a1e1d21SSam Leffler 3931a1e1d21SSam Leffler static struct ieee80211_node * 3941a1e1d21SSam Leffler ieee80211_node_alloc(struct ieee80211com *ic) 3951a1e1d21SSam Leffler { 3961a1e1d21SSam Leffler return malloc(sizeof(struct ieee80211_node), M_DEVBUF, 3971a1e1d21SSam Leffler M_NOWAIT | M_ZERO); 3981a1e1d21SSam Leffler } 3991a1e1d21SSam Leffler 4001a1e1d21SSam Leffler static void 4011a1e1d21SSam Leffler ieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni) 4021a1e1d21SSam Leffler { 4031a1e1d21SSam Leffler free(ni, M_DEVBUF); 4041a1e1d21SSam Leffler } 4051a1e1d21SSam Leffler 4061a1e1d21SSam Leffler static void 4071a1e1d21SSam Leffler ieee80211_node_copy(struct ieee80211com *ic, 4081a1e1d21SSam Leffler struct ieee80211_node *dst, const struct ieee80211_node *src) 4091a1e1d21SSam Leffler { 4101a1e1d21SSam Leffler *dst = *src; 4111a1e1d21SSam Leffler } 4121a1e1d21SSam Leffler 413d1e61976SSam Leffler static u_int8_t 414d1e61976SSam Leffler ieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni) 415d1e61976SSam Leffler { 416d1e61976SSam Leffler return ni->ni_rssi; 417d1e61976SSam Leffler } 418d1e61976SSam Leffler 4191a1e1d21SSam Leffler static void 4201a1e1d21SSam Leffler ieee80211_setup_node(struct ieee80211com *ic, 4211a1e1d21SSam Leffler struct ieee80211_node *ni, u_int8_t *macaddr) 4221a1e1d21SSam Leffler { 4231a1e1d21SSam Leffler int hash; 4241a1e1d21SSam Leffler 4251a1e1d21SSam Leffler IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); 4261a1e1d21SSam Leffler hash = IEEE80211_NODE_HASH(macaddr); 4271a1e1d21SSam Leffler ni->ni_refcnt = 1; /* mark referenced */ 4281a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 4291a1e1d21SSam Leffler TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list); 4301a1e1d21SSam Leffler LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash); 4311a1e1d21SSam Leffler /* 4321a1e1d21SSam Leffler * Note we don't enable the inactive timer when acting 4331a1e1d21SSam Leffler * as a station. Nodes created in this mode represent 4341a1e1d21SSam Leffler * AP's identified while scanning. If we time them out 4351a1e1d21SSam Leffler * then several things happen: we can't return the data 4361a1e1d21SSam Leffler * to users to show the list of AP's we encountered, and 4371a1e1d21SSam Leffler * more importantly, we'll incorrectly deauthenticate 4381a1e1d21SSam Leffler * ourself because the inactivity timer will kick us off. 4391a1e1d21SSam Leffler */ 4401a1e1d21SSam Leffler if (ic->ic_opmode != IEEE80211_M_STA) 4411a1e1d21SSam Leffler ic->ic_inact_timer = IEEE80211_INACT_WAIT; 4421a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 4431a1e1d21SSam Leffler } 4441a1e1d21SSam Leffler 4451a1e1d21SSam Leffler struct ieee80211_node * 4461a1e1d21SSam Leffler ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr) 4471a1e1d21SSam Leffler { 4481a1e1d21SSam Leffler struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); 4491a1e1d21SSam Leffler if (ni != NULL) 4501a1e1d21SSam Leffler ieee80211_setup_node(ic, ni, macaddr); 4511a1e1d21SSam Leffler return ni; 4521a1e1d21SSam Leffler } 4531a1e1d21SSam Leffler 4541a1e1d21SSam Leffler struct ieee80211_node * 4551a1e1d21SSam Leffler ieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr) 4561a1e1d21SSam Leffler { 4571a1e1d21SSam Leffler struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); 4581a1e1d21SSam Leffler if (ni != NULL) { 4591a1e1d21SSam Leffler memcpy(ni, ic->ic_bss, sizeof(struct ieee80211_node)); 4601a1e1d21SSam Leffler ieee80211_setup_node(ic, ni, macaddr); 4611a1e1d21SSam Leffler } 4621a1e1d21SSam Leffler return ni; 4631a1e1d21SSam Leffler } 4641a1e1d21SSam Leffler 4651a1e1d21SSam Leffler struct ieee80211_node * 4661a1e1d21SSam Leffler ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) 4671a1e1d21SSam Leffler { 4681a1e1d21SSam Leffler struct ieee80211_node *ni; 4691a1e1d21SSam Leffler int hash; 4701a1e1d21SSam Leffler 4711a1e1d21SSam Leffler hash = IEEE80211_NODE_HASH(macaddr); 4721a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 4731a1e1d21SSam Leffler LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { 4741a1e1d21SSam Leffler if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { 4751a1e1d21SSam Leffler atomic_add_int(&ni->ni_refcnt, 1); /* mark referenced */ 4761a1e1d21SSam Leffler break; 4771a1e1d21SSam Leffler } 4781a1e1d21SSam Leffler } 4791a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 4801a1e1d21SSam Leffler return ni; 4811a1e1d21SSam Leffler } 4821a1e1d21SSam Leffler 4831a1e1d21SSam Leffler /* 4841a1e1d21SSam Leffler * Like find but search based on the channel too. 4851a1e1d21SSam Leffler */ 4861a1e1d21SSam Leffler struct ieee80211_node * 4871a1e1d21SSam Leffler ieee80211_lookup_node(struct ieee80211com *ic, 4881a1e1d21SSam Leffler u_int8_t *macaddr, struct ieee80211_channel *chan) 4891a1e1d21SSam Leffler { 4901a1e1d21SSam Leffler struct ieee80211_node *ni; 4911a1e1d21SSam Leffler int hash; 4921a1e1d21SSam Leffler 4931a1e1d21SSam Leffler hash = IEEE80211_NODE_HASH(macaddr); 4941a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 4951a1e1d21SSam Leffler LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { 4961a1e1d21SSam Leffler if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && ni->ni_chan == chan) { 4971a1e1d21SSam Leffler atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */ 4981a1e1d21SSam Leffler break; 4991a1e1d21SSam Leffler } 5001a1e1d21SSam Leffler } 5011a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 5021a1e1d21SSam Leffler return ni; 5031a1e1d21SSam Leffler } 5041a1e1d21SSam Leffler 5051a1e1d21SSam Leffler static void 5061a1e1d21SSam Leffler _ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) 5071a1e1d21SSam Leffler { 5080a915fadSSam Leffler KASSERT(ni != ic->ic_bss, ("freeing bss node")); 5090a915fadSSam Leffler 5101a1e1d21SSam Leffler TAILQ_REMOVE(&ic->ic_node, ni, ni_list); 5111a1e1d21SSam Leffler LIST_REMOVE(ni, ni_hash); 5121a1e1d21SSam Leffler if (TAILQ_EMPTY(&ic->ic_node)) 5131a1e1d21SSam Leffler ic->ic_inact_timer = 0; 5141a1e1d21SSam Leffler (*ic->ic_node_free)(ic, ni); 5151a1e1d21SSam Leffler } 5161a1e1d21SSam Leffler 5171a1e1d21SSam Leffler void 5181a1e1d21SSam Leffler ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) 5191a1e1d21SSam Leffler { 5200a915fadSSam Leffler KASSERT(ni != ic->ic_bss, ("freeing ic_bss")); 5210a915fadSSam Leffler 5221a1e1d21SSam Leffler /* XXX need equivalent of atomic_dec_and_test */ 5231a1e1d21SSam Leffler atomic_subtract_int(&ni->ni_refcnt, 1); 5241a1e1d21SSam Leffler if (atomic_cmpset_int(&ni->ni_refcnt, 0, 1)) { 5251a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 5261a1e1d21SSam Leffler _ieee80211_free_node(ic, ni); 5271a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 5281a1e1d21SSam Leffler } 5291a1e1d21SSam Leffler } 5301a1e1d21SSam Leffler 5311a1e1d21SSam Leffler void 5321a1e1d21SSam Leffler ieee80211_free_allnodes(struct ieee80211com *ic) 5331a1e1d21SSam Leffler { 5341a1e1d21SSam Leffler struct ieee80211_node *ni; 5351a1e1d21SSam Leffler 5361a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 5371a1e1d21SSam Leffler while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL) 5381a1e1d21SSam Leffler _ieee80211_free_node(ic, ni); 5391a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 5401a1e1d21SSam Leffler } 5411a1e1d21SSam Leffler 5421a1e1d21SSam Leffler void 5431a1e1d21SSam Leffler ieee80211_timeout_nodes(struct ieee80211com *ic) 5441a1e1d21SSam Leffler { 5451a1e1d21SSam Leffler struct ieee80211_node *ni, *nextbs; 5461a1e1d21SSam Leffler 5471a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 5481a1e1d21SSam Leffler for (ni = TAILQ_FIRST(&ic->ic_node); ni != NULL;) { 5490a915fadSSam Leffler if (++ni->ni_inact > IEEE80211_INACT_MAX) { 5501a1e1d21SSam Leffler IEEE80211_DPRINTF(("station %s timed out " 5511a1e1d21SSam Leffler "due to inactivity (%u secs)\n", 5521a1e1d21SSam Leffler ether_sprintf(ni->ni_macaddr), 5531a1e1d21SSam Leffler ni->ni_inact)); 5541a1e1d21SSam Leffler nextbs = TAILQ_NEXT(ni, ni_list); 5550a915fadSSam Leffler /* 5560a915fadSSam Leffler * Send a deauthenticate frame. 5570a915fadSSam Leffler */ 5581a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 5591a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, 5601a1e1d21SSam Leffler IEEE80211_REASON_AUTH_EXPIRE); 5610a915fadSSam Leffler ieee80211_free_node(ic, ni); 5621a1e1d21SSam Leffler ni = nextbs; 5630a915fadSSam Leffler } else 5640a915fadSSam Leffler ni = TAILQ_NEXT(ni, ni_list); 5651a1e1d21SSam Leffler } 5661a1e1d21SSam Leffler if (!TAILQ_EMPTY(&ic->ic_node)) 5671a1e1d21SSam Leffler ic->ic_inact_timer = IEEE80211_INACT_WAIT; 5681a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 5691a1e1d21SSam Leffler } 5701a1e1d21SSam Leffler 5711a1e1d21SSam Leffler void 5721a1e1d21SSam Leffler ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg) 5731a1e1d21SSam Leffler { 5741a1e1d21SSam Leffler struct ieee80211_node *ni; 5751a1e1d21SSam Leffler 5761a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 5771a1e1d21SSam Leffler TAILQ_FOREACH(ni, &ic->ic_node, ni_list) 5781a1e1d21SSam Leffler (*f)(arg, ni); 5791a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 5801a1e1d21SSam Leffler } 581