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