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 *); 731a1e1d21SSam Leffler static void ieee80211_setup_node(struct ieee80211com *ic, 741a1e1d21SSam Leffler struct ieee80211_node *ni, u_int8_t *macaddr); 751a1e1d21SSam Leffler static void _ieee80211_free_node(struct ieee80211com *, 761a1e1d21SSam Leffler struct ieee80211_node *); 771a1e1d21SSam Leffler 781a1e1d21SSam Leffler void 791a1e1d21SSam Leffler ieee80211_node_attach(struct ifnet *ifp) 801a1e1d21SSam Leffler { 811a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 821a1e1d21SSam Leffler 831a1e1d21SSam Leffler /* XXX need unit */ 841a1e1d21SSam Leffler mtx_init(&ic->ic_nodelock, ifp->if_name, "802.11 node table", MTX_DEF); 851a1e1d21SSam Leffler TAILQ_INIT(&ic->ic_node); 861a1e1d21SSam Leffler ic->ic_node_alloc = ieee80211_node_alloc; 871a1e1d21SSam Leffler ic->ic_node_free = ieee80211_node_free; 881a1e1d21SSam Leffler ic->ic_node_copy = ieee80211_node_copy; 892692bb26SSam Leffler } 902692bb26SSam Leffler 912692bb26SSam Leffler void 922692bb26SSam Leffler ieee80211_node_lateattach(struct ifnet *ifp) 932692bb26SSam Leffler { 942692bb26SSam Leffler struct ieee80211com *ic = (void *)ifp; 952692bb26SSam Leffler 961a1e1d21SSam Leffler ic->ic_bss = (*ic->ic_node_alloc)(ic); 9758f40303SSam Leffler KASSERT(ic->ic_bss != NULL, ("unable to setup inital BSS node")); 9858f40303SSam Leffler ic->ic_bss->ni_chan = IEEE80211_CHAN_ANYC; 991a1e1d21SSam Leffler } 1001a1e1d21SSam Leffler 1011a1e1d21SSam Leffler void 1021a1e1d21SSam Leffler ieee80211_node_detach(struct ifnet *ifp) 1031a1e1d21SSam Leffler { 1041a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 1051a1e1d21SSam Leffler 1061a1e1d21SSam Leffler if (ic->ic_bss != NULL) 1071a1e1d21SSam Leffler (*ic->ic_node_free)(ic, ic->ic_bss); 1081a1e1d21SSam Leffler ieee80211_free_allnodes(ic); 1091a1e1d21SSam Leffler mtx_destroy(&ic->ic_nodelock); 1101a1e1d21SSam Leffler } 1111a1e1d21SSam Leffler 1121a1e1d21SSam Leffler /* 1131a1e1d21SSam Leffler * AP scanning support. 1141a1e1d21SSam Leffler */ 1151a1e1d21SSam Leffler 1161a1e1d21SSam Leffler /* 1171a1e1d21SSam Leffler * Initialize the active channel set based on the set 1181a1e1d21SSam Leffler * of available channels and the current PHY mode. 1191a1e1d21SSam Leffler */ 120a11c9a5cSSam Leffler static void 1211a1e1d21SSam Leffler ieee80211_reset_scan(struct ifnet *ifp) 1221a1e1d21SSam Leffler { 1231a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 1241a1e1d21SSam Leffler 1251a1e1d21SSam Leffler memcpy(ic->ic_chan_scan, ic->ic_chan_active, 1261a1e1d21SSam Leffler sizeof(ic->ic_chan_active)); 127a11c9a5cSSam Leffler /* NB: hack, setup so next_scan starts with the first channel */ 128a11c9a5cSSam Leffler if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC) 129a11c9a5cSSam Leffler ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX]; 1301a1e1d21SSam Leffler } 1311a1e1d21SSam Leffler 1321a1e1d21SSam Leffler /* 1331a1e1d21SSam Leffler * Begin an active scan. 1341a1e1d21SSam Leffler */ 1351a1e1d21SSam Leffler void 136a11c9a5cSSam Leffler ieee80211_begin_scan(struct ifnet *ifp) 1371a1e1d21SSam Leffler { 1381a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 1391a1e1d21SSam Leffler 140a11c9a5cSSam Leffler /* 141a11c9a5cSSam Leffler * In all but hostap mode scanning starts off in 142a11c9a5cSSam Leffler * an active mode before switching to passive. 143a11c9a5cSSam Leffler */ 144a11c9a5cSSam Leffler if (ic->ic_opmode != IEEE80211_M_HOSTAP) 145a11c9a5cSSam Leffler ic->ic_flags |= IEEE80211_F_ASCAN; 1461a1e1d21SSam Leffler if (ifp->if_flags & IFF_DEBUG) 1471a1e1d21SSam Leffler if_printf(ifp, "begin %s scan\n", 148a11c9a5cSSam Leffler (ic->ic_flags & IEEE80211_F_ASCAN) ? 1491a1e1d21SSam Leffler "active" : "passive"); 1501a1e1d21SSam Leffler /* 151a11c9a5cSSam Leffler * Clear scan state and flush any previously seen 152a11c9a5cSSam Leffler * AP's. Note that the latter assumes we don't act 153a11c9a5cSSam Leffler * as both an AP and a station, otherwise we'll 154a11c9a5cSSam Leffler * potentially flush state of stations associated 155a11c9a5cSSam Leffler * with us. 1561a1e1d21SSam Leffler */ 157a11c9a5cSSam Leffler ieee80211_reset_scan(ifp); 1581a1e1d21SSam Leffler ieee80211_free_allnodes(ic); 1591a1e1d21SSam Leffler 160a11c9a5cSSam Leffler /* Scan the next channel. */ 161a11c9a5cSSam Leffler ieee80211_next_scan(ifp); 1621a1e1d21SSam Leffler } 1631a1e1d21SSam Leffler 1641a1e1d21SSam Leffler /* 1651a1e1d21SSam Leffler * Switch to the next channel marked for scanning. 1661a1e1d21SSam Leffler */ 1671a1e1d21SSam Leffler void 1681a1e1d21SSam Leffler ieee80211_next_scan(struct ifnet *ifp) 1691a1e1d21SSam Leffler { 1701a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 1711a1e1d21SSam Leffler struct ieee80211_channel *chan; 1721a1e1d21SSam Leffler 1731a1e1d21SSam Leffler chan = ic->ic_bss->ni_chan; 1741a1e1d21SSam Leffler for (;;) { 1751a1e1d21SSam Leffler if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX]) 1761a1e1d21SSam Leffler chan = &ic->ic_channels[0]; 1771a1e1d21SSam Leffler if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) { 1781a1e1d21SSam Leffler /* 1791a1e1d21SSam Leffler * Honor channels marked passive-only 1801a1e1d21SSam Leffler * during an active scan. 1811a1e1d21SSam Leffler */ 1821a1e1d21SSam Leffler if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 || 1831a1e1d21SSam Leffler (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) 1841a1e1d21SSam Leffler break; 1851a1e1d21SSam Leffler } 1861a1e1d21SSam Leffler if (chan == ic->ic_bss->ni_chan) { 1871a1e1d21SSam Leffler ieee80211_end_scan(ifp); 1881a1e1d21SSam Leffler return; 1891a1e1d21SSam Leffler } 1901a1e1d21SSam Leffler } 1911a1e1d21SSam Leffler clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan)); 1921a1e1d21SSam Leffler IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n", 1931a1e1d21SSam Leffler ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan), 1941a1e1d21SSam Leffler ieee80211_chan2ieee(ic, chan))); 1951a1e1d21SSam Leffler ic->ic_bss->ni_chan = chan; 196a11c9a5cSSam Leffler ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 1971a1e1d21SSam Leffler } 1981a1e1d21SSam Leffler 1991a1e1d21SSam Leffler void 2001a1e1d21SSam Leffler ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) 2011a1e1d21SSam Leffler { 2021a1e1d21SSam Leffler struct ieee80211_node *ni; 2031a1e1d21SSam Leffler struct ifnet *ifp = &ic->ic_if; 2041a1e1d21SSam Leffler 2051a1e1d21SSam Leffler ni = ic->ic_bss; 2061a1e1d21SSam Leffler if (ifp->if_flags & IFF_DEBUG) 2071a1e1d21SSam Leffler if_printf(ifp, "creating ibss\n"); 2081a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_SIBSS; 2091a1e1d21SSam Leffler ni->ni_chan = chan; 2101a1e1d21SSam Leffler ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; 2111a1e1d21SSam Leffler IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr); 2121a1e1d21SSam Leffler IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr); 2131a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) 2141a1e1d21SSam Leffler ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */ 2151a1e1d21SSam Leffler ni->ni_esslen = ic->ic_des_esslen; 2161a1e1d21SSam Leffler memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); 2171a1e1d21SSam Leffler ni->ni_rssi = 0; 2181a1e1d21SSam Leffler ni->ni_rstamp = 0; 2191a1e1d21SSam Leffler memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp)); 2201a1e1d21SSam Leffler ni->ni_intval = ic->ic_lintval; 2211a1e1d21SSam Leffler ni->ni_capinfo = IEEE80211_CAPINFO_IBSS; 2221a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_WEPON) 2231a1e1d21SSam Leffler ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; 2241a1e1d21SSam Leffler if (ic->ic_phytype == IEEE80211_T_FH) { 2251a1e1d21SSam Leffler ni->ni_fhdwell = 200; /* XXX */ 2261a1e1d21SSam Leffler ni->ni_fhindex = 1; 2271a1e1d21SSam Leffler } 228a11c9a5cSSam Leffler ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 2291a1e1d21SSam Leffler } 2301a1e1d21SSam Leffler 2311a1e1d21SSam Leffler /* 2321a1e1d21SSam Leffler * Complete a scan of potential channels. 2331a1e1d21SSam Leffler */ 2341a1e1d21SSam Leffler void 2351a1e1d21SSam Leffler ieee80211_end_scan(struct ifnet *ifp) 2361a1e1d21SSam Leffler { 2371a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 2381a1e1d21SSam Leffler struct ieee80211_node *ni, *nextbs, *selbs; 2391a1e1d21SSam Leffler u_int8_t rate; 2401a1e1d21SSam Leffler int i, fail; 2411a1e1d21SSam Leffler 2421a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_ASCAN; 2431a1e1d21SSam Leffler ni = TAILQ_FIRST(&ic->ic_node); 2441a1e1d21SSam Leffler 2451a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 2461a1e1d21SSam Leffler /* XXX off stack? */ 2471a1e1d21SSam Leffler u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)]; 2481a1e1d21SSam Leffler /* 2491a1e1d21SSam Leffler * The passive scan to look for existing AP's completed, 2501a1e1d21SSam Leffler * select a channel to camp on. Identify the channels 2511a1e1d21SSam Leffler * that already have one or more AP's and try to locate 2521a1e1d21SSam Leffler * an unnoccupied one. If that fails, pick a random 2531a1e1d21SSam Leffler * channel from the active set. 2541a1e1d21SSam Leffler */ 2551a1e1d21SSam Leffler for (; ni != NULL; ni = nextbs) { 2561a1e1d21SSam Leffler ieee80211_ref_node(ni); 2571a1e1d21SSam Leffler nextbs = TAILQ_NEXT(ni, ni_list); 2581a1e1d21SSam Leffler setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan)); 2591a1e1d21SSam Leffler ieee80211_free_node(ic, ni); 2601a1e1d21SSam Leffler } 2611a1e1d21SSam Leffler for (i = 0; i < IEEE80211_CHAN_MAX; i++) 2621a1e1d21SSam Leffler if (isset(ic->ic_chan_active, i) && isclr(occupied, i)) 2631a1e1d21SSam Leffler break; 2641a1e1d21SSam Leffler if (i == IEEE80211_CHAN_MAX) { 2651a1e1d21SSam Leffler fail = arc4random() & 3; /* random 0-3 */ 2661a1e1d21SSam Leffler for (i = 0; i < IEEE80211_CHAN_MAX; i++) 2671a1e1d21SSam Leffler if (isset(ic->ic_chan_active, i) && fail-- == 0) 2681a1e1d21SSam Leffler break; 2691a1e1d21SSam Leffler } 2701a1e1d21SSam Leffler ieee80211_create_ibss(ic, &ic->ic_channels[i]); 2711a1e1d21SSam Leffler return; 2721a1e1d21SSam Leffler } 2731a1e1d21SSam Leffler if (ni == NULL) { 2741a1e1d21SSam Leffler IEEE80211_DPRINTF(("%s: no scan candidate\n", __func__)); 2751a1e1d21SSam Leffler notfound: 2761a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && 2771a1e1d21SSam Leffler (ic->ic_flags & IEEE80211_F_IBSSON) && 2781a1e1d21SSam Leffler ic->ic_des_esslen != 0) { 2791a1e1d21SSam Leffler ieee80211_create_ibss(ic, ic->ic_ibss_chan); 2801a1e1d21SSam Leffler return; 2811a1e1d21SSam Leffler } 2821a1e1d21SSam Leffler /* 2831a1e1d21SSam Leffler * Reset the list of channels to scan and start again. 2841a1e1d21SSam Leffler */ 2851a1e1d21SSam Leffler ieee80211_reset_scan(ifp); 2861a1e1d21SSam Leffler ieee80211_next_scan(ifp); 2871a1e1d21SSam Leffler return; 2881a1e1d21SSam Leffler } 2891a1e1d21SSam Leffler selbs = NULL; 2901a1e1d21SSam Leffler if (ifp->if_flags & IFF_DEBUG) 2910a915fadSSam Leffler if_printf(ifp, "\tmacaddr bssid chan rssi rate flag wep essid\n"); 2921a1e1d21SSam Leffler for (; ni != NULL; ni = nextbs) { 2931a1e1d21SSam Leffler ieee80211_ref_node(ni); 2941a1e1d21SSam Leffler nextbs = TAILQ_NEXT(ni, ni_list); 2951a1e1d21SSam Leffler if (ni->ni_fails) { 2961a1e1d21SSam Leffler /* 2971a1e1d21SSam Leffler * The configuration of the access points may change 2981a1e1d21SSam Leffler * during my scan. So delete the entry for the AP 2991a1e1d21SSam Leffler * and retry to associate if there is another beacon. 3001a1e1d21SSam Leffler */ 3011a1e1d21SSam Leffler if (ni->ni_fails++ > 2) 3021a1e1d21SSam Leffler ieee80211_free_node(ic, ni); 3031a1e1d21SSam Leffler continue; 3041a1e1d21SSam Leffler } 3051a1e1d21SSam Leffler fail = 0; 3061a1e1d21SSam Leffler if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) 3071a1e1d21SSam Leffler fail |= 0x01; 3081a1e1d21SSam Leffler if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 3091a1e1d21SSam Leffler ni->ni_chan != ic->ic_des_chan) 3101a1e1d21SSam Leffler fail |= 0x01; 3111a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) { 3121a1e1d21SSam Leffler if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) 3131a1e1d21SSam Leffler fail |= 0x02; 3141a1e1d21SSam Leffler } else { 3151a1e1d21SSam Leffler if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) 3161a1e1d21SSam Leffler fail |= 0x02; 3171a1e1d21SSam Leffler } 3181a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_WEPON) { 3191a1e1d21SSam Leffler if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) 3201a1e1d21SSam Leffler fail |= 0x04; 3211a1e1d21SSam Leffler } else { 3221a1e1d21SSam Leffler if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) 3231a1e1d21SSam Leffler fail |= 0x04; 3241a1e1d21SSam Leffler } 3251a1e1d21SSam Leffler rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO); 3261a1e1d21SSam Leffler if (rate & IEEE80211_RATE_BASIC) 3271a1e1d21SSam Leffler fail |= 0x08; 3281a1e1d21SSam Leffler if (ic->ic_des_esslen != 0 && 3291a1e1d21SSam Leffler (ni->ni_esslen != ic->ic_des_esslen || 3301a1e1d21SSam Leffler memcmp(ni->ni_essid, ic->ic_des_essid, 3311a1e1d21SSam Leffler ic->ic_des_esslen != 0))) 3321a1e1d21SSam Leffler fail |= 0x10; 3331a1e1d21SSam Leffler if ((ic->ic_flags & IEEE80211_F_DESBSSID) && 3341a1e1d21SSam Leffler !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) 3351a1e1d21SSam Leffler fail |= 0x20; 3361a1e1d21SSam Leffler if (ifp->if_flags & IFF_DEBUG) { 3371a1e1d21SSam Leffler printf(" %c %s", fail ? '-' : '+', 3381a1e1d21SSam Leffler ether_sprintf(ni->ni_macaddr)); 3391a1e1d21SSam Leffler printf(" %s%c", ether_sprintf(ni->ni_bssid), 3401a1e1d21SSam Leffler fail & 0x20 ? '!' : ' '); 3411a1e1d21SSam Leffler printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan), 3421a1e1d21SSam Leffler fail & 0x01 ? '!' : ' '); 3431a1e1d21SSam Leffler printf(" %+4d", ni->ni_rssi); 3441a1e1d21SSam Leffler printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, 3451a1e1d21SSam Leffler fail & 0x08 ? '!' : ' '); 3461a1e1d21SSam Leffler printf(" %4s%c", 3471a1e1d21SSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : 3481a1e1d21SSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : 3491a1e1d21SSam Leffler "????", 3501a1e1d21SSam Leffler fail & 0x02 ? '!' : ' '); 3511a1e1d21SSam Leffler printf(" %3s%c ", 3521a1e1d21SSam Leffler (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? 3531a1e1d21SSam Leffler "wep" : "no", 3541a1e1d21SSam Leffler fail & 0x04 ? '!' : ' '); 3551a1e1d21SSam Leffler ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); 3561a1e1d21SSam Leffler printf("%s\n", fail & 0x10 ? "!" : ""); 3571a1e1d21SSam Leffler } 3581a1e1d21SSam Leffler if (!fail) { 3591a1e1d21SSam Leffler if (selbs == NULL) 3601a1e1d21SSam Leffler selbs = ni; 3611a1e1d21SSam Leffler else if (ni->ni_rssi > selbs->ni_rssi) { 3621a1e1d21SSam Leffler ieee80211_unref_node(&selbs); 3631a1e1d21SSam Leffler selbs = ni; 3641a1e1d21SSam Leffler } else 3651a1e1d21SSam Leffler ieee80211_unref_node(&ni); 3661a1e1d21SSam Leffler } else { 3671a1e1d21SSam Leffler ieee80211_unref_node(&ni); 3681a1e1d21SSam Leffler } 3691a1e1d21SSam Leffler } 3701a1e1d21SSam Leffler if (selbs == NULL) 3711a1e1d21SSam Leffler goto notfound; 3721a1e1d21SSam Leffler (*ic->ic_node_copy)(ic, ic->ic_bss, selbs); 3731a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) { 3741a1e1d21SSam Leffler ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE | 3751a1e1d21SSam Leffler IEEE80211_F_DONEGO | IEEE80211_F_DODEL); 3761a1e1d21SSam Leffler if (ic->ic_bss->ni_rates.rs_nrates == 0) { 3771a1e1d21SSam Leffler selbs->ni_fails++; 3781a1e1d21SSam Leffler ieee80211_unref_node(&selbs); 3791a1e1d21SSam Leffler goto notfound; 3801a1e1d21SSam Leffler } 3811a1e1d21SSam Leffler ieee80211_unref_node(&selbs); 382a11c9a5cSSam Leffler ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 3831a1e1d21SSam Leffler } else { 3841a1e1d21SSam Leffler ieee80211_unref_node(&selbs); 385a11c9a5cSSam Leffler ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); 3861a1e1d21SSam Leffler } 3871a1e1d21SSam Leffler } 3881a1e1d21SSam Leffler 3891a1e1d21SSam Leffler static struct ieee80211_node * 3901a1e1d21SSam Leffler ieee80211_node_alloc(struct ieee80211com *ic) 3911a1e1d21SSam Leffler { 3921a1e1d21SSam Leffler return malloc(sizeof(struct ieee80211_node), M_DEVBUF, 3931a1e1d21SSam Leffler M_NOWAIT | M_ZERO); 3941a1e1d21SSam Leffler } 3951a1e1d21SSam Leffler 3961a1e1d21SSam Leffler static void 3971a1e1d21SSam Leffler ieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni) 3981a1e1d21SSam Leffler { 3991a1e1d21SSam Leffler free(ni, M_DEVBUF); 4001a1e1d21SSam Leffler } 4011a1e1d21SSam Leffler 4021a1e1d21SSam Leffler static void 4031a1e1d21SSam Leffler ieee80211_node_copy(struct ieee80211com *ic, 4041a1e1d21SSam Leffler struct ieee80211_node *dst, const struct ieee80211_node *src) 4051a1e1d21SSam Leffler { 4061a1e1d21SSam Leffler *dst = *src; 4071a1e1d21SSam Leffler } 4081a1e1d21SSam Leffler 4091a1e1d21SSam Leffler static void 4101a1e1d21SSam Leffler ieee80211_setup_node(struct ieee80211com *ic, 4111a1e1d21SSam Leffler struct ieee80211_node *ni, u_int8_t *macaddr) 4121a1e1d21SSam Leffler { 4131a1e1d21SSam Leffler int hash; 4141a1e1d21SSam Leffler 4151a1e1d21SSam Leffler IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); 4161a1e1d21SSam Leffler hash = IEEE80211_NODE_HASH(macaddr); 4171a1e1d21SSam Leffler ni->ni_refcnt = 1; /* mark referenced */ 4181a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 4191a1e1d21SSam Leffler TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list); 4201a1e1d21SSam Leffler LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash); 4211a1e1d21SSam Leffler /* 4221a1e1d21SSam Leffler * Note we don't enable the inactive timer when acting 4231a1e1d21SSam Leffler * as a station. Nodes created in this mode represent 4241a1e1d21SSam Leffler * AP's identified while scanning. If we time them out 4251a1e1d21SSam Leffler * then several things happen: we can't return the data 4261a1e1d21SSam Leffler * to users to show the list of AP's we encountered, and 4271a1e1d21SSam Leffler * more importantly, we'll incorrectly deauthenticate 4281a1e1d21SSam Leffler * ourself because the inactivity timer will kick us off. 4291a1e1d21SSam Leffler */ 4301a1e1d21SSam Leffler if (ic->ic_opmode != IEEE80211_M_STA) 4311a1e1d21SSam Leffler ic->ic_inact_timer = IEEE80211_INACT_WAIT; 4321a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 4331a1e1d21SSam Leffler } 4341a1e1d21SSam Leffler 4351a1e1d21SSam Leffler struct ieee80211_node * 4361a1e1d21SSam Leffler ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr) 4371a1e1d21SSam Leffler { 4381a1e1d21SSam Leffler struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); 4391a1e1d21SSam Leffler if (ni != NULL) 4401a1e1d21SSam Leffler ieee80211_setup_node(ic, ni, macaddr); 4411a1e1d21SSam Leffler return ni; 4421a1e1d21SSam Leffler } 4431a1e1d21SSam Leffler 4441a1e1d21SSam Leffler struct ieee80211_node * 4451a1e1d21SSam Leffler ieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr) 4461a1e1d21SSam Leffler { 4471a1e1d21SSam Leffler struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); 4481a1e1d21SSam Leffler if (ni != NULL) { 4491a1e1d21SSam Leffler memcpy(ni, ic->ic_bss, sizeof(struct ieee80211_node)); 4501a1e1d21SSam Leffler ieee80211_setup_node(ic, ni, macaddr); 4511a1e1d21SSam Leffler } 4521a1e1d21SSam Leffler return ni; 4531a1e1d21SSam Leffler } 4541a1e1d21SSam Leffler 4551a1e1d21SSam Leffler struct ieee80211_node * 4561a1e1d21SSam Leffler ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) 4571a1e1d21SSam Leffler { 4581a1e1d21SSam Leffler struct ieee80211_node *ni; 4591a1e1d21SSam Leffler int hash; 4601a1e1d21SSam Leffler 4611a1e1d21SSam Leffler hash = IEEE80211_NODE_HASH(macaddr); 4621a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 4631a1e1d21SSam Leffler LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { 4641a1e1d21SSam Leffler if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { 4651a1e1d21SSam Leffler atomic_add_int(&ni->ni_refcnt, 1); /* mark referenced */ 4661a1e1d21SSam Leffler break; 4671a1e1d21SSam Leffler } 4681a1e1d21SSam Leffler } 4691a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 4701a1e1d21SSam Leffler return ni; 4711a1e1d21SSam Leffler } 4721a1e1d21SSam Leffler 4731a1e1d21SSam Leffler /* 4741a1e1d21SSam Leffler * Like find but search based on the channel too. 4751a1e1d21SSam Leffler */ 4761a1e1d21SSam Leffler struct ieee80211_node * 4771a1e1d21SSam Leffler ieee80211_lookup_node(struct ieee80211com *ic, 4781a1e1d21SSam Leffler u_int8_t *macaddr, struct ieee80211_channel *chan) 4791a1e1d21SSam Leffler { 4801a1e1d21SSam Leffler struct ieee80211_node *ni; 4811a1e1d21SSam Leffler int hash; 4821a1e1d21SSam Leffler 4831a1e1d21SSam Leffler hash = IEEE80211_NODE_HASH(macaddr); 4841a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 4851a1e1d21SSam Leffler LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { 4861a1e1d21SSam Leffler if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && ni->ni_chan == chan) { 4871a1e1d21SSam Leffler atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */ 4881a1e1d21SSam Leffler break; 4891a1e1d21SSam Leffler } 4901a1e1d21SSam Leffler } 4911a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 4921a1e1d21SSam Leffler return ni; 4931a1e1d21SSam Leffler } 4941a1e1d21SSam Leffler 4951a1e1d21SSam Leffler static void 4961a1e1d21SSam Leffler _ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) 4971a1e1d21SSam Leffler { 4980a915fadSSam Leffler KASSERT(ni != ic->ic_bss, ("freeing bss node")); 4990a915fadSSam Leffler 5001a1e1d21SSam Leffler TAILQ_REMOVE(&ic->ic_node, ni, ni_list); 5011a1e1d21SSam Leffler LIST_REMOVE(ni, ni_hash); 5021a1e1d21SSam Leffler if (TAILQ_EMPTY(&ic->ic_node)) 5031a1e1d21SSam Leffler ic->ic_inact_timer = 0; 5041a1e1d21SSam Leffler (*ic->ic_node_free)(ic, ni); 5051a1e1d21SSam Leffler } 5061a1e1d21SSam Leffler 5071a1e1d21SSam Leffler void 5081a1e1d21SSam Leffler ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) 5091a1e1d21SSam Leffler { 5100a915fadSSam Leffler KASSERT(ni != ic->ic_bss, ("freeing ic_bss")); 5110a915fadSSam Leffler 5121a1e1d21SSam Leffler /* XXX need equivalent of atomic_dec_and_test */ 5131a1e1d21SSam Leffler atomic_subtract_int(&ni->ni_refcnt, 1); 5141a1e1d21SSam Leffler if (atomic_cmpset_int(&ni->ni_refcnt, 0, 1)) { 5151a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 5161a1e1d21SSam Leffler _ieee80211_free_node(ic, ni); 5171a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 5181a1e1d21SSam Leffler } 5191a1e1d21SSam Leffler } 5201a1e1d21SSam Leffler 5211a1e1d21SSam Leffler void 5221a1e1d21SSam Leffler ieee80211_free_allnodes(struct ieee80211com *ic) 5231a1e1d21SSam Leffler { 5241a1e1d21SSam Leffler struct ieee80211_node *ni; 5251a1e1d21SSam Leffler 5261a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 5271a1e1d21SSam Leffler while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL) 5281a1e1d21SSam Leffler _ieee80211_free_node(ic, ni); 5291a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 5301a1e1d21SSam Leffler } 5311a1e1d21SSam Leffler 5321a1e1d21SSam Leffler void 5331a1e1d21SSam Leffler ieee80211_timeout_nodes(struct ieee80211com *ic) 5341a1e1d21SSam Leffler { 5351a1e1d21SSam Leffler struct ieee80211_node *ni, *nextbs; 5361a1e1d21SSam Leffler 5371a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 5381a1e1d21SSam Leffler for (ni = TAILQ_FIRST(&ic->ic_node); ni != NULL;) { 5390a915fadSSam Leffler if (++ni->ni_inact > IEEE80211_INACT_MAX) { 5401a1e1d21SSam Leffler IEEE80211_DPRINTF(("station %s timed out " 5411a1e1d21SSam Leffler "due to inactivity (%u secs)\n", 5421a1e1d21SSam Leffler ether_sprintf(ni->ni_macaddr), 5431a1e1d21SSam Leffler ni->ni_inact)); 5441a1e1d21SSam Leffler nextbs = TAILQ_NEXT(ni, ni_list); 5450a915fadSSam Leffler /* 5460a915fadSSam Leffler * Send a deauthenticate frame. 5470a915fadSSam Leffler */ 5481a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 5491a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, 5501a1e1d21SSam Leffler IEEE80211_REASON_AUTH_EXPIRE); 5510a915fadSSam Leffler ieee80211_free_node(ic, ni); 5521a1e1d21SSam Leffler ni = nextbs; 5530a915fadSSam Leffler } else 5540a915fadSSam Leffler ni = TAILQ_NEXT(ni, ni_list); 5551a1e1d21SSam Leffler } 5561a1e1d21SSam Leffler if (!TAILQ_EMPTY(&ic->ic_node)) 5571a1e1d21SSam Leffler ic->ic_inact_timer = IEEE80211_INACT_WAIT; 5581a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 5591a1e1d21SSam Leffler } 5601a1e1d21SSam Leffler 5611a1e1d21SSam Leffler void 5621a1e1d21SSam Leffler ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg) 5631a1e1d21SSam Leffler { 5641a1e1d21SSam Leffler struct ieee80211_node *ni; 5651a1e1d21SSam Leffler 5661a1e1d21SSam Leffler mtx_lock(&ic->ic_nodelock); 5671a1e1d21SSam Leffler TAILQ_FOREACH(ni, &ic->ic_node, ni_list) 5681a1e1d21SSam Leffler (*f)(arg, ni); 5691a1e1d21SSam Leffler mtx_unlock(&ic->ic_nodelock); 5701a1e1d21SSam Leffler } 571