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