#include <sys/cdefs.h>
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/endian.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_input.h>
#include <net80211/ieee80211_regdomain.h>
#ifdef IEEE80211_SUPPORT_TDMA
#include <net80211/ieee80211_tdma.h>
#endif
#ifdef IEEE80211_SUPPORT_MESH
#include <net80211/ieee80211_mesh.h>
#endif
#include <net80211/ieee80211_ratectl.h>
#include <net80211/ieee80211_vht.h>
#include <net/bpf.h>
#define STA_FAILS_MAX 2
#define STA_FAILS_AGE (2*60)
#define STA_PURGE_SCANS 2
#define STA_RSSI_MIN 8
#define STA_RSSI_MAX 40
struct sta_entry {
struct ieee80211_scan_entry base;
TAILQ_ENTRY(sta_entry) se_list;
LIST_ENTRY(sta_entry) se_hash;
uint8_t se_fails;
uint8_t se_seen;
uint8_t se_notseen;
uint8_t se_flags;
#define STA_DEMOTE11B 0x01
uint32_t se_avgrssi;
unsigned long se_lastupdate;
unsigned long se_lastfail;
unsigned long se_lastassoc;
u_int se_scangen;
u_int se_countrygen;
};
#define STA_HASHSIZE 32
#define STA_HASH(addr) \
(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % STA_HASHSIZE)
#define MAX_IEEE_CHAN 256
CTASSERT(MAX_IEEE_CHAN >= 256);
struct sta_table {
ieee80211_scan_table_lock_t st_lock;
TAILQ_HEAD(, sta_entry) st_entry;
LIST_HEAD(, sta_entry) st_hash[STA_HASHSIZE];
ieee80211_scan_iter_lock_t st_scanlock;
u_int st_scaniter;
u_int st_scangen;
int st_newscan;
int st_maxrssi[MAX_IEEE_CHAN];
};
static void sta_flush_table(struct sta_table *);
#define MATCH_CHANNEL 0x00001
#define MATCH_CAPINFO 0x00002
#define MATCH_PRIVACY 0x00004
#define MATCH_RATE 0x00008
#define MATCH_SSID 0x00010
#define MATCH_BSSID 0x00020
#define MATCH_FAILS 0x00040
#define MATCH_NOTSEEN 0x00080
#define MATCH_RSSI 0x00100
#define MATCH_CC 0x00200
#ifdef IEEE80211_SUPPORT_TDMA
#define MATCH_TDMA_NOIE 0x00400
#define MATCH_TDMA_NOTMASTER 0x00800
#define MATCH_TDMA_NOSLOT 0x01000
#define MATCH_TDMA_LOCAL 0x02000
#define MATCH_TDMA_VERSION 0x04000
#endif
#define MATCH_MESH_NOID 0x10000
#define MATCH_MESHID 0x20000
static int match_bss(struct ieee80211vap *,
const struct ieee80211_scan_state *, struct sta_entry *, int);
static void adhoc_age(struct ieee80211_scan_state *);
static __inline int
isocmp(const uint8_t cc1[], const uint8_t cc2[])
{
return (cc1[0] == cc2[0] && cc1[1] == cc2[1]);
}
static int nrefs = 0;
IEEE80211_SCANNER_MODULE(sta, 1);
static int
sta_attach(struct ieee80211_scan_state *ss)
{
struct sta_table *st;
st = (struct sta_table *) IEEE80211_MALLOC(sizeof(struct sta_table),
M_80211_SCAN,
IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (st == NULL)
return 0;
IEEE80211_SCAN_TABLE_LOCK_INIT(st, "scantable");
IEEE80211_SCAN_ITER_LOCK_INIT(st, "scangen");
TAILQ_INIT(&st->st_entry);
ss->ss_priv = st;
nrefs++;
return 1;
}
static int
sta_detach(struct ieee80211_scan_state *ss)
{
struct sta_table *st = ss->ss_priv;
if (st != NULL) {
sta_flush_table(st);
IEEE80211_SCAN_TABLE_LOCK_DESTROY(st);
IEEE80211_SCAN_ITER_LOCK_DESTROY(st);
IEEE80211_FREE(st, M_80211_SCAN);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--;
}
return 1;
}
static int
sta_flush(struct ieee80211_scan_state *ss)
{
struct sta_table *st = ss->ss_priv;
IEEE80211_SCAN_TABLE_LOCK(st);
sta_flush_table(st);
IEEE80211_SCAN_TABLE_UNLOCK(st);
ss->ss_last = 0;
return 0;
}
static void
sta_flush_table(struct sta_table *st)
{
struct sta_entry *se, *next;
TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
TAILQ_REMOVE(&st->st_entry, se, se_list);
LIST_REMOVE(se, se_hash);
ieee80211_ies_cleanup(&se->base.se_ies);
IEEE80211_FREE(se, M_80211_SCAN);
}
memset(st->st_maxrssi, 0, sizeof(st->st_maxrssi));
}
static int
sta_add(struct ieee80211_scan_state *ss,
struct ieee80211_channel *curchan,
const struct ieee80211_scanparams *sp,
const struct ieee80211_frame *wh,
int subtype, int rssi, int noise)
{
#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
#define PICK1ST(_ss) \
((ss->ss_flags & (IEEE80211_SCAN_PICK1ST | IEEE80211_SCAN_GOTPICK)) == \
IEEE80211_SCAN_PICK1ST)
struct sta_table *st = ss->ss_priv;
const uint8_t *macaddr = wh->i_addr2;
struct ieee80211vap *vap = ss->ss_vap;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c;
struct sta_entry *se;
struct ieee80211_scan_entry *ise;
int hash;
hash = STA_HASH(macaddr);
IEEE80211_SCAN_TABLE_LOCK(st);
LIST_FOREACH(se, &st->st_hash[hash], se_hash)
if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr))
goto found;
se = (struct sta_entry *) IEEE80211_MALLOC(sizeof(struct sta_entry),
M_80211_SCAN, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (se == NULL) {
IEEE80211_SCAN_TABLE_UNLOCK(st);
return 0;
}
se->se_scangen = st->st_scaniter-1;
se->se_avgrssi = IEEE80211_RSSI_DUMMY_MARKER;
IEEE80211_ADDR_COPY(se->base.se_macaddr, macaddr);
TAILQ_INSERT_TAIL(&st->st_entry, se, se_list);
LIST_INSERT_HEAD(&st->st_hash[hash], se, se_hash);
found:
ise = &se->base;
if (sp->ssid[1] != 0 &&
(ISPROBE(subtype) || ise->se_ssid[1] == 0))
memcpy(ise->se_ssid, sp->ssid, 2+sp->ssid[1]);
KASSERT(sp->rates[1] <= IEEE80211_RATE_MAXSIZE,
("rate set too large: %u", sp->rates[1]));
memcpy(ise->se_rates, sp->rates, 2+sp->rates[1]);
if (sp->xrates != NULL) {
KASSERT(sp->xrates[1] <= IEEE80211_RATE_MAXSIZE,
("xrate set too large: %u", sp->xrates[1]));
memcpy(ise->se_xrates, sp->xrates, 2+sp->xrates[1]);
} else
ise->se_xrates[1] = 0;
IEEE80211_ADDR_COPY(ise->se_bssid, wh->i_addr3);
if ((sp->status & IEEE80211_BPARSE_OFFCHAN) == 0) {
IEEE80211_RSSI_LPF(se->se_avgrssi, rssi);
ise->se_rssi = IEEE80211_RSSI_GET(se->se_avgrssi);
ise->se_noise = noise;
}
memcpy(ise->se_tstamp.data, sp->tstamp, sizeof(ise->se_tstamp));
ise->se_intval = sp->bintval;
ise->se_capinfo = sp->capinfo;
#ifdef IEEE80211_SUPPORT_MESH
if (sp->meshid != NULL && sp->meshid[1] != 0)
memcpy(ise->se_meshid, sp->meshid, 2+sp->meshid[1]);
#endif
if (sp->status & IEEE80211_BPARSE_OFFCHAN) {
c = ieee80211_find_channel_byieee(ic, sp->chan,
curchan->ic_flags);
if (c != NULL) {
ise->se_chan = c;
} else if (ise->se_chan == NULL) {
ise->se_chan = curchan;
}
} else
ise->se_chan = curchan;
if (IEEE80211_IS_CHAN_VHT(ise->se_chan) && sp->vhtcap == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N,
"%s: demoting VHT->HT %d/0x%08x\n",
__func__, ise->se_chan->ic_freq, ise->se_chan->ic_flags);
c = ieee80211_find_channel(ic, ise->se_chan->ic_freq,
ise->se_chan->ic_flags & ~IEEE80211_CHAN_VHT);
KASSERT(c != NULL,
("no non-VHT channel %u", ise->se_chan->ic_ieee));
ise->se_chan = c;
}
if (IEEE80211_IS_CHAN_HT(ise->se_chan) && sp->htcap == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N,
"%s: demoting HT->legacy %d/0x%08x\n",
__func__, ise->se_chan->ic_freq, ise->se_chan->ic_flags);
c = ieee80211_find_channel(ic, ise->se_chan->ic_freq,
ise->se_chan->ic_flags & ~IEEE80211_CHAN_HT);
KASSERT(c != NULL,
("no legacy channel %u", ise->se_chan->ic_ieee));
ise->se_chan = c;
}
ise->se_fhdwell = sp->fhdwell;
ise->se_fhindex = sp->fhindex;
ise->se_erp = sp->erp;
ise->se_timoff = sp->timoff;
if (sp->tim != NULL) {
const struct ieee80211_tim_ie *tim =
(const struct ieee80211_tim_ie *) sp->tim;
ise->se_dtimperiod = tim->tim_period;
}
if (sp->country != NULL) {
const struct ieee80211_country_ie *cie =
(const struct ieee80211_country_ie *) sp->country;
if ((IEEE80211_IS_CHAN_11D(ise->se_chan) ||
(vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) &&
(ic->ic_regdomain.country == CTRY_DEFAULT ||
!isocmp(cie->cc, ic->ic_regdomain.isocc))) {
if (se->se_countrygen != st->st_scangen) {
ieee80211_notify_country(vap, ise->se_bssid,
cie->cc);
se->se_countrygen = st->st_scangen;
}
}
ise->se_cc[0] = cie->cc[0];
ise->se_cc[1] = cie->cc[1];
}
(void) ieee80211_ies_init(&ise->se_ies, sp->ies, sp->ies_len);
if (se->se_fails && (ticks - se->se_lastfail) > STA_FAILS_AGE*hz) {
se->se_fails = 0;
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, macaddr,
"%s: fails %u", __func__, se->se_fails);
}
se->se_lastupdate = ticks;
se->se_seen = 1;
se->se_notseen = 0;
KASSERT(sizeof(sp->bchan) == 1, ("bchan size"));
if (rssi > st->st_maxrssi[sp->bchan])
st->st_maxrssi[sp->bchan] = rssi;
IEEE80211_SCAN_TABLE_UNLOCK(st);
if (PICK1ST(ss) && match_bss(vap, ss, se, IEEE80211_MSG_SCAN) == 0)
ss->ss_flags |= IEEE80211_SCAN_GOTPICK;
return 1;
#undef PICK1ST
#undef ISPROBE
}
static int
isexcluded(struct ieee80211vap *vap, const struct ieee80211_channel *c)
{
return (isclr(vap->iv_ic->ic_chan_active, c->ic_ieee) ||
(vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
c->ic_freq != vap->iv_des_chan->ic_freq));
}
static struct ieee80211_channel *
find11gchannel(struct ieee80211com *ic, int i, int freq)
{
struct ieee80211_channel *c;
int j;
for (j = i+1; j < ic->ic_nchans; j++) {
c = &ic->ic_channels[j];
if (c->ic_freq == freq && IEEE80211_IS_CHAN_G(c))
return c;
}
for (j = 0; j < i; j++) {
c = &ic->ic_channels[j];
if (c->ic_freq == freq && IEEE80211_IS_CHAN_G(c))
return c;
}
return NULL;
}
static const u_int chanflags[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = IEEE80211_CHAN_B,
[IEEE80211_MODE_11A] = IEEE80211_CHAN_A,
[IEEE80211_MODE_11B] = IEEE80211_CHAN_B,
[IEEE80211_MODE_11G] = IEEE80211_CHAN_G,
[IEEE80211_MODE_FH] = IEEE80211_CHAN_FHSS,
[IEEE80211_MODE_TURBO_A] = IEEE80211_CHAN_A,
[IEEE80211_MODE_TURBO_G] = IEEE80211_CHAN_G,
[IEEE80211_MODE_STURBO_A] = IEEE80211_CHAN_ST,
[IEEE80211_MODE_HALF] = IEEE80211_CHAN_HALF,
[IEEE80211_MODE_QUARTER] = IEEE80211_CHAN_QUARTER,
[IEEE80211_MODE_11NA] = IEEE80211_CHAN_A,
[IEEE80211_MODE_11NG] = IEEE80211_CHAN_G,
[IEEE80211_MODE_VHT_5GHZ] = IEEE80211_CHAN_A,
[IEEE80211_MODE_VHT_2GHZ] = IEEE80211_CHAN_G,
};
static void
add_channels(struct ieee80211vap *vap,
struct ieee80211_scan_state *ss,
enum ieee80211_phymode mode, const uint16_t freq[], int nfreq)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c, *cg;
u_int modeflags;
int i;
KASSERT(mode < nitems(chanflags), ("Unexpected mode %u", mode));
modeflags = chanflags[mode];
for (i = 0; i < nfreq; i++) {
if (ss->ss_last >= IEEE80211_SCAN_MAX)
break;
c = ieee80211_find_channel(ic, freq[i], modeflags);
if (c == NULL || isexcluded(vap, c))
continue;
if (mode == IEEE80211_MODE_AUTO) {
KASSERT(IEEE80211_IS_CHAN_B(c),
("%s: wrong channel for 'auto' mode %u / %u\n",
__func__, c->ic_freq, c->ic_flags));
if ((cg = find11gchannel(ic, i, c->ic_freq)) != NULL)
c = cg;
}
ss->ss_chans[ss->ss_last++] = c;
}
}
struct scanlist {
uint16_t mode;
uint16_t count;
const uint16_t *list;
};
static int
checktable(const struct scanlist *scan, const struct ieee80211_channel *c)
{
int i;
for (; scan->list != NULL; scan++) {
for (i = 0; i < scan->count; i++)
if (scan->list[i] == c->ic_freq)
return 1;
}
return 0;
}
static int
onscanlist(const struct ieee80211_scan_state *ss,
const struct ieee80211_channel *c)
{
int i;
for (i = 0; i < ss->ss_last; i++)
if (ss->ss_chans[i] == c)
return 1;
return 0;
}
static void
sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211vap *vap,
const struct scanlist table[])
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c;
int i;
for (i = 0; i < ic->ic_nchans; i++) {
if (ss->ss_last >= IEEE80211_SCAN_MAX)
break;
c = &ic->ic_channels[i];
if (IEEE80211_IS_CHAN_DTURBO(c) || IEEE80211_IS_CHAN_HT(c) ||
IEEE80211_IS_CHAN_VHT(c))
continue;
if (vap->iv_des_mode != IEEE80211_MODE_AUTO &&
vap->iv_des_mode != ieee80211_chan2mode(c))
continue;
if (isexcluded(vap, c))
continue;
if (checktable(table, c))
continue;
ss->ss_chans[ss->ss_last++] = c;
}
c = vap->iv_des_chan;
if (c != IEEE80211_CHAN_ANYC &&
!onscanlist(ss, c) &&
(vap->iv_des_mode == IEEE80211_MODE_AUTO ||
vap->iv_des_mode == ieee80211_chan2mode(c)) &&
ss->ss_last < IEEE80211_SCAN_MAX)
ss->ss_chans[ss->ss_last++] = c;
}
static void
makescanlist(struct ieee80211_scan_state *ss, struct ieee80211vap *vap,
const struct scanlist table[])
{
const struct scanlist *scan;
enum ieee80211_phymode mode;
ss->ss_last = 0;
for (scan = table; scan->list != NULL; scan++) {
mode = scan->mode;
switch (mode) {
case IEEE80211_MODE_11B:
if (vap->iv_des_mode == IEEE80211_MODE_11B)
break;
if (vap->iv_des_mode == IEEE80211_MODE_AUTO ||
vap->iv_des_mode == IEEE80211_MODE_11G ||
vap->iv_des_mode == IEEE80211_MODE_11NG ||
vap->iv_des_mode == IEEE80211_MODE_VHT_2GHZ) {
mode = vap->iv_des_mode;
break;
}
continue;
case IEEE80211_MODE_11A:
if (vap->iv_des_mode == IEEE80211_MODE_11NA ||
vap->iv_des_mode == IEEE80211_MODE_VHT_5GHZ) {
mode = vap->iv_des_mode;
break;
}
default:
if (vap->iv_des_mode != IEEE80211_MODE_AUTO &&
vap->iv_des_mode != mode)
continue;
}
#ifdef IEEE80211_F_XR
if ((vap->iv_flags & IEEE80211_F_XR) &&
(mode == IEEE80211_MODE_TURBO_A ||
mode == IEEE80211_MODE_TURBO_G ||
mode == IEEE80211_MODE_STURBO_A))
continue;
#endif
add_channels(vap, ss, mode, scan->list, scan->count);
}
sweepchannels(ss, vap, table);
}
static const uint16_t rcl1[] =
{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 };
static const uint16_t rcl2[] =
{ 5170, 5190, 5210, 5230 };
static const uint16_t rcl3[] =
{ 2412, 2437, 2462, 2442, 2472 };
static const uint16_t rcl4[] =
{ 5745, 5765, 5785, 5805, 5825 };
static const uint16_t rcl7[] =
{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 };
static const uint16_t rcl8[] =
{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 };
static const uint16_t rcl9[] =
{ 2484 };
static const uint16_t rcl10[] =
{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 };
static const uint16_t rcl11[] =
{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 };
#ifdef ATH_TURBO_SCAN
static const uint16_t rcl5[] =
{ 5210, 5250, 5290 };
static const uint16_t rcl6[] =
{ 5760, 5800 };
static const uint16_t rcl6x[] =
{ 5540, 5580, 5620, 5660 };
static const uint16_t rcl12[] =
{ 2437 };
static const uint16_t rcl13[] =
{ 5200, 5240, 5280, 5765, 5805 };
#endif
#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a
static const struct scanlist staScanTable[] = {
{ IEEE80211_MODE_11B, X(rcl3) },
{ IEEE80211_MODE_11A, X(rcl1) },
{ IEEE80211_MODE_11A, X(rcl2) },
{ IEEE80211_MODE_11B, X(rcl8) },
{ IEEE80211_MODE_11B, X(rcl9) },
{ IEEE80211_MODE_11A, X(rcl4) },
#ifdef ATH_TURBO_SCAN
{ IEEE80211_MODE_STURBO_A, X(rcl5) },
{ IEEE80211_MODE_STURBO_A, X(rcl6) },
{ IEEE80211_MODE_TURBO_A, X(rcl6x) },
{ IEEE80211_MODE_TURBO_A, X(rcl13) },
#endif
{ IEEE80211_MODE_11A, X(rcl7) },
{ IEEE80211_MODE_11B, X(rcl10) },
{ IEEE80211_MODE_11A, X(rcl11) },
#ifdef ATH_TURBO_SCAN
{ IEEE80211_MODE_TURBO_G, X(rcl12) },
#endif
{ .list = NULL }
};
static int
sta_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
makescanlist(ss, vap, staScanTable);
if (ss->ss_mindwell == 0)
ss->ss_mindwell = msecs_to_ticks(20);
if (ss->ss_maxdwell == 0)
ss->ss_maxdwell = msecs_to_ticks(200);
st->st_scangen++;
st->st_newscan = 1;
return 0;
}
static int
sta_restart(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
st->st_newscan = 1;
return 0;
}
static int
sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
return 0;
}
static struct ieee80211_channel *
demote11b(struct ieee80211vap *vap, struct ieee80211_channel *chan)
{
struct ieee80211_channel *c;
if (IEEE80211_IS_CHAN_ANYG(chan) &&
vap->iv_des_mode == IEEE80211_MODE_AUTO) {
c = ieee80211_find_channel(vap->iv_ic, chan->ic_freq,
(chan->ic_flags &~ (IEEE80211_CHAN_PUREG | IEEE80211_CHAN_G)) |
IEEE80211_CHAN_B);
if (c != NULL)
chan = c;
}
return chan;
}
static int
maxrate(const struct ieee80211_scan_entry *se)
{
const struct ieee80211_ie_htcap *htcap =
(const struct ieee80211_ie_htcap *) se->se_ies.htcap_ie;
int rmax, r, i, txstream;
uint16_t caps;
uint8_t txparams;
rmax = 0;
if (htcap != NULL) {
txparams = htcap->hc_mcsset[12];
if (txparams & 0x3) {
txstream = ((txparams & 0xc) >> 2) + 1;
i = txstream * 8 - 1;
} else
for (i = 31; i >= 0 && isclr(htcap->hc_mcsset, i); i--);
if (i >= 0) {
caps = le16dec(&htcap->hc_cap);
if ((caps & IEEE80211_HTCAP_CHWIDTH40) &&
(caps & IEEE80211_HTCAP_SHORTGI40))
rmax = ieee80211_htrates[i].ht40_rate_400ns;
else if (caps & IEEE80211_HTCAP_CHWIDTH40)
rmax = ieee80211_htrates[i].ht40_rate_800ns;
else if (caps & IEEE80211_HTCAP_SHORTGI20)
rmax = ieee80211_htrates[i].ht20_rate_400ns;
else
rmax = ieee80211_htrates[i].ht20_rate_800ns;
}
}
for (i = 0; i < se->se_rates[1]; i++) {
r = se->se_rates[2+i] & IEEE80211_RATE_VAL;
if (r > rmax)
rmax = r;
}
for (i = 0; i < se->se_xrates[1]; i++) {
r = se->se_xrates[2+i] & IEEE80211_RATE_VAL;
if (r > rmax)
rmax = r;
}
return rmax;
}
static int
sta_compare(const struct sta_entry *a, const struct sta_entry *b)
{
#define PREFER(_a,_b,_what) do { \
if (((_a) ^ (_b)) & (_what)) \
return ((_a) & (_what)) ? 1 : -1; \
} while (0)
int maxa, maxb;
int8_t rssia, rssib;
int weight;
PREFER(a->base.se_capinfo, b->base.se_capinfo,
IEEE80211_CAPINFO_PRIVACY);
weight = b->se_fails - a->se_fails;
if (abs(weight) > 1)
return weight;
rssia = MIN(a->base.se_rssi, STA_RSSI_MAX);
rssib = MIN(b->base.se_rssi, STA_RSSI_MAX);
if (abs(rssib - rssia) < 5) {
maxa = maxrate(&a->base);
maxb = maxrate(&b->base);
if (maxa != maxb)
return maxa - maxb;
PREFER(IEEE80211_IS_CHAN_5GHZ(a->base.se_chan),
IEEE80211_IS_CHAN_5GHZ(b->base.se_chan), 1);
}
return a->base.se_rssi - b->base.se_rssi;
#undef PREFER
}
static int
check_rate(struct ieee80211vap *vap, const struct ieee80211_channel *chan,
const struct ieee80211_scan_entry *se)
{
const struct ieee80211_rateset *srs;
int i, j, nrs, r, okrate, badrate, fixedrate, ucastrate;
const uint8_t *rs;
okrate = badrate = 0;
srs = ieee80211_get_suprates(vap->iv_ic, chan);
nrs = se->se_rates[1];
rs = se->se_rates+2;
ucastrate = vap->iv_txparms[ieee80211_chan2mode(chan)].ucastrate;
fixedrate = IEEE80211_FIXED_RATE_NONE;
again:
for (i = 0; i < nrs; i++) {
r = IEEE80211_RV(rs[i]);
badrate = r;
if (r == ucastrate)
fixedrate = r;
for (j = 0; j < srs->rs_nrates; j++)
if (r == IEEE80211_RV(srs->rs_rates[j])) {
if (r > okrate)
okrate = r;
break;
}
if (j == srs->rs_nrates && (rs[i] & IEEE80211_RATE_BASIC)) {
okrate = 0;
goto back;
}
}
if (rs == se->se_rates+2) {
nrs = se->se_xrates[1];
rs = se->se_xrates+2;
goto again;
}
back:
if (okrate == 0 || ucastrate != fixedrate)
return badrate | IEEE80211_RATE_BASIC;
else
return IEEE80211_RV(okrate);
}
static __inline int
match_id(const uint8_t *ie, const uint8_t *val, int len)
{
return (ie[1] == len && memcmp(ie+2, val, len) == 0);
}
static int
match_ssid(const uint8_t *ie,
int nssid, const struct ieee80211_scan_ssid ssids[])
{
int i;
for (i = 0; i < nssid; i++) {
if (match_id(ie, ssids[i].ssid, ssids[i].len))
return 1;
}
return 0;
}
#ifdef IEEE80211_SUPPORT_TDMA
static int
tdma_isfull(const struct ieee80211_tdma_param *tdma)
{
int slot, slotcnt;
slotcnt = tdma->tdma_slotcnt;
for (slot = slotcnt-1; slot >= 0; slot--)
if (isclr(tdma->tdma_inuse, slot))
return 0;
return 1;
}
#endif
static int
match_bss(struct ieee80211vap *vap,
const struct ieee80211_scan_state *ss, struct sta_entry *se0,
int debug)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_entry *se = &se0->base;
uint8_t rate;
int fail;
fail = 0;
if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan)))
fail |= MATCH_CHANNEL;
if (vap->iv_des_mode != IEEE80211_MODE_AUTO &&
(se->se_chan->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
chanflags[vap->iv_des_mode])
fail |= MATCH_CHANNEL;
if (vap->iv_opmode == IEEE80211_M_IBSS) {
if ((se->se_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
fail |= MATCH_CAPINFO;
#ifdef IEEE80211_SUPPORT_TDMA
} else if (vap->iv_opmode == IEEE80211_M_AHDEMO) {
if (se->se_capinfo & (IEEE80211_CAPINFO_IBSS|IEEE80211_CAPINFO_ESS))
fail |= MATCH_CAPINFO;
if (vap->iv_caps & IEEE80211_C_TDMA) {
const struct ieee80211_tdma_param *tdma =
(const struct ieee80211_tdma_param *)se->se_ies.tdma_ie;
const struct ieee80211_tdma_state *ts = vap->iv_tdma;
if (tdma == NULL)
fail |= MATCH_TDMA_NOIE;
else if (tdma->tdma_version != ts->tdma_version)
fail |= MATCH_TDMA_VERSION;
else if (tdma->tdma_slot != 0)
fail |= MATCH_TDMA_NOTMASTER;
else if (tdma_isfull(tdma))
fail |= MATCH_TDMA_NOSLOT;
#if 0
else if (ieee80211_local_address(se->se_macaddr))
fail |= MATCH_TDMA_LOCAL;
#endif
}
#endif
#ifdef IEEE80211_SUPPORT_MESH
} else if (vap->iv_opmode == IEEE80211_M_MBSS) {
const struct ieee80211_mesh_state *ms = vap->iv_mesh;
if (se->se_capinfo & (IEEE80211_CAPINFO_IBSS|IEEE80211_CAPINFO_ESS))
fail |= MATCH_CAPINFO;
else if (se->se_meshid[0] != IEEE80211_ELEMID_MESHID)
fail |= MATCH_MESH_NOID;
else if (ms->ms_idlen != 0 &&
match_id(se->se_meshid, ms->ms_id, ms->ms_idlen))
fail |= MATCH_MESHID;
#endif
} else {
if ((se->se_capinfo & IEEE80211_CAPINFO_ESS) == 0)
fail |= MATCH_CAPINFO;
if ((IEEE80211_IS_CHAN_11D(se->se_chan) ||
(vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) &&
se->se_cc[0] != 0 &&
(ic->ic_regdomain.country == CTRY_DEFAULT ||
!isocmp(se->se_cc, ic->ic_regdomain.isocc)))
fail |= MATCH_CC;
}
if (vap->iv_flags & IEEE80211_F_PRIVACY) {
if ((se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
fail |= MATCH_PRIVACY;
} else {
if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY)
fail |= MATCH_PRIVACY;
}
se0->se_flags &= ~STA_DEMOTE11B;
rate = check_rate(vap, se->se_chan, se);
if (rate & IEEE80211_RATE_BASIC) {
fail |= MATCH_RATE;
if (IEEE80211_IS_CHAN_ANYG(se->se_chan)) {
rate = check_rate(vap, demote11b(vap, se->se_chan), se);
if ((rate & IEEE80211_RATE_BASIC) == 0) {
fail &= ~MATCH_RATE;
se0->se_flags |= STA_DEMOTE11B;
}
}
} else if (rate < 2*24) {
if (!(vap->iv_des_mode == IEEE80211_MODE_AUTO ||
vap->iv_des_mode == IEEE80211_MODE_11B))
fail |= MATCH_RATE;
else
se0->se_flags |= STA_DEMOTE11B;
}
if (ss->ss_nssid != 0 &&
!match_ssid(se->se_ssid, ss->ss_nssid, ss->ss_ssid))
fail |= MATCH_SSID;
if ((vap->iv_flags & IEEE80211_F_DESBSSID) &&
!IEEE80211_ADDR_EQ(vap->iv_des_bssid, se->se_bssid))
fail |= MATCH_BSSID;
if (se0->se_fails >= STA_FAILS_MAX)
fail |= MATCH_FAILS;
if (se0->se_notseen >= STA_PURGE_SCANS)
fail |= MATCH_NOTSEEN;
if (se->se_rssi < STA_RSSI_MIN)
fail |= MATCH_RSSI;
#ifdef IEEE80211_DEBUG
if (ieee80211_msg(vap, debug)) {
net80211_printf(" %c %s",
fail & MATCH_FAILS ? '=' :
fail & MATCH_NOTSEEN ? '^' :
fail & MATCH_CC ? '$' :
#ifdef IEEE80211_SUPPORT_TDMA
fail & MATCH_TDMA_NOIE ? '&' :
fail & MATCH_TDMA_VERSION ? 'v' :
fail & MATCH_TDMA_NOTMASTER ? 's' :
fail & MATCH_TDMA_NOSLOT ? 'f' :
fail & MATCH_TDMA_LOCAL ? 'l' :
#endif
fail & MATCH_MESH_NOID ? 'm' :
fail ? '-' : '+', ether_sprintf(se->se_macaddr));
net80211_printf(" %s%c", ether_sprintf(se->se_bssid),
fail & MATCH_BSSID ? '!' : ' ');
net80211_printf(" %3d%c", ieee80211_chan2ieee(ic, se->se_chan),
fail & MATCH_CHANNEL ? '!' : ' ');
net80211_printf(" %+4d%c", se->se_rssi, fail & MATCH_RSSI ? '!' : ' ');
net80211_printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
fail & MATCH_RATE ? '!' : ' ');
net80211_printf(" %4s%c",
(se->se_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
(se->se_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : "",
fail & MATCH_CAPINFO ? '!' : ' ');
net80211_printf(" %3s%c ",
(se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) ?
"wep" : "no",
fail & MATCH_PRIVACY ? '!' : ' ');
ieee80211_print_essid(se->se_ssid+2, se->se_ssid[1]);
net80211_printf("%s\n", fail & (MATCH_SSID | MATCH_MESHID) ? "!" : "");
}
#endif
return fail;
}
static void
sta_update_notseen(struct sta_table *st)
{
struct sta_entry *se;
IEEE80211_SCAN_TABLE_LOCK(st);
TAILQ_FOREACH(se, &st->st_entry, se_list) {
if (se->se_seen)
se->se_seen = 0;
else
se->se_notseen++;
}
IEEE80211_SCAN_TABLE_UNLOCK(st);
}
static void
sta_dec_fails(struct sta_table *st)
{
struct sta_entry *se;
IEEE80211_SCAN_TABLE_LOCK(st);
TAILQ_FOREACH(se, &st->st_entry, se_list)
if (se->se_fails)
se->se_fails--;
IEEE80211_SCAN_TABLE_UNLOCK(st);
}
static struct sta_entry *
select_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, int debug)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *se, *selbs = NULL;
IEEE80211_DPRINTF(vap, debug, " %s\n",
"macaddr bssid chan rssi rate flag wep essid");
IEEE80211_SCAN_TABLE_LOCK(st);
TAILQ_FOREACH(se, &st->st_entry, se_list) {
ieee80211_ies_expand(&se->base.se_ies);
if (match_bss(vap, ss, se, debug) == 0) {
if (selbs == NULL)
selbs = se;
else if (sta_compare(se, selbs) > 0)
selbs = se;
}
}
IEEE80211_SCAN_TABLE_UNLOCK(st);
return selbs;
}
static int
sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *selbs;
struct ieee80211_channel *chan;
KASSERT(vap->iv_opmode == IEEE80211_M_STA,
("wrong mode %u", vap->iv_opmode));
if (st->st_newscan) {
sta_update_notseen(st);
st->st_newscan = 0;
}
if (ss->ss_flags & IEEE80211_SCAN_NOPICK) {
ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: nopick; return 1\n", __func__);
return 1;
}
if (TAILQ_FIRST(&st->st_entry) == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no scan candidate, join=%d, return 0\n",
__func__,
!! (ss->ss_flags & IEEE80211_SCAN_NOJOIN));
if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
return 0;
notfound:
sta_dec_fails(st);
st->st_newscan = 1;
return 0;
}
selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN);
if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
return (selbs != NULL);
if (selbs == NULL)
goto notfound;
chan = selbs->base.se_chan;
if (selbs->se_flags & STA_DEMOTE11B)
chan = demote11b(vap, chan);
if (!ieee80211_sta_join(vap, chan, &selbs->base))
goto notfound;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: terminate scan; return 1\n", __func__);
return 1;
}
static struct sta_entry *
sta_lookup(struct sta_table *st, const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
struct sta_entry *se;
int hash = STA_HASH(macaddr);
IEEE80211_SCAN_TABLE_LOCK(st);
LIST_FOREACH(se, &st->st_hash[hash], se_hash)
if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr))
break;
IEEE80211_SCAN_TABLE_UNLOCK(st);
return se;
}
static void
sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni = vap->iv_bss;
struct sta_table *st = ss->ss_priv;
enum ieee80211_phymode mode;
struct sta_entry *se, *selbs;
uint8_t roamRate, curRate, ucastRate;
int8_t roamRssi, curRssi;
se = sta_lookup(st, ni->ni_macaddr);
if (se == NULL) {
return;
}
mode = ieee80211_chan2mode(ic->ic_bsschan);
roamRate = vap->iv_roamparms[mode].rate;
roamRssi = vap->iv_roamparms[mode].rssi;
KASSERT(roamRate != 0 && roamRssi != 0, ("iv_roamparms are not"
"initialized for %s mode!", ieee80211_phymode_name[mode]));
ucastRate = vap->iv_txparms[mode].ucastrate;
curRssi = ic->ic_node_getrssi(ni);
if (ucastRate == IEEE80211_FIXED_RATE_NONE) {
curRate = ieee80211_node_get_txrate_dot11rate(ni);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM,
"%s: currssi %d currate %u roamrssi %d roamrate %u\n",
__func__, curRssi, curRate, roamRssi, roamRate);
} else {
curRate = roamRate;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM,
"%s: currssi %d roamrssi %d\n", __func__, curRssi, roamRssi);
}
if (curRate < roamRate || curRssi < roamRssi) {
if (ieee80211_time_after(ticks, ic->ic_lastscan + vap->iv_scanvalid)) {
if (!IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) ||
ieee80211_time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)))
ieee80211_bg_scan(vap, 0);
return;
}
se->base.se_rssi = curRssi;
selbs = select_bss(ss, vap, IEEE80211_MSG_ROAM);
if (selbs != NULL && selbs != se) {
struct ieee80211_channel *chan;
IEEE80211_DPRINTF(vap,
IEEE80211_MSG_ROAM | IEEE80211_MSG_DEBUG,
"%s: ROAM: curRate %u, roamRate %u, "
"curRssi %d, roamRssi %d\n", __func__,
curRate, roamRate, curRssi, roamRssi);
chan = selbs->base.se_chan;
if (selbs->se_flags & STA_DEMOTE11B)
chan = demote11b(vap, chan);
(void) ieee80211_sta_join(vap, chan, &selbs->base);
}
}
}
static void
sta_age(struct ieee80211_scan_state *ss)
{
struct ieee80211vap *vap = ss->ss_vap;
adhoc_age(ss);
KASSERT(vap->iv_opmode == IEEE80211_M_STA,
("wrong mode %u", vap->iv_opmode));
if (vap->iv_roaming == IEEE80211_ROAMING_AUTO &&
(vap->iv_flags & IEEE80211_F_BGSCAN) &&
vap->iv_state >= IEEE80211_S_RUN)
sta_roam_check(ss, vap);
}
static void
sta_iterate(struct ieee80211_scan_state *ss,
ieee80211_scan_iter_func *f, void *arg)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *se;
u_int gen;
IEEE80211_SCAN_ITER_LOCK(st);
gen = st->st_scaniter++;
restart:
IEEE80211_SCAN_TABLE_LOCK(st);
TAILQ_FOREACH(se, &st->st_entry, se_list) {
if (se->se_scangen != gen) {
se->se_scangen = gen;
se->base.se_age = ticks - se->se_lastupdate;
IEEE80211_SCAN_TABLE_UNLOCK(st);
(*f)(arg, &se->base);
goto restart;
}
}
IEEE80211_SCAN_TABLE_UNLOCK(st);
IEEE80211_SCAN_ITER_UNLOCK(st);
}
static void
sta_assoc_fail(struct ieee80211_scan_state *ss,
const uint8_t macaddr[IEEE80211_ADDR_LEN], int reason)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *se;
se = sta_lookup(st, macaddr);
if (se != NULL) {
se->se_fails++;
se->se_lastfail = ticks;
IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN,
macaddr, "%s: reason %u fails %u",
__func__, reason, se->se_fails);
}
}
static void
sta_assoc_success(struct ieee80211_scan_state *ss,
const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *se;
se = sta_lookup(st, macaddr);
if (se != NULL) {
#if 0
se->se_fails = 0;
IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN,
macaddr, "%s: fails %u",
__func__, se->se_fails);
#endif
se->se_lastassoc = ticks;
}
}
static const struct ieee80211_scanner sta_default = {
.scan_name = "default",
.scan_attach = sta_attach,
.scan_detach = sta_detach,
.scan_start = sta_start,
.scan_restart = sta_restart,
.scan_cancel = sta_cancel,
.scan_end = sta_pick_bss,
.scan_flush = sta_flush,
.scan_add = sta_add,
.scan_age = sta_age,
.scan_iterate = sta_iterate,
.scan_assoc_fail = sta_assoc_fail,
.scan_assoc_success = sta_assoc_success,
};
IEEE80211_SCANNER_ALG(sta, IEEE80211_M_STA, sta_default);
static const uint16_t adhocWorld[] =
{ 5180, 5200, 5220, 5240 };
static const uint16_t adhocFcc3[] =
{ 5180, 5200, 5220, 5240, 5725, 5745, 5765, 5785, 5805, 5825 };
static const uint16_t adhocMkk[] =
{ 5170, 5190, 5210, 5230 };
static const uint16_t adhoc11b[] =
{ 2457, 2462 };
static const struct scanlist adhocScanTable[] = {
{ IEEE80211_MODE_11B, X(adhoc11b) },
{ IEEE80211_MODE_11A, X(adhocWorld) },
{ IEEE80211_MODE_11A, X(adhocFcc3) },
{ IEEE80211_MODE_11B, X(adhocMkk) },
{ .list = NULL }
};
#undef X
static int
adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
makescanlist(ss, vap, adhocScanTable);
if (ss->ss_mindwell == 0)
ss->ss_mindwell = msecs_to_ticks(200);
if (ss->ss_maxdwell == 0)
ss->ss_maxdwell = msecs_to_ticks(200);
st->st_scangen++;
st->st_newscan = 1;
return 0;
}
static struct ieee80211_channel *
adhoc_pick_channel(struct ieee80211_scan_state *ss, int flags)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *se;
struct ieee80211_channel *c, *bestchan;
int i, bestrssi, maxrssi;
bestchan = NULL;
bestrssi = -1;
IEEE80211_SCAN_TABLE_LOCK(st);
for (i = 0; i < ss->ss_last; i++) {
c = ss->ss_chans[i];
if (IEEE80211_IS_CHAN_RADAR(c))
continue;
if (IEEE80211_IS_CHAN_NOADHOC(c))
continue;
if (flags != 0 && (c->ic_flags & flags) != flags)
continue;
maxrssi = 0;
TAILQ_FOREACH(se, &st->st_entry, se_list) {
if (se->base.se_chan != c)
continue;
if (se->base.se_rssi > maxrssi)
maxrssi = se->base.se_rssi;
}
if (bestchan == NULL || maxrssi < bestrssi)
bestchan = c;
}
IEEE80211_SCAN_TABLE_UNLOCK(st);
return bestchan;
}
static int
adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *selbs;
struct ieee80211_channel *chan;
struct ieee80211com *ic = vap->iv_ic;
KASSERT(vap->iv_opmode == IEEE80211_M_IBSS ||
vap->iv_opmode == IEEE80211_M_AHDEMO ||
vap->iv_opmode == IEEE80211_M_MBSS,
("wrong opmode %u", vap->iv_opmode));
if (st->st_newscan) {
sta_update_notseen(st);
st->st_newscan = 0;
}
if (ss->ss_flags & IEEE80211_SCAN_NOPICK) {
ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
return 1;
}
if (TAILQ_FIRST(&st->st_entry) == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no scan candidate\n", __func__);
if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
return 0;
notfound:
#ifdef IEEE80211_SUPPORT_TDMA
if (vap->iv_des_nssid &&
((vap->iv_caps & IEEE80211_C_TDMA) == 0 ||
ieee80211_tdma_getslot(vap) == 0)) {
#else
if (vap->iv_des_nssid) {
#endif
if (vap->iv_des_chan == IEEE80211_CHAN_ANYC ||
IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
chan = adhoc_pick_channel(ss, 0);
} else
chan = vap->iv_des_chan;
if (chan != NULL) {
chan = ieee80211_ht_adjust_channel(ic,
chan, vap->iv_flags_ht);
chan = ieee80211_vht_adjust_channel(ic,
chan, vap->iv_vht_flags);
ieee80211_create_ibss(vap, chan);
return 1;
}
}
sta_dec_fails(st);
st->st_newscan = 1;
return 0;
}
selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN);
if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
return (selbs != NULL);
if (selbs == NULL)
goto notfound;
chan = selbs->base.se_chan;
if (selbs->se_flags & STA_DEMOTE11B)
chan = demote11b(vap, chan);
chan = ieee80211_ht_adjust_channel(ic,
chan, vap->iv_flags_ht);
chan = ieee80211_vht_adjust_channel(ic,
chan, vap->iv_vht_flags);
if (!ieee80211_sta_join(vap, chan, &selbs->base))
goto notfound;
return 1;
}
static void
adhoc_age(struct ieee80211_scan_state *ss)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *se, *next;
IEEE80211_SCAN_TABLE_LOCK(st);
TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
if (se->se_notseen > STA_PURGE_SCANS) {
TAILQ_REMOVE(&st->st_entry, se, se_list);
LIST_REMOVE(se, se_hash);
ieee80211_ies_cleanup(&se->base.se_ies);
IEEE80211_FREE(se, M_80211_SCAN);
}
}
IEEE80211_SCAN_TABLE_UNLOCK(st);
}
static const struct ieee80211_scanner adhoc_default = {
.scan_name = "default",
.scan_attach = sta_attach,
.scan_detach = sta_detach,
.scan_start = adhoc_start,
.scan_restart = sta_restart,
.scan_cancel = sta_cancel,
.scan_end = adhoc_pick_bss,
.scan_flush = sta_flush,
.scan_pickchan = adhoc_pick_channel,
.scan_add = sta_add,
.scan_age = adhoc_age,
.scan_iterate = sta_iterate,
.scan_assoc_fail = sta_assoc_fail,
.scan_assoc_success = sta_assoc_success,
};
IEEE80211_SCANNER_ALG(ibss, IEEE80211_M_IBSS, adhoc_default);
IEEE80211_SCANNER_ALG(ahdemo, IEEE80211_M_AHDEMO, adhoc_default);
static int
ap_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
makescanlist(ss, vap, staScanTable);
if (ss->ss_mindwell == 0)
ss->ss_mindwell = msecs_to_ticks(200);
if (ss->ss_maxdwell == 0)
ss->ss_maxdwell = msecs_to_ticks(200);
st->st_scangen++;
st->st_newscan = 1;
return 0;
}
static int
ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
return 0;
}
static struct ieee80211_channel *
ap_pick_channel(struct ieee80211_scan_state *ss, int flags)
{
struct sta_table *st = ss->ss_priv;
struct ieee80211_channel *bestchan = NULL;
int i;
for (i = 0; i < ss->ss_last; i++) {
struct ieee80211_channel *chan = ss->ss_chans[i];
if (IEEE80211_IS_CHAN_RADAR(chan))
continue;
if (IEEE80211_IS_CHAN_NOHOSTAP(chan))
continue;
if (flags != 0 && (chan->ic_flags & flags) != flags)
continue;
KASSERT(sizeof(chan->ic_ieee) == 1, ("ic_chan size"));
if (st->st_maxrssi[chan->ic_ieee] == 0) {
return chan;
}
if (bestchan == NULL ||
st->st_maxrssi[chan->ic_ieee] < st->st_maxrssi[bestchan->ic_ieee])
bestchan = chan;
}
return bestchan;
}
static int
ap_end(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *bestchan, *chan;
KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP,
("wrong opmode %u", vap->iv_opmode));
bestchan = ap_pick_channel(ss, 0);
if (bestchan == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no suitable channel! (should not happen)\n", __func__);
return 0;
}
if (IEEE80211_IS_CHAN_TURBO(bestchan)) {
bestchan = ieee80211_find_channel(ic, bestchan->ic_freq,
bestchan->ic_flags & ~IEEE80211_CHAN_TURBO);
if (bestchan == NULL) {
return 0;
}
}
if (ss->ss_flags & (IEEE80211_SCAN_NOPICK | IEEE80211_SCAN_NOJOIN)) {
ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
return 1;
}
chan = ieee80211_ht_adjust_channel(ic, bestchan, vap->iv_flags_ht);
chan = ieee80211_vht_adjust_channel(ic, chan, vap->iv_vht_flags);
ieee80211_create_ibss(vap, chan);
return 1;
}
static const struct ieee80211_scanner ap_default = {
.scan_name = "default",
.scan_attach = sta_attach,
.scan_detach = sta_detach,
.scan_start = ap_start,
.scan_restart = sta_restart,
.scan_cancel = ap_cancel,
.scan_end = ap_end,
.scan_flush = sta_flush,
.scan_pickchan = ap_pick_channel,
.scan_add = sta_add,
.scan_age = adhoc_age,
.scan_iterate = sta_iterate,
.scan_assoc_success = sta_assoc_success,
.scan_assoc_fail = sta_assoc_fail,
};
IEEE80211_SCANNER_ALG(ap, IEEE80211_M_HOSTAP, ap_default);
#ifdef IEEE80211_SUPPORT_MESH
static int
mesh_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
struct ieee80211_mesh_state *ms = vap->iv_mesh;
struct sta_entry *selbs;
struct ieee80211_channel *chan;
KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
("wrong opmode %u", vap->iv_opmode));
if (st->st_newscan) {
sta_update_notseen(st);
st->st_newscan = 0;
}
if (ss->ss_flags & IEEE80211_SCAN_NOPICK) {
ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
return 1;
}
if (TAILQ_FIRST(&st->st_entry) == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no scan candidate\n", __func__);
if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
return 0;
notfound:
if (ms->ms_idlen != 0) {
if (vap->iv_des_chan == IEEE80211_CHAN_ANYC ||
IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
struct ieee80211com *ic = vap->iv_ic;
chan = adhoc_pick_channel(ss, 0);
if (chan != NULL) {
chan = ieee80211_ht_adjust_channel(ic,
chan, vap->iv_flags_ht);
chan = ieee80211_vht_adjust_channel(ic,
chan, vap->iv_vht_flags);
}
} else
chan = vap->iv_des_chan;
if (chan != NULL) {
ieee80211_create_ibss(vap, chan);
return 1;
}
}
sta_dec_fails(st);
st->st_newscan = 1;
return 0;
}
selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN);
if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
return (selbs != NULL);
if (selbs == NULL)
goto notfound;
chan = selbs->base.se_chan;
if (selbs->se_flags & STA_DEMOTE11B)
chan = demote11b(vap, chan);
if (!ieee80211_sta_join(vap, chan, &selbs->base))
goto notfound;
return 1;
}
static const struct ieee80211_scanner mesh_default = {
.scan_name = "default",
.scan_attach = sta_attach,
.scan_detach = sta_detach,
.scan_start = adhoc_start,
.scan_restart = sta_restart,
.scan_cancel = sta_cancel,
.scan_end = mesh_pick_bss,
.scan_flush = sta_flush,
.scan_pickchan = adhoc_pick_channel,
.scan_add = sta_add,
.scan_age = adhoc_age,
.scan_iterate = sta_iterate,
.scan_assoc_fail = sta_assoc_fail,
.scan_assoc_success = sta_assoc_success,
};
IEEE80211_SCANNER_ALG(mesh, IEEE80211_M_MBSS, mesh_default);
#endif