Changeset View
Changeset View
Standalone View
Standalone View
sys/net80211/ieee80211_scan_sta.c
Show First 20 Lines • Show All 783 Lines • ▼ Show 20 Lines | demote11b(struct ieee80211vap *vap, struct ieee80211_channel *chan) | ||||
return chan; | return chan; | ||||
} | } | ||||
static int | static int | ||||
maxrate(const struct ieee80211_scan_entry *se) | maxrate(const struct ieee80211_scan_entry *se) | ||||
{ | { | ||||
const struct ieee80211_ie_htcap *htcap = | const struct ieee80211_ie_htcap *htcap = | ||||
(const struct ieee80211_ie_htcap *) se->se_ies.htcap_ie; | (const struct ieee80211_ie_htcap *) se->se_ies.htcap_ie; | ||||
int rmax, r, i, txstream; | int rmax, r, i, txstream, sgi; | ||||
uint32_t chan_flags; | |||||
uint16_t caps; | uint16_t caps; | ||||
uint8_t txparams; | uint8_t txparams; | ||||
rmax = 0; | rmax = 0; | ||||
if (htcap != NULL) { | if (htcap != NULL) { | ||||
/* | /* | ||||
* HT station; inspect supported MCS and then adjust | * HT station; inspect supported MCS and then adjust | ||||
* rate by channel width. | * rate by channel width. | ||||
*/ | */ | ||||
txparams = htcap->hc_mcsset[12]; | txparams = htcap->hc_mcsset[12]; | ||||
if (txparams & 0x3) { | if (txparams & 0x3) { | ||||
/* | /* | ||||
* TX MCS parameters defined and not equal to RX, | * TX MCS parameters defined and not equal to RX, | ||||
* extract the number of spartial streams and | * extract the number of spartial streams and | ||||
* map it to the highest MCS rate. | * map it to the highest MCS rate. | ||||
*/ | */ | ||||
txstream = ((txparams & 0xc) >> 2) + 1; | txstream = ((txparams & 0xc) >> 2) + 1; | ||||
i = txstream * 8 - 1; | i = txstream * 8 - 1; | ||||
} else | } else | ||||
for (i = 31; i >= 0 && isclr(htcap->hc_mcsset, i); i--); | for (i = 31; i >= 0 && isclr(htcap->hc_mcsset, i); i--); | ||||
if (i >= 0) { | if (i >= 0) { | ||||
caps = le16dec(&htcap->hc_cap); | caps = le16dec(&htcap->hc_cap); | ||||
if ((caps & IEEE80211_HTCAP_CHWIDTH40) && | if (caps & IEEE80211_HTCAP_CHWIDTH40) { | ||||
(caps & IEEE80211_HTCAP_SHORTGI40)) | chan_flags = IEEE80211_CHAN_HT40; | ||||
rmax = ieee80211_htrates[i].ht40_rate_400ns; | sgi = (caps & IEEE80211_HTCAP_SHORTGI40) != 0; | ||||
else if (caps & IEEE80211_HTCAP_CHWIDTH40) | } else { | ||||
rmax = ieee80211_htrates[i].ht40_rate_800ns; | chan_flags = IEEE80211_CHAN_HT20; | ||||
else if (caps & IEEE80211_HTCAP_SHORTGI20) | sgi = (caps & IEEE80211_HTCAP_SHORTGI20) != 0; | ||||
rmax = ieee80211_htrates[i].ht20_rate_400ns; | |||||
else | |||||
rmax = ieee80211_htrates[i].ht20_rate_800ns; | |||||
} | } | ||||
rmax = ieee80211_get_rateKbps_by_idx( | |||||
IEEE80211_RATE_INDEX_HT(i), chan_flags, sgi); | |||||
rmax /= 500; | |||||
} | } | ||||
} | |||||
for (i = 0; i < se->se_rates[1]; i++) { | for (i = 0; i < se->se_rates[1]; i++) { | ||||
r = se->se_rates[2+i] & IEEE80211_RATE_VAL; | r = se->se_rates[2+i] & IEEE80211_RATE_VAL; | ||||
if (r > rmax) | if (r > rmax) | ||||
rmax = r; | rmax = r; | ||||
} | } | ||||
for (i = 0; i < se->se_xrates[1]; i++) { | for (i = 0; i < se->se_xrates[1]; i++) { | ||||
r = se->se_xrates[2+i] & IEEE80211_RATE_VAL; | r = se->se_xrates[2+i] & IEEE80211_RATE_VAL; | ||||
if (r > rmax) | if (r > rmax) | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Check rate set suitability and return the best supported rate. | * Check rate set suitability and return the best supported rate. | ||||
* XXX inspect MCS for HT | * XXX inspect MCS for HT | ||||
*/ | */ | ||||
static int | static int | ||||
check_rate(struct ieee80211vap *vap, const struct ieee80211_channel *chan, | check_rate(struct ieee80211vap *vap, const struct ieee80211_channel *chan, | ||||
const struct ieee80211_scan_entry *se) | const struct ieee80211_scan_entry *se) | ||||
{ | { | ||||
const enum ieee80211_phymode mode = ieee80211_chan2mode(chan); | |||||
const struct ieee80211_rateset *srs; | const struct ieee80211_rateset *srs; | ||||
int i, j, nrs, r, okrate, badrate, fixedrate, ucastrate; | uint16_t ucastrate, rate_index, lastrate, okrate; | ||||
uint8_t rv; | |||||
int i, nrs, fixed_rate_not_found; | |||||
const uint8_t *rs; | const uint8_t *rs; | ||||
okrate = badrate = 0; | |||||
srs = ieee80211_get_suprates(vap->iv_ic, chan); | srs = ieee80211_get_suprates(vap->iv_ic, chan); | ||||
nrs = se->se_rates[1]; | nrs = se->se_rates[1]; | ||||
rs = se->se_rates+2; | rs = se->se_rates+2; | ||||
/* XXX MCS */ | /* XXX MCS */ | ||||
ucastrate = vap->iv_txparms[ieee80211_chan2mode(chan)].ucastrate; | ucastrate = vap->iv_txparms[mode].ucastrate; | ||||
fixedrate = IEEE80211_FIXED_RATE_NONE; | fixed_rate_not_found = (ucastrate != IEEE80211_RATE_NONEXISTENT); | ||||
okrate = lastrate = IEEE80211_RATE_NONEXISTENT; | |||||
again: | again: | ||||
for (i = 0; i < nrs; i++) { | for (i = 0; i < nrs; i++) { | ||||
r = IEEE80211_RV(rs[i]); | rv = IEEE80211_RV(rs[i]); | ||||
badrate = r; | rate_index = ieee80211_convert_from_legacy_rate(rv, mode); | ||||
if (rate_index == IEEE80211_RATE_NONEXISTENT) { | |||||
if (rs[i] & IEEE80211_RATE_BASIC) { | |||||
okrate = IEEE80211_RATE_NONEXISTENT; | |||||
goto back; | |||||
} else | |||||
continue; | |||||
} | |||||
lastrate = rate_index; | |||||
/* | /* | ||||
* Check any fixed rate is included. | * Check any fixed rate is included. | ||||
*/ | */ | ||||
if (r == ucastrate) | if (rate_index == ucastrate) | ||||
fixedrate = r; | fixed_rate_not_found = 0; | ||||
/* | /* | ||||
* Check against our supported rates. | * Check against our supported rates. | ||||
*/ | */ | ||||
for (j = 0; j < srs->rs_nrates; j++) | if (isclr(srs->rs_bitmap, rv) && | ||||
if (r == IEEE80211_RV(srs->rs_rates[j])) { | (rs[i] & IEEE80211_RATE_BASIC)) { | ||||
if (r > okrate) /* NB: track max */ | |||||
okrate = r; | |||||
break; | |||||
} | |||||
if (j == srs->rs_nrates && (rs[i] & IEEE80211_RATE_BASIC)) { | |||||
/* | /* | ||||
* Don't try joining a BSS, if we don't support | * Don't try joining a BSS, if we don't support | ||||
* one of its basic rates. | * one of its basic rates. | ||||
*/ | */ | ||||
okrate = 0; | okrate = IEEE80211_RATE_NONEXISTENT; | ||||
goto back; | goto back; | ||||
} | } | ||||
if (okrate == IEEE80211_RATE_NONEXISTENT || | |||||
okrate < rate_index) /* NB: track max */ | |||||
okrate = rate_index; | |||||
} | } | ||||
if (rs == se->se_rates+2) { | if (rs == se->se_rates+2) { | ||||
/* scan xrates too; sort of an algol68-style for loop */ | /* scan xrates too; sort of an algol68-style for loop */ | ||||
nrs = se->se_xrates[1]; | nrs = se->se_xrates[1]; | ||||
rs = se->se_xrates+2; | rs = se->se_xrates+2; | ||||
goto again; | goto again; | ||||
} | } | ||||
back: | back: | ||||
if (okrate == 0 || ucastrate != fixedrate) | if (okrate == IEEE80211_RATE_NONEXISTENT || fixed_rate_not_found) | ||||
return badrate | IEEE80211_RATE_BASIC; | return (lastrate | IEEE80211_F_RATESET_ERROR); | ||||
else | else | ||||
return IEEE80211_RV(okrate); | return (okrate); | ||||
} | } | ||||
static __inline int | static __inline int | ||||
match_id(const uint8_t *ie, const uint8_t *val, int len) | match_id(const uint8_t *ie, const uint8_t *val, int len) | ||||
{ | { | ||||
return (ie[1] == len && memcmp(ie+2, val, len) == 0); | return (ie[1] == len && memcmp(ie+2, val, len) == 0); | ||||
} | } | ||||
Show All 27 Lines | |||||
/* | /* | ||||
* Test a scan candidate for suitability/compatibility. | * Test a scan candidate for suitability/compatibility. | ||||
*/ | */ | ||||
static int | static int | ||||
match_bss(struct ieee80211vap *vap, | match_bss(struct ieee80211vap *vap, | ||||
const struct ieee80211_scan_state *ss, struct sta_entry *se0, | const struct ieee80211_scan_state *ss, struct sta_entry *se0, | ||||
int debug) | int debug) | ||||
{ | { | ||||
const struct ieee80211_rate_t *rate = NULL; | |||||
struct ieee80211com *ic = vap->iv_ic; | struct ieee80211com *ic = vap->iv_ic; | ||||
struct ieee80211_scan_entry *se = &se0->base; | struct ieee80211_scan_entry *se = &se0->base; | ||||
uint8_t rate; | int fail, rate_index; | ||||
int fail; | |||||
fail = 0; | fail = 0; | ||||
if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan))) | if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan))) | ||||
fail |= MATCH_CHANNEL; | fail |= MATCH_CHANNEL; | ||||
/* | /* | ||||
* NB: normally the desired mode is used to construct | * NB: normally the desired mode is used to construct | ||||
* the channel list, but it's possible for the scan | * the channel list, but it's possible for the scan | ||||
* cache to include entries for stations outside this | * cache to include entries for stations outside this | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | if (vap->iv_flags & IEEE80211_F_PRIVACY) { | ||||
if ((se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) | if ((se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) | ||||
fail |= MATCH_PRIVACY; | fail |= MATCH_PRIVACY; | ||||
} else { | } else { | ||||
/* XXX does this mean privacy is supported or required? */ | /* XXX does this mean privacy is supported or required? */ | ||||
if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) | if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) | ||||
fail |= MATCH_PRIVACY; | fail |= MATCH_PRIVACY; | ||||
} | } | ||||
se0->se_flags &= ~STA_DEMOTE11B; | se0->se_flags &= ~STA_DEMOTE11B; | ||||
rate = check_rate(vap, se->se_chan, se); | rate_index = check_rate(vap, se->se_chan, se); | ||||
if (rate & IEEE80211_RATE_BASIC) { | if (rate_index & IEEE80211_F_RATESET_ERROR) { | ||||
fail |= MATCH_RATE; | fail |= MATCH_RATE; | ||||
/* | /* | ||||
* An 11b-only ap will give a rate mismatch if there is an | * An 11b-only ap will give a rate mismatch if there is an | ||||
* OFDM fixed tx rate for 11g. Try downgrading the channel | * OFDM fixed tx rate for 11g. Try downgrading the channel | ||||
* in the scan list to 11b and retry the rate check. | * in the scan list to 11b and retry the rate check. | ||||
*/ | */ | ||||
if (IEEE80211_IS_CHAN_ANYG(se->se_chan)) { | if (IEEE80211_IS_CHAN_ANYG(se->se_chan)) { | ||||
rate = check_rate(vap, demote11b(vap, se->se_chan), se); | rate_index = check_rate(vap, | ||||
if ((rate & IEEE80211_RATE_BASIC) == 0) { | demote11b(vap, se->se_chan), se); | ||||
if ((rate_index & IEEE80211_F_RATESET_ERROR) == 0) { | |||||
fail &= ~MATCH_RATE; | fail &= ~MATCH_RATE; | ||||
se0->se_flags |= STA_DEMOTE11B; | se0->se_flags |= STA_DEMOTE11B; | ||||
} | } | ||||
} | } | ||||
} else if (rate < 2*24) { | } else { | ||||
/* NB: we know that the rate exist. */ | |||||
rate = ieee80211_get_rate(rate_index); | |||||
if (rate->type == IEEE80211_T_CCK) { | |||||
/* | /* | ||||
* This is an 11b-only ap. Check the desired mode in | * This is an 11b-only ap. Check the desired mode in | ||||
* case that needs to be honored (mode 11g filters out | * case that needs to be honored (mode 11g filters out | ||||
* 11b-only ap's). Otherwise force any 11g channel used | * 11b-only ap's). Otherwise force any 11g channel | ||||
* in scanning to be demoted. | * used in scanning to be demoted. | ||||
* | * | ||||
* NB: we cheat a bit here by looking at the max rate; | * NB: we cheat a bit here by looking at the max rate; | ||||
* we could/should check the rates. | * we could/should check the rates. | ||||
*/ | */ | ||||
if (!(vap->iv_des_mode == IEEE80211_MODE_AUTO || | if (!(vap->iv_des_mode == IEEE80211_MODE_AUTO || | ||||
vap->iv_des_mode == IEEE80211_MODE_11B)) | vap->iv_des_mode == IEEE80211_MODE_11B)) | ||||
fail |= MATCH_RATE; | fail |= MATCH_RATE; | ||||
else | else | ||||
se0->se_flags |= STA_DEMOTE11B; | se0->se_flags |= STA_DEMOTE11B; | ||||
} | } | ||||
} | |||||
if (ss->ss_nssid != 0 && | if (ss->ss_nssid != 0 && | ||||
!match_ssid(se->se_ssid, ss->ss_nssid, ss->ss_ssid)) | !match_ssid(se->se_ssid, ss->ss_nssid, ss->ss_ssid)) | ||||
fail |= MATCH_SSID; | fail |= MATCH_SSID; | ||||
if ((vap->iv_flags & IEEE80211_F_DESBSSID) && | if ((vap->iv_flags & IEEE80211_F_DESBSSID) && | ||||
!IEEE80211_ADDR_EQ(vap->iv_des_bssid, se->se_bssid)) | !IEEE80211_ADDR_EQ(vap->iv_des_bssid, se->se_bssid)) | ||||
fail |= MATCH_BSSID; | fail |= MATCH_BSSID; | ||||
if (se0->se_fails >= STA_FAILS_MAX) | if (se0->se_fails >= STA_FAILS_MAX) | ||||
fail |= MATCH_FAILS; | fail |= MATCH_FAILS; | ||||
Show All 16 Lines | |||||
#endif | #endif | ||||
fail & MATCH_MESH_NOID ? 'm' : | fail & MATCH_MESH_NOID ? 'm' : | ||||
fail ? '-' : '+', ether_sprintf(se->se_macaddr)); | fail ? '-' : '+', ether_sprintf(se->se_macaddr)); | ||||
printf(" %s%c", ether_sprintf(se->se_bssid), | printf(" %s%c", ether_sprintf(se->se_bssid), | ||||
fail & MATCH_BSSID ? '!' : ' '); | fail & MATCH_BSSID ? '!' : ' '); | ||||
printf(" %3d%c", ieee80211_chan2ieee(ic, se->se_chan), | printf(" %3d%c", ieee80211_chan2ieee(ic, se->se_chan), | ||||
fail & MATCH_CHANNEL ? '!' : ' '); | fail & MATCH_CHANNEL ? '!' : ' '); | ||||
printf(" %+4d%c", se->se_rssi, fail & MATCH_RSSI ? '!' : ' '); | printf(" %+4d%c", se->se_rssi, fail & MATCH_RSSI ? '!' : ' '); | ||||
printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, | printf(" %2dM%c", rate ? rate->value / 2 : rate_index, | ||||
fail & MATCH_RATE ? '!' : ' '); | fail & MATCH_RATE ? '!' : ' '); | ||||
printf(" %4s%c", | printf(" %4s%c", | ||||
(se->se_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : | (se->se_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : | ||||
(se->se_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : "", | (se->se_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : "", | ||||
fail & MATCH_CAPINFO ? '!' : ' '); | fail & MATCH_CAPINFO ? '!' : ' '); | ||||
printf(" %3s%c ", | printf(" %3s%c ", | ||||
(se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) ? | (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) ? | ||||
"wep" : "no", | "wep" : "no", | ||||
▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | sta_lookup(struct sta_table *st, const uint8_t macaddr[IEEE80211_ADDR_LEN]) | ||||
LIST_FOREACH(se, &st->st_hash[hash], se_hash) | LIST_FOREACH(se, &st->st_hash[hash], se_hash) | ||||
if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr)) | if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr)) | ||||
break; | break; | ||||
IEEE80211_SCAN_TABLE_UNLOCK(st); | IEEE80211_SCAN_TABLE_UNLOCK(st); | ||||
return se; /* NB: unlocked */ | return se; /* NB: unlocked */ | ||||
} | } | ||||
static __inline int | |||||
compare_rates(uint16_t rate_index1, uint16_t rate_index2) | |||||
{ | |||||
uint8_t value1, value2; | |||||
if (rate_index1 == rate_index2) | |||||
return (0); | |||||
value1 = ieee80211_get_rateKbps_by_idx(rate_index1, 0, 0); | |||||
value2 = ieee80211_get_rateKbps_by_idx(rate_index2, 0, 0); | |||||
return (value1 < value2); | |||||
} | |||||
static void | static void | ||||
sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) | sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) | ||||
{ | { | ||||
struct ieee80211com *ic = vap->iv_ic; | struct ieee80211com *ic = vap->iv_ic; | ||||
struct ieee80211_node *ni = vap->iv_bss; | struct ieee80211_node *ni = vap->iv_bss; | ||||
struct sta_table *st = ss->ss_priv; | struct sta_table *st = ss->ss_priv; | ||||
enum ieee80211_phymode mode; | enum ieee80211_phymode mode; | ||||
struct sta_entry *se, *selbs; | struct sta_entry *se, *selbs; | ||||
uint8_t roamRate, curRate, ucastRate; | uint16_t ucastRate, curRate, roamRate; | ||||
int8_t roamRssi, curRssi; | int8_t roamRssi, curRssi; | ||||
se = sta_lookup(st, ni->ni_macaddr); | se = sta_lookup(st, ni->ni_macaddr); | ||||
if (se == NULL) { | if (se == NULL) { | ||||
/* XXX something is wrong */ | /* XXX something is wrong */ | ||||
return; | return; | ||||
} | } | ||||
mode = ieee80211_chan2mode(ic->ic_bsschan); | mode = ieee80211_chan2mode(ic->ic_bsschan); | ||||
roamRate = vap->iv_roamparms[mode].rate; | roamRate = vap->iv_roamparms[mode].rate; | ||||
roamRssi = vap->iv_roamparms[mode].rssi; | roamRssi = vap->iv_roamparms[mode].rssi; | ||||
ucastRate = vap->iv_txparms[mode].ucastrate; | ucastRate = vap->iv_txparms[mode].ucastrate; | ||||
/* NB: the most up to date rssi is in the node, not the scan cache */ | /* NB: the most up to date rssi is in the node, not the scan cache */ | ||||
curRssi = ic->ic_node_getrssi(ni); | curRssi = ic->ic_node_getrssi(ni); | ||||
if (ucastRate == IEEE80211_FIXED_RATE_NONE) { | if (ucastRate == IEEE80211_RATE_NONEXISTENT) { | ||||
curRate = ni->ni_txrate; | curRate = ni->ni_txrate; | ||||
roamRate &= IEEE80211_RATE_VAL; | |||||
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM, | IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM, | ||||
"%s: currssi %d currate %u roamrssi %d roamrate %u\n", | "%s: currssi %d currate %u roamrssi %d roamrate %u\n", | ||||
__func__, curRssi, curRate, roamRssi, roamRate); | __func__, curRssi, curRate, roamRssi, roamRate); | ||||
} else { | } else { | ||||
curRate = roamRate; /* NB: insure compare below fails */ | curRate = roamRate; /* NB: insure compare below fails */ | ||||
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM, | IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM, | ||||
"%s: currssi %d roamrssi %d\n", __func__, curRssi, roamRssi); | "%s: currssi %d roamrssi %d\n", __func__, curRssi, roamRssi); | ||||
} | } | ||||
/* | /* | ||||
* Check if a new ap should be used and switch. | * Check if a new ap should be used and switch. | ||||
* XXX deauth current ap | * XXX deauth current ap | ||||
*/ | */ | ||||
if (curRate < roamRate || curRssi < roamRssi) { | if (compare_rates(curRate, roamRate) || curRssi < roamRssi) { | ||||
if (ieee80211_time_after(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { | if (ieee80211_time_after(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { | ||||
/* | /* | ||||
* Scan cache contents are too old; force a scan now | * Scan cache contents are too old; force a scan now | ||||
* if possible so we have current state to make a | * if possible so we have current state to make a | ||||
* decision with. We don't kick off a bg scan if | * decision with. We don't kick off a bg scan if | ||||
* we're using dynamic turbo and boosted or if the | * we're using dynamic turbo and boosted or if the | ||||
* channel is busy. | * channel is busy. | ||||
* XXX force immediate switch on scan complete | * XXX force immediate switch on scan complete | ||||
▲ Show 20 Lines • Show All 610 Lines • Show Last 20 Lines |