summaryrefslogtreecommitdiff
path: root/sys/net80211
diff options
context:
space:
mode:
authorStefan Sperling <stsp@cvs.openbsd.org>2017-12-08 21:16:02 +0000
committerStefan Sperling <stsp@cvs.openbsd.org>2017-12-08 21:16:02 +0000
commit8dd7445ec11646a03591b357efe373d50259ad22 (patch)
treea06e8616a0dcf0a86323cc94ceccf649b81edb60 /sys/net80211
parentd1ff86bb1e72aebf0111a2f7ef8d27eafdcc16c3 (diff)
Add support for background scanning to net80211 and iwm(4).
The iwm(4) driver will now roam between access points which share an SSID. Use 'ifconfig iwm0 debug' and 'tail -f /var/log/messages' to watch it do so. Tested by several people in various iterations. As usual, let me know if you run into issues. ok phessler deraadt
Diffstat (limited to 'sys/net80211')
-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
6 files changed, 309 insertions, 56 deletions
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 */