summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/pci/if_iwm.c146
-rw-r--r--sys/dev/pci/if_iwmvar.h3
-rw-r--r--sys/net80211/ieee80211.c30
-rw-r--r--sys/net80211/ieee80211_input.c39
-rw-r--r--sys/net80211/ieee80211_node.c260
-rw-r--r--sys/net80211/ieee80211_node.h8
-rw-r--r--sys/net80211/ieee80211_proto.c9
-rw-r--r--sys/net80211/ieee80211_var.h19
8 files changed, 434 insertions, 80 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c
index 47905c03df7..ead82d7da64 100644
--- a/sys/dev/pci/if_iwm.c
+++ b/sys/dev/pci/if_iwm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwm.c,v 1.220 2017/12/08 20:55:46 stsp Exp $ */
+/* $OpenBSD: if_iwm.c,v 1.221 2017/12/08 21:16:01 stsp Exp $ */
/*
* Copyright (c) 2014, 2016 genua gmbh <info@genua.de>
@@ -420,9 +420,9 @@ uint32_t iwm_scan_rate_n_flags(struct iwm_softc *, int, int);
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 *, struct iwm_node *, int);
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 *);
@@ -4879,7 +4883,7 @@ iwm_fill_probe_req(struct iwm_softc *sc, struct iwm_scan_probe_req *preq)
}
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 = {
@@ -4890,28 +4894,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);
@@ -5059,7 +5069,7 @@ iwm_config_umac_scan(struct iwm_softc *sc)
}
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 = {
@@ -5071,7 +5081,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) *
@@ -5079,20 +5089,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);
@@ -5472,13 +5489,19 @@ 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_BGSCAN) {
+ err = iwm_scan_abort(sc);
+ if (err) {
+ printf("%s: could not abort background 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;
@@ -5493,6 +5516,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_BGSCAN;
+ 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 | IWM_FLAG_BGSCAN);
+ return err;
+}
+
+int
iwm_auth(struct iwm_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
@@ -6116,10 +6212,10 @@ iwm_endscan(struct iwm_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
- if ((sc->sc_flags & IWM_FLAG_SCANNING) == 0)
+ if ((sc->sc_flags & (IWM_FLAG_SCANNING | IWM_FLAG_BGSCAN)) == 0)
return;
- sc->sc_flags &= ~IWM_FLAG_SCANNING;
+ sc->sc_flags &= ~(IWM_FLAG_SCANNING | IWM_FLAG_BGSCAN);
ieee80211_end_scan(&ic->ic_if);
}
@@ -6550,9 +6646,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)
@@ -6632,7 +6729,7 @@ iwm_stop(struct ifnet *ifp)
if (ic->ic_state == IEEE80211_S_RUN)
ieee80211_mira_cancel_timeouts(&in->in_mn); /* XXX refcount? */
- sc->sc_flags &= ~IWM_FLAG_SCANNING;
+ sc->sc_flags &= ~(IWM_FLAG_SCANNING | IWM_FLAG_BGSCAN);
sc->sc_flags &= ~IWM_FLAG_MAC_ACTIVE;
sc->sc_flags &= ~IWM_FLAG_BINDING_ACTIVE;
sc->sc_flags &= ~IWM_FLAG_STA_ACTIVE;
@@ -7132,7 +7229,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:
@@ -7798,6 +7897,7 @@ iwm_attach(struct device *parent, struct device *self, void *aux)
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;
diff --git a/sys/dev/pci/if_iwmvar.h b/sys/dev/pci/if_iwmvar.h
index 3896d8dee93..7e35820c7ce 100644
--- a/sys/dev/pci/if_iwmvar.h
+++ b/sys/dev/pci/if_iwmvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwmvar.h,v 1.36 2017/10/22 09:51:58 stsp Exp $ */
+/* $OpenBSD: if_iwmvar.h,v 1.37 2017/12/08 21:16:01 stsp Exp $ */
/*
* Copyright (c) 2014 genua mbh <info@genua.de>
@@ -288,6 +288,7 @@ struct iwm_rx_ring {
#define IWM_FLAG_TE_ACTIVE 0x40 /* time event is scheduled */
#define IWM_FLAG_HW_ERR 0x80 /* hardware error occurred */
#define IWM_FLAG_SHUTDOWN 0x100 /* shutting down; new tasks forbidden */
+#define IWM_FLAG_BGSCAN 0x200 /* background scan in progress */
struct iwm_ucode_status {
uint32_t uc_error_event_table;
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c
index ff788315911..9e92fbbae19 100644
--- a/sys/net80211/ieee80211.c
+++ b/sys/net80211/ieee80211.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211.c,v 1.63 2017/09/05 12:02:21 stsp Exp $ */
+/* $OpenBSD: ieee80211.c,v 1.64 2017/12/08 21:16:01 stsp Exp $ */
/* $NetBSD: ieee80211.c,v 1.19 2004/06/06 05:45:29 dyoung Exp $ */
/*-
@@ -72,6 +72,32 @@ void ieee80211_setbasicrates(struct ieee80211com *);
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
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index e7add1e4a75..be869244e40 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_input.c,v 1.196 2017/09/04 09:11:46 stsp Exp $ */
+/* $OpenBSD: ieee80211_input.c,v 1.197 2017/12/08 21:16:01 stsp Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
@@ -267,6 +267,14 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
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
@@ -1504,7 +1512,8 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m,
#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 +1598,8 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m,
* 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 +1616,8 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m,
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 +2372,13 @@ ieee80211_recv_deauth(struct ieee80211com *ic, struct mbuf *m,
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 +2422,13 @@ ieee80211_recv_disassoc(struct ieee80211com *ic, struct mbuf *m,
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:
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index aac5308668f..c0675b17958 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_node.c,v 1.121 2017/09/05 14:56:59 stsp Exp $ */
+/* $OpenBSD: ieee80211_node.c,v 1.122 2017/12/08 21:16:01 stsp Exp $ */
/* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */
/*-
@@ -65,12 +65,16 @@ void ieee80211_node_copy(struct ieee80211com *, struct ieee80211_node *,
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 *ic, struct ieee80211_node *ni)
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 (bgscan && ieee80211_node_cmp(ic->ic_bss, ni) == 0)
+ 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 ieee80211com *ic, struct ieee80211_node *ni)
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 ieee80211com *ic,
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 ieee80211com *ic, struct ieee80211_node *ni)
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);
}
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index fc088b3324f..2a5b0f1f0dc 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_node.h,v 1.69 2017/08/17 06:01:05 stsp Exp $ */
+/* $OpenBSD: ieee80211_node.h,v 1.70 2017/12/08 21:16:01 stsp Exp $ */
/* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */
/*-
@@ -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);
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
index c0978acfc31..8f6d804ae77 100644
--- a/sys/net80211/ieee80211_proto.c
+++ b/sys/net80211/ieee80211_proto.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_proto.c,v 1.80 2017/08/18 17:30:12 stsp Exp $ */
+/* $OpenBSD: ieee80211_proto.c,v 1.81 2017/12/08 21:16:01 stsp Exp $ */
/* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */
/*-
@@ -853,6 +853,7 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate,
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,
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index 593860863c1..51c3c56cbf0 100644
--- a/sys/net80211/ieee80211_var.h
+++ b/sys/net80211/ieee80211_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_var.h,v 1.81 2017/11/06 11:34:29 phessler Exp $ */
+/* $OpenBSD: ieee80211_var.h,v 1.82 2017/12/08 21:16:01 stsp Exp $ */
/* $NetBSD: ieee80211_var.h,v 1.7 2004/05/06 03:07:10 dyoung Exp $ */
/*-
@@ -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,8 +365,12 @@ extern struct ieee80211com_head ieee80211com_head;
#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 */
#define IEEE80211_C_IBSS 0x00000002 /* CAPABILITY: IBSS available */