-/* $OpenBSD: if_iwn.c,v 1.194 2017/10/26 15:00:28 mpi Exp $ */
+/* $OpenBSD: if_iwn.c,v 1.195 2017/12/14 14:21:11 stsp Exp $ */
/*-
* Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
uint16_t iwn_get_active_dwell_time(struct iwn_softc *, uint16_t, uint8_t);
uint16_t iwn_limit_dwell(struct iwn_softc *, uint16_t);
uint16_t iwn_get_passive_dwell_time(struct iwn_softc *, uint16_t);
-int iwn_scan(struct iwn_softc *, uint16_t);
+int iwn_scan(struct iwn_softc *, uint16_t, int);
+void iwn_scan_abort(struct iwn_softc *);
+int iwn_bgscan(struct ieee80211com *);
int iwn_auth(struct iwn_softc *, int);
int iwn_run(struct iwn_softc *);
int iwn_set_key(struct ieee80211com *, struct ieee80211_node *,
if_attach(ifp);
ieee80211_ifattach(ifp);
ic->ic_node_alloc = iwn_node_alloc;
+ ic->ic_bgscan_start = iwn_bgscan;
ic->ic_newassoc = iwn_newassoc;
ic->ic_updateedca = iwn_updateedca;
ic->ic_set_key = iwn_set_key;
struct iwn_node *wn = (void *)ni;
int error;
- timeout_del(&sc->calib_to);
-
- if (ic->ic_state == IEEE80211_S_RUN &&
- (ni->ni_flags & IEEE80211_NODE_HT))
+ if (ic->ic_state == IEEE80211_S_RUN) {
ieee80211_mira_cancel_timeouts(&wn->mn);
+ timeout_del(&sc->calib_to);
+ sc->calib.state = IWN_CALIB_STATE_INIT;
+ if (sc->sc_flags & IWN_FLAG_BGSCAN)
+ iwn_scan_abort(sc);
+ }
switch (nstate) {
case IEEE80211_S_SCAN:
/* Make the link LED blink while we're scanning. */
iwn_set_led(sc, IWN_LED_LINK, 10, 10);
- if ((error = iwn_scan(sc, IEEE80211_CHAN_2GHZ)) != 0) {
+ if ((error = iwn_scan(sc, IEEE80211_CHAN_2GHZ, 0)) != 0) {
printf("%s: could not initiate scan\n",
sc->sc_dev.dv_xname);
return error;
struct ieee80211_frame *wh;
struct ieee80211_rxinfo rxi;
struct ieee80211_node *ni;
+ struct ieee80211_channel *bss_chan = NULL;
struct mbuf *m, *m1;
struct iwn_rx_stat *stat;
caddr_t head;
uint32_t flags;
int error, len, rssi;
+ uint8_t chan;
if (desc->type == IWN_MPDU_RX_DONE) {
/* Check for prior RX_PHY notification. */
rssi = ops->get_rssi(stat);
+ chan = stat->chan;
+ if (chan > IEEE80211_CHAN_MAX)
+ chan = IEEE80211_CHAN_MAX;
+
+ if (ni == ic->ic_bss) {
+ bss_chan = ni->ni_chan;
+ /* Fix current channel. */
+ ni->ni_chan = &ic->ic_channels[chan];
+ }
+
#if NBPFILTER > 0
if (sc->sc_drvbpf != NULL) {
struct mbuf mb;
tap->wr_flags = 0;
if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE))
tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
- tap->wr_chan_freq =
- htole16(ic->ic_channels[stat->chan].ic_freq);
- chan_flags = ic->ic_channels[stat->chan].ic_flags;
+ tap->wr_chan_freq = htole16(ic->ic_channels[chan].ic_freq);
+ chan_flags = ic->ic_channels[chan].ic_flags;
if (ic->ic_curmode != IEEE80211_MODE_11N)
chan_flags &= ~IEEE80211_CHAN_HT;
tap->wr_chan_flags = htole16(chan_flags);
rxi.rxi_tstamp = 0; /* unused */
ieee80211_input(ifp, m, ni, &rxi);
+ /* Restore BSS channel. */
+ if (ni == ic->ic_bss)
+ ni->ni_chan = bss_chan;
+
/* Node is no longer needed. */
ieee80211_release_node(ic, ni);
}
DPRINTFN(2, ("scanning channel %d status %x\n",
scan->chan, letoh32(scan->status)));
+ if (sc->sc_flags & IWN_FLAG_BGSCAN)
+ break;
+
/* Fix current channel. */
ic->ic_bss->ni_chan = &ic->ic_channels[scan->chan];
break;
if (scan->status == 1 && scan->chan <= 14 &&
(sc->sc_flags & IWN_FLAG_HAS_5GHZ)) {
+ int error;
/*
* We just finished scanning 2GHz channels,
* start scanning 5GHz ones.
*/
- if (iwn_scan(sc, IEEE80211_CHAN_5GHZ) == 0)
+ error = iwn_scan(sc, IEEE80211_CHAN_5GHZ,
+ (sc->sc_flags & IWN_FLAG_BGSCAN) ? 1 : 0);
+ if (error == 0)
break;
}
ieee80211_end_scan(ifp);
+ sc->sc_flags &= ~IWN_FLAG_BGSCAN;
break;
}
case IWN5000_CALIBRATION_RESULT:
* XXX Yes, the math should take into account that bintval
* is 1.024mS, not 1mS..
*/
- if (bintval > 0) {
+ if (ic->ic_state == IEEE80211_S_RUN && bintval > 0)
return (MIN(IWN_PASSIVE_DWELL_BASE, ((bintval * 85) / 100)));
- }
/* No association context? Default */
return (IWN_PASSIVE_DWELL_BASE);
}
int
-iwn_scan(struct iwn_softc *sc, uint16_t flags)
+iwn_scan(struct iwn_softc *sc, uint16_t flags, int bgscan)
{
struct ieee80211com *ic = &sc->sc_ic;
struct iwn_scan_hdr *hdr;
hdr->quiet_time = htole16(10); /* timeout in milliseconds */
hdr->quiet_threshold = htole16(1); /* min # of packets */
+ if (bgscan) {
+ int bintval;
+
+ /* Set maximum off-channel time. */
+ hdr->max_out = htole32(200 * 1024);
+
+ /* Configure scan pauses which service on-channel traffic. */
+ bintval = ic->ic_bss->ni_intval ? ic->ic_bss->ni_intval : 100;
+ hdr->pause_scan = htole32(((100 / bintval) << 22) |
+ ((100 % bintval) * 1024));
+ }
+
/* Select antennas for scanning. */
rxchain =
IWN_RXCHAIN_VALID(sc->rxchainmask) |
hdr->len = htole16(buflen);
DPRINTF(("sending scan command nchan=%d\n", hdr->nchan));
+ if (bgscan)
+ sc->sc_flags |= IWN_FLAG_BGSCAN;
error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1);
+ if (bgscan && error)
+ sc->sc_flags &= ~IWN_FLAG_BGSCAN;
free(buf, M_DEVBUF, IWN_SCAN_MAXSZ);
return error;
}
+void
+iwn_scan_abort(struct iwn_softc *sc)
+{
+ iwn_cmd(sc, IWN_CMD_SCAN_ABORT, NULL, 0, 1);
+
+ /* XXX Cannot wait for status response in interrupt context. */
+ DELAY(100);
+
+ sc->sc_flags &= ~IWN_FLAG_BGSCAN;
+}
+
+int
+iwn_bgscan(struct ieee80211com *ic)
+{
+ struct iwn_softc *sc = ic->ic_softc;
+ int error;
+
+ error = iwn_scan(sc, IEEE80211_CHAN_2GHZ, 1);
+ if (error)
+ printf("%s: could not initiate background scan\n",
+ sc->sc_dev.dv_xname);
+ return error;
+}
+
int
iwn_auth(struct iwn_softc *sc, int arg)
{
struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211_node *ni = ic->ic_bss;
int error, ridx;
+ int bss_switch =
+ (!IEEE80211_ADDR_EQ(sc->bss_node_addr, etheranyaddr) &&
+ !IEEE80211_ADDR_EQ(sc->bss_node_addr, ni->ni_macaddr));
/* Update adapter configuration. */
IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
}
if (ic->ic_flags & IEEE80211_F_SHSLOT)
sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
+ else
+ sc->rxon.flags &= ~htole32(IWN_RXON_SHSLOT);
if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
+ else
+ sc->rxon.flags &= ~htole32(IWN_RXON_SHPREAMBLE);
switch (ic->ic_curmode) {
case IEEE80211_MODE_11A:
sc->rxon.cck_mask = 0;
* Make sure the firmware gets to see a beacon before we send
* the auth request. Otherwise the Tx attempt can fail due to
* the firmware's built-in regulatory domain enforcement.
+ * Delaying here for every incoming deauth frame can result in a DoS.
* Don't delay if we're here because of an incoming frame (arg != -1)
* or if we're already waiting for a response (ic_mgt_timer != 0).
+ * If we are switching APs after a background scan then net80211 has
+ * just faked the reception of a deauth frame from our old AP, so it
+ * is safe to delay in that case.
*/
- if (arg == -1 && ic->ic_mgt_timer == 0)
+ if ((arg == -1 || bss_switch) && ic->ic_mgt_timer == 0)
DELAY(ni->ni_intval * 3 * IEEE80211_DUR_TU);
+ /* We can now clear the cached address of our previous AP. */
+ memset(sc->bss_node_addr, 0, sizeof(sc->bss_node_addr));
+
return 0;
}
struct iwn_ops *ops = &sc->ops;
struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211_node *ni = ic->ic_bss;
+ struct iwn_node *wn = (void *)ni;
struct iwn_node_info node;
int error;
printf("%s: could not add BSS node\n", sc->sc_dev.dv_xname);
return error;
}
+
+ /* Cache address of AP in case it changes after a background scan. */
+ IEEE80211_ADDR_COPY(sc->bss_node_addr, ni->ni_macaddr);
+
DPRINTF(("setting link quality for node %d\n", node.id));
if ((error = iwn_set_link_quality(sc, ni)) != 0) {
printf("%s: could not setup link quality for node %d\n",
sc->calib_cnt = 0;
timeout_add_msec(&sc->calib_to, 500);
- if (ni->ni_flags & IEEE80211_NODE_HT) {
- struct iwn_node *wn = (void *)ni;
- ieee80211_mira_node_init(&wn->mn);
- }
+ ieee80211_mira_node_init(&wn->mn);
/* Link LED always on while associated. */
iwn_set_led(sc, IWN_LED_LINK, 0, 1);
struct ieee80211com *ic = &sc->sc_ic;
int error;
+ memset(sc->bss_node_addr, 0, sizeof(sc->bss_node_addr));
+
if ((error = iwn_hw_prepare(sc)) != 0) {
printf("%s: hardware not ready\n", sc->sc_dev.dv_xname);
goto fail;