On Wed, Dec 06, 2017 at 06:28:28PM +0100, Stefan Sperling wrote:
> I found one bug which rendered the driver unable to make progress after
> an association failure. This new version fixes that problem for me.
New diff against -current.
The fixes I committed to the driver today make the scan abort code
from the previous diff unnecessary.
Index: dev/pci/if_iwm.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v
retrieving revision 1.219
diff -u -p -r1.219 if_iwm.c
--- dev/pci/if_iwm.c 7 Dec 2017 14:13:05 -0000 1.219
+++ dev/pci/if_iwm.c 7 Dec 2017 14:39:07 -0000
@@ -420,9 +420,9 @@ uint32_t iwm_scan_rate_n_flags(struct iw
uint8_t iwm_lmac_scan_fill_channels(struct iwm_softc *,
struct iwm_scan_channel_cfg_lmac *, int);
int iwm_fill_probe_req(struct iwm_softc *, struct iwm_scan_probe_req *);
-int iwm_lmac_scan(struct iwm_softc *);
+int iwm_lmac_scan(struct iwm_softc *, int);
int iwm_config_umac_scan(struct iwm_softc *);
-int iwm_umac_scan(struct iwm_softc *);
+int iwm_umac_scan(struct iwm_softc *, int);
uint8_t iwm_ridx2rate(struct ieee80211_rateset *, int);
int iwm_rval2ridx(int);
void iwm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, int *);
@@ -435,6 +435,7 @@ int iwm_update_quotas(struct iwm_softc *
void iwm_add_task(struct iwm_softc *, struct taskq *, struct task *);
void iwm_del_task(struct iwm_softc *, struct taskq *, struct task *);
int iwm_scan(struct iwm_softc *);
+int iwm_bgscan(struct ieee80211com *);
int iwm_auth(struct iwm_softc *);
int iwm_deauth(struct iwm_softc *);
int iwm_assoc(struct iwm_softc *);
@@ -4868,7 +4869,7 @@ iwm_fill_probe_req(struct iwm_softc *sc,
}
int
-iwm_lmac_scan(struct iwm_softc *sc)
+iwm_lmac_scan(struct iwm_softc *sc, int bgscan)
{
struct ieee80211com *ic = &sc->sc_ic;
struct iwm_host_cmd hcmd = {
@@ -4879,28 +4880,34 @@ iwm_lmac_scan(struct iwm_softc *sc)
};
struct iwm_scan_req_lmac *req;
size_t req_len;
- int err;
+ int err, async = bgscan;
req_len = sizeof(struct iwm_scan_req_lmac) +
(sizeof(struct iwm_scan_channel_cfg_lmac) *
sc->sc_capa_n_scan_channels) + sizeof(struct iwm_scan_probe_req);
if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE)
return ENOMEM;
- req = malloc(req_len, M_DEVBUF, M_WAIT | M_CANFAIL | M_ZERO);
+ req = malloc(req_len, M_DEVBUF,
+ (async ? M_NOWAIT : M_WAIT) | M_CANFAIL | M_ZERO);
if (req == NULL)
return ENOMEM;
hcmd.len[0] = (uint16_t)req_len;
hcmd.data[0] = (void *)req;
+ hcmd.flags |= async ? IWM_CMD_ASYNC : 0;
/* These timings correspond to iwlwifi's UNASSOC scan. */
req->active_dwell = 10;
req->passive_dwell = 110;
req->fragmented_dwell = 44;
req->extended_dwell = 90;
- req->max_out_time = 0;
- req->suspend_time = 0;
-
+ if (bgscan) {
+ req->max_out_time = htole32(120);
+ req->suspend_time = htole32(120);
+ } else {
+ req->max_out_time = htole32(0);
+ req->suspend_time = htole32(0);
+ }
req->scan_prio = htole32(IWM_SCAN_PRIORITY_HIGH);
req->rx_chain_select = iwm_scan_rx_chain(sc);
req->iter_num = htole32(1);
@@ -5048,7 +5055,7 @@ iwm_config_umac_scan(struct iwm_softc *s
}
int
-iwm_umac_scan(struct iwm_softc *sc)
+iwm_umac_scan(struct iwm_softc *sc, int bgscan)
{
struct ieee80211com *ic = &sc->sc_ic;
struct iwm_host_cmd hcmd = {
@@ -5060,7 +5067,7 @@ iwm_umac_scan(struct iwm_softc *sc)
struct iwm_scan_req_umac *req;
struct iwm_scan_req_umac_tail *tail;
size_t req_len;
- int err;
+ int err, async = bgscan;
req_len = sizeof(struct iwm_scan_req_umac) +
(sizeof(struct iwm_scan_channel_cfg_umac) *
@@ -5068,20 +5075,27 @@ iwm_umac_scan(struct iwm_softc *sc)
sizeof(struct iwm_scan_req_umac_tail);
if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE)
return ENOMEM;
- req = malloc(req_len, M_DEVBUF, M_WAIT | M_CANFAIL | M_ZERO);
+ req = malloc(req_len, M_DEVBUF,
+ (async ? M_NOWAIT : M_WAIT) | M_CANFAIL | M_ZERO);
if (req == NULL)
return ENOMEM;
hcmd.len[0] = (uint16_t)req_len;
hcmd.data[0] = (void *)req;
+ hcmd.flags |= async ? IWM_CMD_ASYNC : 0;
/* These timings correspond to iwlwifi's UNASSOC scan. */
req->active_dwell = 10;
req->passive_dwell = 110;
req->fragmented_dwell = 44;
req->extended_dwell = 90;
- req->max_out_time = 0;
- req->suspend_time = 0;
+ if (bgscan) {
+ req->max_out_time = htole32(120);
+ req->suspend_time = htole32(120);
+ } else {
+ req->max_out_time = htole32(0);
+ req->suspend_time = htole32(0);
+ }
req->scan_priority = htole32(IWM_SCAN_PRIORITY_HIGH);
req->ooc_priority = htole32(IWM_SCAN_PRIORITY_HIGH);
@@ -5465,9 +5479,9 @@ iwm_scan(struct iwm_softc *sc)
return 0;
if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN))
- err = iwm_umac_scan(sc);
+ err = iwm_umac_scan(sc, 0);
else
- err = iwm_lmac_scan(sc);
+ err = iwm_lmac_scan(sc, 0);
if (err) {
printf("%s: could not initiate scan\n", DEVNAME(sc));
return err;
@@ -5482,6 +5496,28 @@ iwm_scan(struct iwm_softc *sc)
}
int
+iwm_bgscan(struct ieee80211com *ic)
+{
+ struct iwm_softc *sc = IC2IFP(ic)->if_softc;
+ int err;
+
+ if (sc->sc_flags & IWM_FLAG_SCANNING)
+ return 0;
+
+ if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN))
+ err = iwm_umac_scan(sc, 1);
+ else
+ err = iwm_lmac_scan(sc, 1);
+ if (err) {
+ printf("%s: could not initiate scan\n", DEVNAME(sc));
+ return err;
+ }
+
+ sc->sc_flags |= IWM_FLAG_SCANNING;
+ return 0;
+}
+
+int
iwm_auth(struct iwm_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
@@ -6539,9 +6575,10 @@ iwm_start(struct ifnet *ifp)
ni = m->m_pkthdr.ph_cookie;
goto sendit;
}
- if (ic->ic_state != IEEE80211_S_RUN) {
+
+ if (ic->ic_state != IEEE80211_S_RUN ||
+ (ic->ic_xflags & IEEE80211_F_TX_MGMT_ONLY))
break;
- }
IFQ_DEQUEUE(&ifp->if_snd, m);
if (!m)
@@ -7787,6 +7824,7 @@ iwm_attach(struct device *parent, struct
task_set(&sc->htprot_task, iwm_htprot_task, sc);
ic->ic_node_alloc = iwm_node_alloc;
+ ic->ic_bgscan_start = iwm_bgscan;
/* Override 802.11 state transition machine. */
sc->sc_newstate = ic->ic_newstate;
Index: net80211/ieee80211.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211.c,v
retrieving revision 1.63
diff -u -p -r1.63 ieee80211.c
--- net80211/ieee80211.c 5 Sep 2017 12:02:21 -0000 1.63
+++ net80211/ieee80211.c 6 Dec 2017 21:55:18 -0000
@@ -72,6 +72,32 @@ void ieee80211_setbasicrates(struct ieee
int ieee80211_findrate(struct ieee80211com *, enum ieee80211_phymode, int);
void
+ieee80211_begin_bgscan(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = (void *)ifp;
+
+ if ((ic->ic_flags & IEEE80211_F_BGSCAN) ||
+ ic->ic_state != IEEE80211_S_RUN)
+ return;
+
+ if (ic->ic_bgscan_start != NULL && ic->ic_bgscan_start(ic) == 0) {
+ ic->ic_flags |= IEEE80211_F_BGSCAN;
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: begin background scan\n", ifp->if_xname);
+
+ /* Driver calls ieee80211_end_scan() when done. */
+ }
+}
+
+void
+ieee80211_bgscan_timeout(void *arg)
+{
+ struct ifnet *ifp = arg;
+
+ ieee80211_begin_bgscan(ifp);
+}
+
+void
ieee80211_channel_init(struct ifnet *ifp)
{
struct ieee80211com *ic = (void *)ifp;
@@ -158,6 +184,8 @@ ieee80211_ifattach(struct ifnet *ifp)
ifp->if_priority = IF_WIRELESS_DEFAULT_PRIORITY;
ieee80211_set_link_state(ic, LINK_STATE_DOWN);
+
+ timeout_set(&ic->ic_bgscan_timeout, ieee80211_bgscan_timeout, ifp);
}
void
Index: net80211/ieee80211_input.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v
retrieving revision 1.196
diff -u -p -r1.196 ieee80211_input.c
--- net80211/ieee80211_input.c 4 Sep 2017 09:11:46 -0000 1.196
+++ net80211/ieee80211_input.c 6 Dec 2017 21:55:18 -0000
@@ -267,6 +267,14 @@ ieee80211_input(struct ifnet *ifp, struc
ni->ni_rssi = rxi->rxi_rssi;
ni->ni_rstamp = rxi->rxi_tstamp;
ni->ni_inact = 0;
+
+ /* Cancel or start background scan based on RSSI. */
+ if ((*ic->ic_node_checkrssi)(ic, ni))
+ timeout_del(&ic->ic_bgscan_timeout);
+ else if (!timeout_pending(&ic->ic_bgscan_timeout) &&
+ (ic->ic_flags & IEEE80211_F_BGSCAN) == 0)
+ timeout_add_msec(&ic->ic_bgscan_timeout,
+ 500 * (ic->ic_bgscan_fail + 1));
}
#ifndef IEEE80211_STA_ONLY
@@ -1462,6 +1470,7 @@ ieee80211_recv_probe_resp(struct ieee802
}
if ((ic->ic_state != IEEE80211_S_SCAN ||
!(ic->ic_caps & IEEE80211_C_SCANALL)) &&
+ (ic->ic_flags & IEEE80211_F_BGSCAN) == 0 &&
chan != bchan) {
/*
* Frame was received on a channel different from the
@@ -1504,7 +1513,8 @@ ieee80211_recv_probe_resp(struct ieee802
#ifdef IEEE80211_DEBUG
if (ieee80211_debug > 1 &&
- (ni == NULL || ic->ic_state == IEEE80211_S_SCAN)) {
+ (ni == NULL || ic->ic_state == IEEE80211_S_SCAN ||
+ (ic->ic_flags & IEEE80211_F_BGSCAN))) {
printf("%s: %s%s on chan %u (bss chan %u) ",
__func__, (ni == NULL ? "new " : ""),
isprobe ? "probe response" : "beacon",
@@ -1589,7 +1599,8 @@ ieee80211_recv_probe_resp(struct ieee802
* This probe response indicates the AP is still serving us
* so don't allow ieee80211_watchdog() to move us into SCAN.
*/
- ic->ic_mgt_timer = 0;
+ if ((ic->ic_flags & IEEE80211_F_BGSCAN) == 0)
+ ic->ic_mgt_timer = 0;
}
/*
* We do not try to update EDCA parameters if QoS was not negotiated
@@ -1606,7 +1617,8 @@ ieee80211_recv_probe_resp(struct ieee802
ni->ni_flags &= ~IEEE80211_NODE_QOS;
}
- if (ic->ic_state == IEEE80211_S_SCAN) {
+ if (ic->ic_state == IEEE80211_S_SCAN ||
+ (ic->ic_flags & IEEE80211_F_BGSCAN)) {
struct ieee80211_rsnparams rsn, wpa;
ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
@@ -2361,9 +2373,13 @@ ieee80211_recv_deauth(struct ieee80211co
ic->ic_stats.is_rx_deauth++;
switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ieee80211_new_state(ic, IEEE80211_S_AUTH,
- IEEE80211_FC0_SUBTYPE_DEAUTH);
+ case IEEE80211_M_STA: {
+ int bgscan = ((ic->ic_flags & IEEE80211_F_BGSCAN) &&
+ ic->ic_state == IEEE80211_S_RUN);
+ if (!bgscan) /* ignore deauth during bgscan */
+ ieee80211_new_state(ic, IEEE80211_S_AUTH,
+ IEEE80211_FC0_SUBTYPE_DEAUTH);
+ }
break;
#ifndef IEEE80211_STA_ONLY
case IEEE80211_M_HOSTAP:
@@ -2407,9 +2423,13 @@ ieee80211_recv_disassoc(struct ieee80211
ic->ic_stats.is_rx_disassoc++;
switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ieee80211_new_state(ic, IEEE80211_S_ASSOC,
- IEEE80211_FC0_SUBTYPE_DISASSOC);
+ case IEEE80211_M_STA: {
+ int bgscan = ((ic->ic_flags & IEEE80211_F_BGSCAN) &&
+ ic->ic_state == IEEE80211_S_RUN);
+ if (!bgscan) /* ignore disassoc during bgscan */
+ ieee80211_new_state(ic, IEEE80211_S_ASSOC,
+ IEEE80211_FC0_SUBTYPE_DISASSOC);
+ }
break;
#ifndef IEEE80211_STA_ONLY
case IEEE80211_M_HOSTAP:
Index: net80211/ieee80211_node.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v
retrieving revision 1.121
diff -u -p -r1.121 ieee80211_node.c
--- net80211/ieee80211_node.c 5 Sep 2017 14:56:59 -0000 1.121
+++ net80211/ieee80211_node.c 6 Dec 2017 21:55:18 -0000
@@ -65,12 +65,16 @@ void ieee80211_node_copy(struct ieee8021
void ieee80211_choose_rsnparams(struct ieee80211com *);
u_int8_t ieee80211_node_getrssi(struct ieee80211com *,
const struct ieee80211_node *);
+int ieee80211_node_checkrssi(struct ieee80211com *,
+ const struct ieee80211_node *);
void ieee80211_setup_node(struct ieee80211com *, struct ieee80211_node *,
const u_int8_t *);
void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_ba_del(struct ieee80211_node *);
struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *);
void ieee80211_node_cleanup(struct ieee80211com *, struct ieee80211_node *);
+void ieee80211_node_switch_bss(struct ieee80211com *, struct ieee80211_node *);
+void ieee80211_node_join_bss(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_needs_auth(struct ieee80211com *, struct ieee80211_node *);
#ifndef IEEE80211_STA_ONLY
void ieee80211_node_join_ht(struct ieee80211com *, struct ieee80211_node *);
@@ -128,6 +132,7 @@ ieee80211_node_attach(struct ifnet *ifp)
ic->ic_node_free = ieee80211_node_free;
ic->ic_node_copy = ieee80211_node_copy;
ic->ic_node_getrssi = ieee80211_node_getrssi;
+ ic->ic_node_checkrssi = ieee80211_node_checkrssi;
ic->ic_scangen = 1;
ic->ic_max_nnodes = ieee80211_cache_size;
@@ -546,6 +551,113 @@ ieee80211_match_bss(struct ieee80211com
return fail;
}
+struct ieee80211_node_switch_bss_arg {
+ u_int8_t cur_macaddr[IEEE80211_ADDR_LEN];
+ u_int8_t sel_macaddr[IEEE80211_ADDR_LEN];
+};
+
+/* Implements ni->ni_unref_cb(). */
+void
+ieee80211_node_switch_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_node_switch_bss_arg *sba = ni->ni_unref_arg;
+ struct ieee80211_node *curbs, *selbs;
+
+ splassert(IPL_NET);
+
+ if ((ic->ic_flags & IEEE80211_F_BGSCAN) == 0) {
+ free(sba, M_DEVBUF, sizeof(*sba));
+ return;
+ }
+
+ ic->ic_xflags &= ~IEEE80211_F_TX_MGMT_ONLY;
+
+ selbs = ieee80211_find_node(ic, sba->sel_macaddr);
+ if (selbs == NULL) {
+ free(sba, M_DEVBUF, sizeof(*sba));
+ ic->ic_flags &= ~IEEE80211_F_BGSCAN;
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+ return;
+ }
+
+ curbs = ieee80211_find_node(ic, sba->cur_macaddr);
+ if (curbs == NULL) {
+ free(sba, M_DEVBUF, sizeof(*sba));
+ ic->ic_flags &= ~IEEE80211_F_BGSCAN;
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+ return;
+ }
+
+ if (ifp->if_flags & IFF_DEBUG) {
+ printf("%s: roaming from %s chan %d ",
+ ifp->if_xname, ether_sprintf(curbs->ni_macaddr),
+ ieee80211_chan2ieee(ic, curbs->ni_chan));
+ printf("to %s chan %d\n", ether_sprintf(selbs->ni_macaddr),
+ ieee80211_chan2ieee(ic, selbs->ni_chan));
+ }
+ ieee80211_node_newstate(curbs, IEEE80211_STA_CACHE);
+ ieee80211_node_join_bss(ic, selbs); /* frees arg and ic->ic_bss */
+}
+
+void
+ieee80211_node_join_bss(struct ieee80211com *ic, struct ieee80211_node *selbs)
+{
+ enum ieee80211_phymode mode;
+ struct ieee80211_node *ni;
+
+ /* Reinitialize media mode and channels if needed. */
+ mode = ieee80211_chan2mode(ic, selbs->ni_chan);
+ if (mode != ic->ic_curmode)
+ ieee80211_setmode(ic, mode);
+
+ (*ic->ic_node_copy)(ic, ic->ic_bss, selbs);
+ ni = ic->ic_bss;
+
+ ic->ic_curmode = ieee80211_chan2mode(ic, ni->ni_chan);
+
+ /* Make sure we send valid rates in an association request. */
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ ieee80211_fix_rate(ic, ni,
+ IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+
+ if (ic->ic_flags & IEEE80211_F_RSNON)
+ ieee80211_choose_rsnparams(ic);
+ else if (ic->ic_flags & IEEE80211_F_WEPON)
+ ni->ni_rsncipher = IEEE80211_CIPHER_USEGROUP;
+
+ ieee80211_node_newstate(selbs, IEEE80211_STA_BSS);
+#ifndef IEEE80211_STA_ONLY
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ ieee80211_fix_rate(ic, ni, IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (ni->ni_rates.rs_nrates == 0) {
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+ return;
+ }
+ ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ } else
+#endif
+ {
+ int bgscan = ((ic->ic_flags & IEEE80211_F_BGSCAN) &&
+ ic->ic_opmode == IEEE80211_M_STA &&
+ ic->ic_state == IEEE80211_S_RUN);
+
+ timeout_del(&ic->ic_bgscan_timeout);
+ ic->ic_flags &= ~IEEE80211_F_BGSCAN;
+
+ /*
+ * After a background scan, we have now switched APs.
+ * Pretend we were just de-authed, which makes
+ * ieee80211_new_state() try to re-auth and thus send
+ * an AUTH frame to our newly selected AP.
+ */
+ ieee80211_new_state(ic, IEEE80211_S_AUTH,
+ bgscan ? IEEE80211_FC0_SUBTYPE_DEAUTH : -1);
+ }
+}
+
/*
* Complete a scan of potential channels.
*/
@@ -553,12 +665,16 @@ void
ieee80211_end_scan(struct ifnet *ifp)
{
struct ieee80211com *ic = (void *)ifp;
- struct ieee80211_node *ni, *nextbs, *selbs;
+ struct ieee80211_node *ni, *nextbs, *selbs, *curbs;
+ int bgscan = ((ic->ic_flags & IEEE80211_F_BGSCAN) &&
+ ic->ic_opmode == IEEE80211_M_STA &&
+ ic->ic_state == IEEE80211_S_RUN);
if (ifp->if_flags & IFF_DEBUG)
printf("%s: end %s scan\n", ifp->if_xname,
- (ic->ic_flags & IEEE80211_F_ASCAN) ?
- "active" : "passive");
+ bgscan ? "background" :
+ ((ic->ic_flags & IEEE80211_F_ASCAN) ?
+ "active" : "passive"));
if (ic->ic_scan_count)
ic->ic_flags &= ~IEEE80211_F_ASCAN;
@@ -634,6 +750,7 @@ ieee80211_end_scan(struct ifnet *ifp)
return;
}
selbs = NULL;
+ curbs = NULL;
for (; ni != NULL; ni = nextbs) {
nextbs = RBT_NEXT(ieee80211_tree, ni);
@@ -647,6 +764,10 @@ ieee80211_end_scan(struct ifnet *ifp)
ieee80211_free_node(ic, ni);
continue;
}
+
+ if (ni->ni_state == IEEE80211_STA_BSS)
+ curbs = ni;
+
if (ieee80211_match_bss(ic, ni) != 0)
continue;
@@ -657,21 +778,17 @@ ieee80211_end_scan(struct ifnet *ifp)
IEEE80211_IS_CHAN_5GHZ(selbs->ni_chan) &&
IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) &&
ni->ni_rssi > selbs->ni_rssi) {
- uint8_t min_rssi = 0, max_rssi = ic->ic_max_rssi;
+ uint8_t min_rssi;
/*
* Prefer 5GHz (with reasonable RSSI) over 2GHz since
* the 5GHz band is usually less saturated.
*/
- if (max_rssi) {
- /* Driver reports RSSI relative to maximum. */
- if (ni->ni_rssi > max_rssi / 3)
- min_rssi = ni->ni_rssi - (max_rssi / 3);
- } else {
- /* Driver reports RSSI value in dBm. */
- if (ni->ni_rssi > 10) /* XXX magic number */
- min_rssi = ni->ni_rssi - 10;
- }
+ if (ic->ic_max_rssi)
+ min_rssi = IEEE80211_RSSI_THRES_RATIO_5GHZ;
+ else
+ min_rssi = (uint8_t)IEEE80211_RSSI_THRES_5GHZ;
+
if (selbs->ni_rssi >= min_rssi)
continue;
}
@@ -679,35 +796,65 @@ ieee80211_end_scan(struct ifnet *ifp)
if (ni->ni_rssi > selbs->ni_rssi)
selbs = ni;
}
- if (selbs == NULL)
- goto notfound;
- (*ic->ic_node_copy)(ic, ic->ic_bss, selbs);
- ni = ic->ic_bss;
- ic->ic_curmode = ieee80211_chan2mode(ic, ni->ni_chan);
+ if (bgscan) {
+ struct ieee80211_node_switch_bss_arg *arg;
- /* Make sure we send valid rates in an association request. */
- if (ic->ic_opmode == IEEE80211_M_STA)
- ieee80211_fix_rate(ic, ni,
- IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
- IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ /* AP disappeared? Should not happen. */
+ if (selbs == NULL || curbs == NULL) {
+ ic->ic_flags &= ~IEEE80211_F_BGSCAN;
+ goto notfound;
+ }
- if (ic->ic_flags & IEEE80211_F_RSNON)
- ieee80211_choose_rsnparams(ic);
- else if (ic->ic_flags & IEEE80211_F_WEPON)
- ni->ni_rsncipher = IEEE80211_CIPHER_USEGROUP;
+ /*
+ * After a background scan we might end up choosing the
+ * same AP again. Do not change ic->ic_bss in this case,
+ * and make background scans less frequent.
+ */
+ if (selbs == curbs) {
+ if (ic->ic_bgscan_fail < IEEE80211_BGSCAN_FAIL_MAX)
+ ic->ic_bgscan_fail++;
+ ic->ic_flags &= ~IEEE80211_F_BGSCAN;
+ goto wakeup;
+ }
+
+ arg = malloc(sizeof(*arg), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (arg == NULL) {
+ ic->ic_flags &= ~IEEE80211_F_BGSCAN;
+ goto wakeup;
+ }
- ieee80211_node_newstate(selbs, IEEE80211_STA_BSS);
-#ifndef IEEE80211_STA_ONLY
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- ieee80211_fix_rate(ic, ni, IEEE80211_F_DOFRATE |
- IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (ni->ni_rates.rs_nrates == 0)
- goto notfound;
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
- } else
-#endif
- ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
+ ic->ic_bgscan_fail = 0;
+
+ /*
+ * We are going to switch APs.
+ * Queue a de-auth frame addressed to our current AP.
+ */
+ if (IEEE80211_SEND_MGMT(ic, ic->ic_bss,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE) != 0) {
+ ic->ic_flags &= ~IEEE80211_F_BGSCAN;
+ goto wakeup;
+ }
+
+ /* Prevent dispatch of additional data frames to hardware. */
+ ic->ic_xflags |= IEEE80211_F_TX_MGMT_ONLY;
+
+ /*
+ * Install a callback which will switch us to the new AP once
+ * all dispatched frames have been processed by hardware.
+ */
+ IEEE80211_ADDR_COPY(arg->cur_macaddr, curbs->ni_macaddr);
+ IEEE80211_ADDR_COPY(arg->sel_macaddr, selbs->ni_macaddr);
+ ic->ic_bss->ni_unref_arg = arg;
+ ic->ic_bss->ni_unref_arg_size = sizeof(*arg);
+ ic->ic_bss->ni_unref_cb = ieee80211_node_switch_bss;
+ /* F_BGSCAN flag gets cleared in ieee80211_node_join_bss(). */
+ goto wakeup;
+ } else if (selbs == NULL)
+ goto notfound;
+
+ ieee80211_node_join_bss(ic, selbs);
wakeup:
if (ic->ic_scan_lock & IEEE80211_SCAN_REQUEST) {
@@ -808,6 +955,9 @@ ieee80211_node_cleanup(struct ieee80211c
ni->ni_rsnie = NULL;
}
ieee80211_ba_del(ni);
+ free(ni->ni_unref_arg, M_DEVBUF, ni->ni_unref_arg_size);
+ ni->ni_unref_arg = NULL;
+ ni->ni_unref_arg_size = 0;
}
void
@@ -835,6 +985,25 @@ ieee80211_node_getrssi(struct ieee80211c
return ni->ni_rssi;
}
+int
+ieee80211_node_checkrssi(struct ieee80211com *ic,
+ const struct ieee80211_node *ni)
+{
+ uint8_t thres;
+
+ if (ic->ic_max_rssi) {
+ thres = (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) ?
+ IEEE80211_RSSI_THRES_RATIO_2GHZ :
+ IEEE80211_RSSI_THRES_RATIO_5GHZ;
+ return ((ni->ni_rssi * 100) / ic->ic_max_rssi >= thres);
+ }
+
+ thres = (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) ?
+ IEEE80211_RSSI_THRES_2GHZ :
+ IEEE80211_RSSI_THRES_5GHZ;
+ return (ni->ni_rssi >= (u_int8_t)thres);
+}
+
void
ieee80211_setup_node(struct ieee80211com *ic,
struct ieee80211_node *ni, const u_int8_t *macaddr)
@@ -1177,9 +1346,16 @@ ieee80211_release_node(struct ieee80211c
DPRINTF(("%s refcnt %u\n", ether_sprintf(ni->ni_macaddr),
ni->ni_refcnt));
s = splnet();
- if (ieee80211_node_decref(ni) == 0 &&
- ni->ni_state == IEEE80211_STA_COLLECT) {
- ieee80211_free_node(ic, ni);
+ if (ieee80211_node_decref(ni) == 0) {
+ if (ni->ni_unref_cb) {
+ (*ni->ni_unref_cb)(ic, ni);
+ ni->ni_unref_cb = NULL;
+ /* Freed by callback if necessary: */
+ ni->ni_unref_arg = NULL;
+ ni->ni_unref_arg_size = 0;
+ }
+ if (ni->ni_state == IEEE80211_STA_COLLECT)
+ ieee80211_free_node(ic, ni);
}
splx(s);
}
Index: net80211/ieee80211_node.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.h,v
retrieving revision 1.69
diff -u -p -r1.69 ieee80211_node.h
--- net80211/ieee80211_node.h 17 Aug 2017 06:01:05 -0000 1.69
+++ net80211/ieee80211_node.h 6 Dec 2017 21:55:18 -0000
@@ -297,6 +297,12 @@ struct ieee80211_node {
#define IEEE80211_NODE_SA_QUERY 0x0800 /* SA Query in progress
*/
#define IEEE80211_NODE_SA_QUERY_FAILED 0x1000 /* last SA Query failed */
#define IEEE80211_NODE_RSN_NEW_PTK 0x2000 /* expecting a new PTK */
+
+ /* If not NULL, this function gets called when ni_refcnt hits zero. */
+ void (*ni_unref_cb)(struct ieee80211com *,
+ struct ieee80211_node *);
+ void * ni_unref_arg;
+ size_t ni_unref_arg_size;
};
RBT_HEAD(ieee80211_tree, ieee80211_node);
Index: net80211/ieee80211_proto.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_proto.c,v
retrieving revision 1.80
diff -u -p -r1.80 ieee80211_proto.c
--- net80211/ieee80211_proto.c 18 Aug 2017 17:30:12 -0000 1.80
+++ net80211/ieee80211_proto.c 6 Dec 2017 21:55:18 -0000
@@ -853,6 +853,7 @@ ieee80211_newstate(struct ieee80211com *
ic->ic_state = nstate; /* state transition */
ni = ic->ic_bss; /* NB: no reference held */
ieee80211_set_link_state(ic, LINK_STATE_DOWN);
+ ic->ic_xflags &= ~IEEE80211_F_TX_MGMT_ONLY;
switch (nstate) {
case IEEE80211_S_INIT:
/*
@@ -919,6 +920,8 @@ justcleanup:
if (ic->ic_opmode == IEEE80211_M_HOSTAP)
timeout_del(&ic->ic_rsn_timeout);
#endif
+ timeout_del(&ic->ic_bgscan_timeout);
+ ic->ic_bgscan_fail = 0;
ic->ic_mgt_timer = 0;
mq_purge(&ic->ic_mgtq);
mq_purge(&ic->ic_pwrsaveq);
@@ -968,6 +971,8 @@ justcleanup:
" rescanning\n", ifp->if_xname,
ether_sprintf(ic->ic_bss->ni_bssid));
}
+ timeout_del(&ic->ic_bgscan_timeout);
+ ic->ic_bgscan_fail = 0;
ieee80211_free_allnodes(ic);
/* FALLTHROUGH */
case IEEE80211_S_AUTH:
@@ -1008,6 +1013,8 @@ justcleanup:
}
break;
case IEEE80211_S_RUN:
+ timeout_del(&ic->ic_bgscan_timeout);
+ ic->ic_bgscan_fail = 0;
switch (mgt) {
case IEEE80211_FC0_SUBTYPE_AUTH:
IEEE80211_SEND_MGMT(ic, ni,
Index: net80211/ieee80211_var.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_var.h,v
retrieving revision 1.81
diff -u -p -r1.81 ieee80211_var.h
--- net80211/ieee80211_var.h 6 Nov 2017 11:34:29 -0000 1.81
+++ net80211/ieee80211_var.h 6 Dec 2017 22:24:51 -0000
@@ -57,6 +57,13 @@
#define IEEE80211_TXPOWER_MAX 100 /* max power */
#define IEEE80211_TXPOWER_MIN -50 /* kill radio (if possible) */
+#define IEEE80211_RSSI_THRES_2GHZ (-60) /* in dBm */
+#define IEEE80211_RSSI_THRES_5GHZ (-70) /* in dBm */
+#define IEEE80211_RSSI_THRES_RATIO_2GHZ 60 /* in percent */
+#define IEEE80211_RSSI_THRES_RATIO_5GHZ 50 /* in percent */
+
+#define IEEE80211_BGSCAN_FAIL_MAX 360 /* units of 500 msec */
+
enum ieee80211_phytype {
IEEE80211_T_DS, /* direct sequence spread spectrum */
IEEE80211_T_OFDM, /* frequency division multiplexing */
@@ -220,6 +227,9 @@ struct ieee80211com {
struct ieee80211_node *, u_int8_t);
void (*ic_update_htprot)(struct ieee80211com *,
struct ieee80211_node *);
+ int (*ic_bgscan_start)(struct ieee80211com *);
+ struct timeout ic_bgscan_timeout;
+ uint32_t ic_bgscan_fail;
u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
@@ -231,6 +241,7 @@ struct ieee80211com {
u_int ic_scan_lock; /* user-initiated scan */
u_int8_t ic_scan_count; /* count scans */
u_int32_t ic_flags; /* state flags */
+ u_int32_t ic_xflags; /* more flags */
u_int32_t ic_caps; /* capabilities */
u_int16_t ic_modecaps; /* set of mode capabilities */
u_int16_t ic_curmode; /* current mode */
@@ -256,6 +267,8 @@ struct ieee80211com {
const struct ieee80211_node *);
u_int8_t (*ic_node_getrssi)(struct ieee80211com *,
const struct ieee80211_node *);
+ int (*ic_node_checkrssi)(struct ieee80211com *,
+ const struct ieee80211_node *);
u_int8_t ic_max_rssi;
struct ieee80211_tree ic_tree;
int ic_nnodes; /* length of ic_nnodes */
@@ -352,7 +365,11 @@ extern struct ieee80211com_head ieee8021
#define IEEE80211_F_MFPR 0x01000000 /* CONF: MFP required */
#define IEEE80211_F_HTON 0x02000000 /* CONF: HT enabled */
#define IEEE80211_F_PBAR 0x04000000 /* CONF: PBAC required
*/
+#define IEEE80211_F_BGSCAN 0x08000000 /* STATUS: background
scan */
#define IEEE80211_F_USERMASK 0xf0000000 /* CONF: ioctl flag mask */
+
+/* ic_xflags */
+#define IEEE80211_F_TX_MGMT_ONLY 0x00000001 /* leave data frames on
ifq */
/* ic_caps */
#define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP
available */