📄 ieee80211_scan_sta.c
字号:
if ((vap->iv_flags & IEEE80211_F_XR) && (mode == IEEE80211_MODE_TURBO_A || mode == IEEE80211_MODE_TURBO_G)) continue; /* * Add the list of the channels; any that are not * in the master channel list will be discarded. */ add_channels(ic, ss, mode, scan->list, scan->count); } /* * Add the channels from the ic (from HAL) that are not present * in the staScanTable. */ for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; /* * scan dynamic turbo channels in normal mode. */ if (IEEE80211_IS_CHAN_DTURBO(c)) continue; mode = ieee80211_chan2mode(c); if (vap->iv_des_mode != IEEE80211_MODE_AUTO) { /* * If a desired mode was specified, scan only * channels that satisfy that constraint. */ if (vap->iv_des_mode != mode) continue; } if (!checktable(staScanTable, c)) ss->ss_chans[ss->ss_last++] = c; } ss->ss_next = 0; /* XXX tunables */ ss->ss_mindwell = msecs_to_jiffies(20); /* 20ms */ ss->ss_maxdwell = msecs_to_jiffies(200); /* 200ms */#ifdef IEEE80211_DEBUG if (ieee80211_msg_scan(vap)) { printf("%s: scan set ", vap->iv_dev->name); ieee80211_scan_dump_channels(ss); printf(" dwell min %ld max %ld\n", ss->ss_mindwell, ss->ss_maxdwell); }#endif /* IEEE80211_DEBUG */ st->st_newscan = 1; return 0;#undef N}/* * Restart a bg scan. */static intsta_restart(struct ieee80211_scan_state *ss, struct ieee80211vap *vap){ struct sta_table *st = ss->ss_priv; st->st_newscan = 1; return 0;}/* * Cancel an ongoing scan. */static intsta_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap){ struct sta_table *st = ss->ss_priv; IEEE80211_CANCEL_TQUEUE(&st->st_actiontq); return 0;}static u_int8_tmaxrate(const struct ieee80211_scan_entry *se){ u_int8_t max, r; int i; max = 0; for (i = 0; i < se->se_rates[1]; i++) { r = se->se_rates[2+i] & IEEE80211_RATE_VAL; if (r > max) max = r; } for (i = 0; i < se->se_xrates[1]; i++) { r = se->se_xrates[2+i] & IEEE80211_RATE_VAL; if (r > max) max = r; } return max;}/* * Compare the capabilities of two entries and decide which is * more desirable (return >0 if a is considered better). Note * that we assume compatibility/usability has already been checked * so we don't need to (e.g. validate whether privacy is supported). * Used to select the best scan candidate for association in a BSS. */static intsta_compare(const struct sta_entry *a, const struct sta_entry *b){ u_int8_t maxa, maxb; int weight; /* privacy support preferred */ if ((a->base.se_capinfo & IEEE80211_CAPINFO_PRIVACY) && (b->base.se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) return 1; if ((a->base.se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0 && (b->base.se_capinfo & IEEE80211_CAPINFO_PRIVACY)) return -1; /* compare count of previous failures */ weight = b->se_fails - a->se_fails; if (abs(weight) > 1) return weight; if (abs(b->base.se_rssi - a->base.se_rssi) < 5) { /* best/max rate preferred if signal level close enough XXX */ maxa = maxrate(&a->base); maxb = maxrate(&b->base); if (maxa != maxb) return maxa - maxb; /* XXX use freq for channel preference */ /* for now just prefer 5Ghz band to all other bands */ if (IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) && !IEEE80211_IS_CHAN_5GHZ(b->base.se_chan)) return 1; if (!IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) && IEEE80211_IS_CHAN_5GHZ(b->base.se_chan)) return -1; } /* all things being equal, use signal level */ return a->base.se_rssi - b->base.se_rssi;}/* * Check rate set suitability and return the best supported rate. */static intcheck_rate(struct ieee80211vap *vap, const struct ieee80211_scan_entry *se){#define RV(v) ((v) & IEEE80211_RATE_VAL) struct ieee80211com *ic = vap->iv_ic; const struct ieee80211_rateset *srs; int i, j, nrs, r, okrate, badrate, fixedrate; const u_int8_t *rs; okrate = badrate = fixedrate = 0; if (IEEE80211_IS_CHAN_HALF(se->se_chan)) srs = &ic->ic_sup_half_rates; else if (IEEE80211_IS_CHAN_QUARTER(se->se_chan)) srs = &ic->ic_sup_quarter_rates; else srs = &ic->ic_sup_rates[ieee80211_chan2mode(se->se_chan)]; nrs = se->se_rates[1]; rs = se->se_rates + 2; fixedrate = IEEE80211_FIXED_RATE_NONE;again: for (i = 0; i < nrs; i++) { r = RV(rs[i]); badrate = r; /* * Check any fixed rate is included. */ if (r == vap->iv_fixed_rate) fixedrate = r; /* * Check against our supported rates. */ for (j = 0; j < srs->rs_nrates; j++) if (r == RV(srs->rs_rates[j])) { if (r > okrate) /* NB: track max */ okrate = r; break; } } if (rs == se->se_rates+2) { /* scan xrates too; sort of an algol68-style for loop */ nrs = se->se_xrates[1]; rs = se->se_xrates + 2; goto again; } if (okrate == 0 || vap->iv_fixed_rate != fixedrate) return badrate | IEEE80211_RATE_BASIC; else return RV(okrate);#undef RV}static intmatch_ssid(const u_int8_t *ie, int nssid, const struct ieee80211_scan_ssid ssids[]){ int i; for (i = 0; i < nssid; i++) { if (ie[1] == ssids[i].len && memcmp(ie + 2, ssids[i].ssid, ie[1]) == 0) return 1; } return 0;}/* * Test a scan candidate for suitability/compatibility. */static intmatch_bss(struct ieee80211vap *vap, const struct ieee80211_scan_state *ss, const struct sta_entry *se0){ struct ieee80211com *ic = vap->iv_ic; const struct ieee80211_scan_entry *se = &se0->base; u_int8_t rate; int fail; fail = 0; if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan))) fail |= 0x01; /* * NB: normally the desired mode is used to construct * the channel list, but it's possible for the scan * cache to include entries for stations outside this * list so we check the desired mode here to weed them * out. */ if (vap->iv_des_mode != IEEE80211_MODE_AUTO && (se->se_chan->ic_flags & IEEE80211_CHAN_ALLTURBO) != chanflags[vap->iv_des_mode]) fail |= 0x01; if (vap->iv_opmode == IEEE80211_M_IBSS) { if ((se->se_capinfo & IEEE80211_CAPINFO_IBSS) == 0) fail |= 0x02; } else { if ((se->se_capinfo & IEEE80211_CAPINFO_ESS) == 0) fail |= 0x02; } if (vap->iv_flags & IEEE80211_F_PRIVACY) { if ((se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) fail |= 0x04; } else { /* XXX does this mean privacy is supported or required? */ if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) fail |= 0x04; } rate = check_rate(vap, se); if (rate & IEEE80211_RATE_BASIC) fail |= 0x08; if (ss->ss_nssid != 0 && !match_ssid(se->se_ssid, ss->ss_nssid, ss->ss_ssid)) fail |= 0x10; if ((vap->iv_flags & IEEE80211_F_DESBSSID) && !IEEE80211_ADDR_EQ(vap->iv_des_bssid, se->se_bssid)) fail |= 0x20; if (se0->se_fails >= STA_FAILS_MAX) fail |= 0x40; if (se0->se_notseen >= STA_PURGE_SCANS) fail |= 0x80; if (se->se_rssi < STA_RSSI_MIN) fail |= 0x100;#ifdef IEEE80211_DEBUG if (ieee80211_msg(vap, IEEE80211_MSG_SCAN | IEEE80211_MSG_ROAM)) { printf(" %03x", fail); printf(" %c %s", fail & 0x40 ? '=' : fail & 0x80 ? '^' : fail ? '-' : '+', ether_sprintf(se->se_macaddr)); printf(" %s%c", ether_sprintf(se->se_bssid), fail & 0x20 ? '!' : ' '); printf(" %3d%c", ieee80211_chan2ieee(ic, se->se_chan), fail & 0x01 ? '!' : ' '); printf(" %+4d%c", se->se_rssi, fail & 0x100 ? '!' : ' '); printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, fail & 0x08 ? '!' : ' '); printf(" %4s%c", (se->se_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : (se->se_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : "????", fail & 0x02 ? '!' : ' '); printf(" %3s%c ", (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) ? "wep" : "no", fail & 0x04 ? '!' : ' '); ieee80211_print_essid(se->se_ssid + 2, se->se_ssid[1]); printf("%s\n", fail & 0x10 ? "!" : ""); }#endif return fail;}static voidsta_update_notseen(struct sta_table *st){ struct sta_entry *se; unsigned long stlockflags; spin_lock_irqsave(&st->st_lock, stlockflags); TAILQ_FOREACH(se, &st->st_entry, se_list) { /* * If seen then reset and don't bump the count; * otherwise bump the ``not seen'' count. Note * that this ensures that stations for which we * see frames while not scanning but not during * this scan will not be penalized. */ if (se->se_seen) se->se_seen = 0; else se->se_notseen++; } spin_unlock_irqrestore(&st->st_lock, stlockflags);}static voidsta_dec_fails(struct sta_table *st){ struct sta_entry *se; unsigned long stlockflags; spin_lock_irqsave(&st->st_lock, stlockflags); TAILQ_FOREACH(se, &st->st_entry, se_list) if (se->se_fails) se->se_fails--; spin_unlock_irqrestore(&st->st_lock, stlockflags);}static struct sta_entry *select_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap){ struct sta_table *st = ss->ss_priv; struct sta_entry *se, *selbs = NULL; unsigned long stlockflags; IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN | IEEE80211_MSG_ROAM, " %s\n", "macaddr bssid chan rssi rate flag wep essid"); spin_lock_irqsave(&st->st_lock, stlockflags); TAILQ_FOREACH(se, &st->st_entry, se_list) { if (match_bss(vap, ss, se) == 0) { if (selbs == NULL) selbs = se; else if (sta_compare(se, selbs) > 0) selbs = se; } } spin_unlock_irqrestore(&st->st_lock, stlockflags); return selbs;}/* * Pick an ap or ibss network to join or find a channel * to use to start an ibss network. */static intsta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, int (*action)(struct ieee80211vap *, const struct ieee80211_scan_entry *), u_int32_t flags){ struct sta_table *st = ss->ss_priv; struct sta_entry *selbss; IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s Checking scan results\n", __func__); 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) { /* * Manual/background scan, don't select+join the * bss, just return. The scanning framework will * handle notification that this has completed. */ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; return 1; } /* * Automatic sequencing; look for a candidate and * if found join the network. */ /* NB: unlocked read should be ok */ if (TAILQ_FIRST(&st->st_entry) == NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: no scan candidate\n", __func__);notfound: /* * If nothing suitable was found decrement * the failure counts so entries will be * reconsidered the next time around. We * really want to do this only for sta's * where we've previously had some success. */ sta_dec_fails(st); st->st_newscan = 1; return 0; /* restart scan */ } st->st_action = ss->ss_ops->scan_default; if (action) st->st_action = action; if ((selbss = select_bss(ss, vap)) == NULL ) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: select_bss failed\n", __func__); goto notfound; } st->st_selbss = selbss->base; /* * Must defer action to avoid possible recursive call through 80211 * state machine, which would result in recursive locking. */ IEEE80211_SCHEDULE_TQUEUE(&st->st_actiontq); return 1; /* terminate scan */}/* * Lookup an entry in the scan cache. We assume we're * called from the bottom half or such that we don't need * to block the bottom half so that it's safe to return * a reference to an entry w/o holding the lock on the table. */static struct sta_entry *sta_lookup(struct sta_table *st, const u_int8_t macaddr[IEEE80211_ADDR_LEN]){ struct sta_entry *se; int hash = STA_HASH(macaddr); spin_lock(&st->st_lock); LIST_FOREACH(se, &st->st_hash[hash], se_hash) if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr)) break; spin_unlock(&st->st_lock); return se; /* NB: unlocked */}static voidsta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap){ struct ieee80211_node *ni = vap->iv_bss; struct ieee80211com *ic = vap->iv_ic; struct sta_table *st = ss->ss_priv; struct sta_entry *se, *selbs; u_int8_t roamRate, curRate; int8_t roamRssi, curRssi; se = sta_lookup(st, ni->ni_macaddr); if (se == NULL) { /* XXX something is wrong */ return; } /* XXX do we need 11g too? */ if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) { roamRate = vap->iv_roam.rate11b; roamRssi = vap->iv_roam.rssi11b; } else if (IEEE80211_IS_CHAN_B(ic->ic_bsschan)) { roamRate = vap->iv_roam.rate11bOnly; roamRssi = vap->iv_roam.rssi11bOnly; } else { roamRate = vap->iv_roam.rate11a; roamRssi = vap->iv_roam.rssi11a; } /* NB: the most up to date rssi is in the node, not the scan cache */ curRssi = ic->ic_node_getrssi(ni); if (vap->iv_fixed_rate == IEEE80211_FIXED_RATE_NONE) { curRate = ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL; IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM, "%s: currssi %d currate %u roamrssi %d roamrate %u\n", __func__, curRssi, curRate, roamRssi, roamRate); } else { curRate = roamRate; /* NB: ensure compare below fails */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM, "%s: currssi %d roamrssi %d\n", __func__, curRssi, roamRssi); } if ((vap->iv_flags & IEEE80211_F_BGSCAN) && time_after(jiffies, ic->ic_lastscan + vap->iv_scanvalid)) { /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -