On Wed, Dec 06, 2017 at 09:25:48AM -0700, Base Pr1me wrote:
> Sorry for the anecdotal information. I've enabled debugging and if I see
> any relevant data, I'll pass it along.
I found one bug which rendered the driver unable to make progress after
an association failure. This new version fixes that problem for me.
Note that if you're watching the extra dmesg output created by
'ifconfig iwm0 debug' on the console, flooding the list of scan results
to the framebuffer results in delays which can cause some frames to be
received too late, and this makes association failures more likely.
There is nothing we can do about that. Either turn off the debug output,
or run in X and watch with 'tail -f /var/log/messages'. However, I could
make sure the failure code path is actually working by flooding the
console to make association fail after a scan. The driver now keeps
scanning when it fails to roam, as expected. It would hang with the
previous version of this diff.
Index: dev/pci/if_iwm.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v
retrieving revision 1.217
diff -u -p -r1.217 if_iwm.c
--- dev/pci/if_iwm.c 26 Oct 2017 15:00:28 -0000 1.217
+++ dev/pci/if_iwm.c 6 Dec 2017 17:11:13 -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,10 @@ 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_umac_scan_abort(struct iwm_softc *);
+int iwm_lmac_scan_abort(struct iwm_softc *);
+int iwm_scan_abort(struct iwm_softc *);
int iwm_auth(struct iwm_softc *);
int iwm_deauth(struct iwm_softc *);
int iwm_assoc(struct iwm_softc *);
@@ -4868,7 +4872,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 +4883,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 +5058,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 +5070,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 +5078,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);
@@ -5461,13 +5478,18 @@ iwm_scan(struct iwm_softc *sc)
struct ieee80211com *ic = &sc->sc_ic;
int err;
- if (sc->sc_flags & IWM_FLAG_SCANNING)
- return 0;
+ if (sc->sc_flags & IWM_FLAG_SCANNING) {
+ err = iwm_scan_abort(sc);
+ if (err) {
+ printf("%s: could not abort scan\n", DEVNAME(sc));
+ return err;
+ }
+ }
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 +5504,79 @@ 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_umac_scan_abort(struct iwm_softc *sc)
+{
+ struct iwm_umac_scan_abort cmd = { 0 };
+
+ return iwm_send_cmd_pdu(sc,
+ IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_ABORT_UMAC),
+ 0, sizeof(cmd), &cmd);
+}
+
+int
+iwm_lmac_scan_abort(struct iwm_softc *sc)
+{
+ struct iwm_host_cmd cmd = {
+ .id = IWM_SCAN_OFFLOAD_ABORT_CMD,
+ };
+ int err, status;
+
+ err = iwm_send_cmd_status(sc, &cmd, &status);
+ if (err)
+ return err;
+
+ if (status != IWM_CAN_ABORT_STATUS) {
+ /*
+ * The scan abort will return 1 for success or
+ * 2 for "failure". A failure condition can be
+ * due to simply not being in an active scan which
+ * can occur if we send the scan abort before the
+ * microcode has notified us that a scan is completed.
+ */
+ return EBUSY;
+ }
+
+ return 0;
+}
+
+int
+iwm_scan_abort(struct iwm_softc *sc)
+{
+ int err;
+
+ if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN))
+ err = iwm_umac_scan_abort(sc);
+ else
+ err = iwm_lmac_scan_abort(sc);
+
+ if (err == 0)
+ sc->sc_flags &= ~IWM_FLAG_SCANNING;
+ return err;
+}
+
+int
iwm_auth(struct iwm_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
@@ -6056,8 +6151,12 @@ iwm_newstate_task(void *psc)
}
out:
- if (err == 0 && (sc->sc_flags & IWM_FLAG_SHUTDOWN) == 0)
- sc->sc_newstate(ic, nstate, arg);
+ if ((sc->sc_flags & IWM_FLAG_SHUTDOWN) == 0) {
+ if (err)
+ task_add(systq, &sc->init_task);
+ else
+ sc->sc_newstate(ic, nstate, arg);
+ }
refcnt_rele_wake(&sc->task_refs);
splx(s);
}
@@ -6524,9 +6623,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)
@@ -7106,7 +7206,9 @@ iwm_notif_intr(struct iwm_softc *sc)
case IWM_BINDING_CONTEXT_CMD:
case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_CFG_CMD):
case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_REQ_UMAC):
+ case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_ABORT_UMAC):
case IWM_SCAN_OFFLOAD_REQUEST_CMD:
+ case IWM_SCAN_OFFLOAD_ABORT_CMD:
case IWM_REPLY_BEACON_FILTERING_CMD:
case IWM_MAC_PM_POWER_TABLE:
case IWM_TIME_QUOTA_CMD:
@@ -7772,6 +7874,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 17:07:10 -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 28 Nov 2017 18:54:19 -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 17:12:42 -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 29 Nov 2017 17:35:35 -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 17:07:31 -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 29 Nov 2017 18:06:56 -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 (-40) /* in dBm */
+#define IEEE80211_RSSI_THRES_5GHZ (-50) /* 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 */