This ensures that if WPA offloading is in use iwx(4) only sets link
state UP once firmware has confirmed that crypto keys have been
installed successfully.
iwm(4) could use a similar patch, but I've not written that yet.
ok?
diff f30a2d1c29071de858b3607fbaa2227aa7c6bfd3
57e0ba6871dcbf4d5516ac085c5c82b830b96e3d
blob - 2775f725d6347e9dbfcc2cf2ce296133ce7d6d47
blob + c0303528fdbc71291f3d5bf4d2677d351c86eb9c
--- sys/dev/pci/if_iwx.c
+++ sys/dev/pci/if_iwx.c
@@ -416,6 +416,7 @@ int iwx_run_stop(struct iwx_softc *);
struct ieee80211_node *iwx_node_alloc(struct ieee80211com *);
int iwx_set_key(struct ieee80211com *, struct ieee80211_node *,
struct ieee80211_key *);
+void iwx_setkey_task(void *);
void iwx_delete_key(struct ieee80211com *,
struct ieee80211_node *, struct ieee80211_key *);
int iwx_media_change(struct ifnet *);
@@ -6670,13 +6671,56 @@ iwx_set_key(struct ieee80211com *ic, struct ieee80211_
struct ieee80211_key *k)
{
struct iwx_softc *sc = ic->ic_softc;
- struct iwx_add_sta_key_cmd cmd;
+ struct iwx_setkey_task_arg *a;
if (k->k_cipher != IEEE80211_CIPHER_CCMP) {
/* Fallback to software crypto for other ciphers. */
return (ieee80211_set_key(ic, ni, k));
}
+ a = &sc->setkey_arg[sc->setkey_cur];
+ if (a->ni != NULL)
+ return ENOSPC;
+ a->sta_id = IWX_STATION_ID;
+ a->ni = ni;
+ a->k = k;
+ sc->setkey_cur = (sc->setkey_cur + 1) % nitems(sc->setkey_arg);
+
+ iwx_add_task(sc, systq, &sc->setkey_task);
+ return EBUSY;
+}
+
+void
+iwx_setkey_task(void *arg)
+{
+ struct iwx_softc *sc = arg;
+ struct iwx_setkey_task_arg *a;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni;
+ struct ieee80211_key *k;
+ int sta_id;
+ struct iwx_add_sta_key_cmd cmd;
+ uint32_t status;
+ int err, s = splnet();
+
+ if (sc->sc_flags & IWX_FLAG_SHUTDOWN)
+ goto out;
+
+ a = &sc->setkey_arg[sc->setkey_tail];
+ sta_id = a->sta_id;
+ ni = a->ni;
+ a->ni = NULL;
+ k = a->k;
+ a->k = NULL;
+ sc->setkey_tail = (sc->setkey_tail + 1) % nitems(sc->setkey_arg);
+
+ /*
+ * Keys are stored in 'ni' so 'k' is valid if 'ni' is valid.
+ * Currently we only implement station mode where 'ni' is always
+ * ic->ic_bss so there is no need to validate arguments beyond this:
+ */
+ KASSERT(ni == ic->ic_bss);
+
memset(&cmd, 0, sizeof(cmd));
cmd.common.key_flags = htole16(IWX_STA_KEY_FLG_CCM |
@@ -6690,12 +6734,29 @@ iwx_set_key(struct ieee80211com *ic, struct ieee80211_
cmd.common.key_offset = 0;
memcpy(cmd.common.key, k->k_key, MIN(sizeof(cmd.common.key), k->k_len));
- cmd.common.sta_id = IWX_STATION_ID;
+ cmd.common.sta_id = sta_id;
cmd.transmit_seq_cnt = htole64(k->k_tsc);
- return iwx_send_cmd_pdu(sc, IWX_ADD_STA_KEY, IWX_CMD_ASYNC,
- sizeof(cmd), &cmd);
+ status = IWX_ADD_STA_SUCCESS;
+ err = iwx_send_cmd_pdu_status(sc, IWX_ADD_STA_KEY, sizeof(cmd), &cmd,
+ &status);
+ if (sc->sc_flags & IWX_FLAG_SHUTDOWN)
+ goto out;
+ if (err || (status & IWX_ADD_STA_STATUS_MASK) != IWX_ADD_STA_SUCCESS) {
+ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+ } else {
+ DPRINTF(("marking port %s valid\n",
+ ether_sprintf(ni->ni_macaddr)));
+ ni->ni_port_valid = 1;
+ ieee80211_set_link_state(ic, LINK_STATE_UP);
+ }
+out:
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
+ return;
}
void
@@ -6868,6 +6929,9 @@ iwx_newstate(struct ieee80211com *ic, enum ieee80211_s
if (ic->ic_state == IEEE80211_S_RUN) {
iwx_del_task(sc, systq, &sc->ba_task);
+ iwx_del_task(sc, systq, &sc->setkey_task);
+ memset(sc->setkey_arg, 0, sizeof(sc->setkey_arg));
+ sc->setkey_cur = sc->setkey_tail = 0;
iwx_del_task(sc, systq, &sc->mac_ctxt_task);
for (i = 0; i < nitems(sc->sc_rxba_data); i++) {
struct iwx_rxba_data *rxba = &sc->sc_rxba_data[i];
@@ -7440,6 +7504,9 @@ iwx_stop(struct ifnet *ifp)
task_del(systq, &sc->init_task);
iwx_del_task(sc, sc->sc_nswq, &sc->newstate_task);
iwx_del_task(sc, systq, &sc->ba_task);
+ iwx_del_task(sc, systq, &sc->setkey_task);
+ memset(sc->setkey_arg, 0, sizeof(sc->setkey_arg));
+ sc->setkey_cur = sc->setkey_tail = 0;
iwx_del_task(sc, systq, &sc->mac_ctxt_task);
KASSERT(sc->task_refs.refs >= 1);
refcnt_finalize(&sc->task_refs, "iwxstop");
@@ -8837,6 +8904,7 @@ iwx_attach(struct device *parent, struct device *self,
task_set(&sc->init_task, iwx_init_task, sc);
task_set(&sc->newstate_task, iwx_newstate_task, sc);
task_set(&sc->ba_task, iwx_ba_task, sc);
+ task_set(&sc->setkey_task, iwx_setkey_task, sc);
task_set(&sc->mac_ctxt_task, iwx_mac_ctxt_task, sc);
ic->ic_node_alloc = iwx_node_alloc;
blob - 993528772f7face7580e9b461b4d795b14ade10a
blob + 224bc5626a20b1beaaf30b430cb901fdf800ed62
--- sys/dev/pci/if_iwxvar.h
+++ sys/dev/pci/if_iwxvar.h
@@ -438,6 +438,12 @@ struct iwx_rxq_dup_data {
uint8_t last_sub_frame[IWX_MAX_TID_COUNT + 1];
};
+struct iwx_setkey_task_arg {
+ int sta_id;
+ struct ieee80211_node *ni;
+ struct ieee80211_key *k;
+};
+
struct iwx_softc {
struct device sc_dev;
struct ieee80211com sc_ic;
@@ -458,6 +464,19 @@ struct iwx_softc {
uint16_t ba_winsize[IWX_MAX_TID_COUNT];
int ba_timeout_val[IWX_MAX_TID_COUNT];
+ /* Task for setting encryption keys and its arguments. */
+ struct task setkey_task;
+ /*
+ * At present we need to process at most two keys at once:
+ * Our pairwise key and a group key.
+ * When hostap mode is implemented this array needs to grow or
+ * it might become a bottleneck for associations that occur at
+ * roughly the same time.
+ */
+ struct iwx_setkey_task_arg setkey_arg[2];
+ int setkey_cur;
+ int setkey_tail;
+
/* Task for ERP/HT prot/slot-time/EDCA updates. */
struct task mac_ctxt_task;