From: Stuart Henderson Subject: Re: wifi protected management frame (PMF) support To: tech@openbsd.org Date: Sun, 23 Nov 2025 13:48:37 +0000 works for me against hapax2 (QCN-5052) running routeros 7.20.4 with PMF required; iwx0 at pci0 dev 20 function 3 "Intel Wi-Fi 6 AX211" rev 0x01, msix iwx0: hw rev 0x370, fw 77.f92b5fed.0, pnvm ce1a5094, address 2c:33:58:0a:b5:54 iwx0: using firmware iwx-so-a0-gf-a0-77 On 2025/11/22 22:45, Stefan Sperling wrote: > This patch adds protected management frame support to iwm, iwx, and qwx. > Support for PMF is a prerequisite for WPA3. > > I am sending this as one giant patch for testing. I do have incremental > changes with individual commit messages which make review a bit easier. > If you would like to review these diffs individually, please ask me to > send them to you. > > Tested by me on: > iwm 7265, 9265 (offloads unicast PMF, multicast is done in software) > iwx AX200 (offloads both unicast and multicast PMF) > qwx QCNFA765 (offloads unicast PMF, multicast is done in software) > > Use of PMF is controlled by the access point, so there is nothing to > configure with ifconfig. Please check if your access point offers settings > related to management frame protection related when testing this. > Tests in any combination of PMF disabled/optional/required across a range > of access points would be welcome. > > In particular, I don't have any iwx "MA" devices to test with. There > could still be unexpected problems such as firmware crashes on these. > If you enable 'ifconfig iwx0 debug' then the driver should display the > name of its firmware file in dmesg. If this name begins with "iwx-ma-" > then you are using an MA device. > > This patch also contains a drive-by fix for iwx MA devices: The driver > never removed crypto keys from firmware on these devices. I do not know > whether this bug actually caused issues in practice but it is worth fixing. > > This effort is supported by the NLnet Foundation's NGI0 Commons Fund, and > relies on PMF support code committed to OpenBSD by Damien Bergamini in 2009. > > I am also grateful for clues from public discussion among OpenIntelWireless > (hackintosh) developers, who wrote down their experiences with PMF here: > https://github.com/OpenIntelWireless/itlwm/pull/676 > > Throughout the code, "PMF" (protected management frames) and "MFP" (management > frame protection) are used interchangably. I don't intend to fix this. > Damien chose "MFP" in most places and changing this would cause needless churn. > > M share/man/man4/iwm.4 | 6+ 0- > M share/man/man4/iwx.4 | 6+ 0- > M share/man/man4/qwx.4 | 10+ 0- > M sys/dev/ic/qwx.c | 107+ 21- > M sys/dev/ic/qwxvar.h | 2+ 0- > M sys/dev/pci/if_iwm.c | 54+ 1- > M sys/dev/pci/if_iwmvar.h | 2+ 0- > M sys/dev/pci/if_iwx.c | 167+ 12- > M sys/dev/pci/if_iwxreg.h | 22+ 4- > M sys/dev/pci/if_iwxvar.h | 9+ 5- > M sys/dev/pci/if_qwx_pci.c | 2+ 1- > M sys/net80211/ieee80211_crypto.c | 11+ 5- > M sys/net80211/ieee80211_input.c | 22+ 9- > M sys/net80211/ieee80211_ioctl.c | 1+ 0- > M sys/net80211/ieee80211_node.c | 5+ 0- > M sys/net80211/ieee80211_output.c | 1+ 1- > M sys/net80211/ieee80211_pae_input.c | 9+ 0- > M sys/net80211/ieee80211_proto.c | 2+ 0- > > 18 files changed, 438 insertions(+), 59 deletions(-) > > commit - a7f244ac5cee08f458e351af876c95d354c6d6e4 > commit + 881c285030b176970507181531f1c9628160a001 > blob - 51e6f730062c7a11127cb96dd267a04b7761fd2b > blob + 9300624d160ff42704c9b5cafa07e9572bb9e075 > --- share/man/man4/iwm.4 > +++ share/man/man4/iwm.4 > @@ -73,6 +73,12 @@ The > driver offloads both encryption and decryption of unicast data frames to the > hardware for the CCMP cipher. > .Pp > +The > +.Nm > +driver supports protected management frames (PMF) which prevents spoofing > +of management frames such as deauthentication, disassociation, and block-ack > +negotiation, on networks using WPA. > +.Pp > In BSS mode the driver supports background scanning; > see > .Xr ifconfig 8 . > blob - dcfbdc9b9bf58238c8540791a3544cb55b7cc9c8 > blob + 5db7fbde1ed3c16e071ec633614bfcfdb7b89806 > --- share/man/man4/iwx.4 > +++ share/man/man4/iwx.4 > @@ -70,6 +70,12 @@ The > driver offloads both encryption and decryption of unicast data frames to the > hardware for the CCMP cipher. > .Pp > +The > +.Nm > +driver supports protected management frames (PMF) which prevents spoofing > +of management frames such as deauthentication, disassociation, and block-ack > +negotiation, on networks using WPA. > +.Pp > In BSS mode the driver supports background scanning; > see > .Xr ifconfig 8 . > blob - 834dc4be5246ecef57c3e4a34d0a3b3f4a0c6ce4 > blob + de9a36265725c5c069c60782ecad548207218839 > --- share/man/man4/qwx.4 > +++ share/man/man4/qwx.4 > @@ -66,6 +66,16 @@ hardware for the CCMP cipher. > .Pp > The > .Nm > +driver supports protected management frames (PMF) which prevents spoofing > +of management frames such as deauthentication, disassociation, and block-ack > +negotiation, on networks using WPA. > +.Pp > +In BSS mode the driver supports background scanning; > +see > +.Xr ifconfig 8 . > +.Pp > +The > +.Nm > driver can be configured at runtime with > .Xr ifconfig 8 > or on boot with > blob - ad3588d5c0f88b8de4171f7617551bfc058e6a36 > blob + 54763a0a479ab49c63d27dbd2de0102b232156b3 > --- sys/dev/ic/qwx.c > +++ sys/dev/ic/qwx.c > @@ -350,6 +350,49 @@ qwx_del_task(struct qwx_softc *sc, struct taskq *taskq > } > > void > +qwx_mfp_leave_done(struct ieee80211com *ic, struct ieee80211_node *ni) > +{ > + struct qwx_softc *sc = ic->ic_softc; > + struct ifnet *ifp = &ic->ic_if; > + > + if ((ifp->if_flags & IFF_RUNNING) && > + ic->ic_state == IEEE80211_S_RUN && > + (ni->ni_flags & IEEE80211_NODE_MFP)) { > + sc->deauth_sent = 1; > + wakeup(&sc->deauth_sent); > + } > +} > + > +void > +qwx_mfp_leave(struct qwx_softc *sc) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + struct ieee80211_node *ni = (void *)ic->ic_bss; > + > + ic->ic_xflags |= IEEE80211_F_TX_MGMT_ONLY; > + sc->deauth_sent = 0; > + > + ni->ni_unref_cb = qwx_mfp_leave_done; > + ni->ni_unref_arg = NULL; > + ni->ni_unref_arg_size = 0; > + > + /* > + * Send an authenticated deauth frame in order to let our AP know we > + * are leaving. This allows our AP to tear down MFP state cleanly. > + * Otherwise we would remain locked out of this AP until a timeout > + * of stale MFP state occurs at the AP, which might take a while. > + */ > + if (IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, > + IEEE80211_REASON_AUTH_LEAVE) != 0) { > + ni->ni_unref_cb = NULL; > + return; > + } > + > + if (tsleep_nsec(&sc->deauth_sent, 0, "qwxlv", MSEC_TO_NSEC(500)) != 0) > + ni->ni_unref_cb = NULL; > +} > + > +void > qwx_stop(struct ifnet *ifp) > { > struct qwx_softc *sc = ifp->if_softc; > @@ -373,6 +416,13 @@ qwx_stop(struct ifnet *ifp) > qwx_del_task(sc, systq, &sc->bgscan_task); > refcnt_finalize(&sc->task_refs, "qwxstop"); > > + clear_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags); > + > + if (ic->ic_opmode == IEEE80211_M_STA && > + ic->ic_state == IEEE80211_S_RUN && > + (ic->ic_bss->ni_flags & IEEE80211_NODE_MFP)) > + qwx_mfp_leave(sc); > + > qwx_setkey_clear(sc); > > ifp->if_timer = sc->sc_tx_timer = 0; > @@ -380,8 +430,6 @@ qwx_stop(struct ifnet *ifp) > ifp->if_flags &= ~IFF_RUNNING; > ifq_clr_oactive(&ifp->if_snd); > > - clear_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags); > - > /* > * Manually run the newstate task's code for switching to INIT state. > * This reconfigures firmware state to stop scanning, or disassociate > @@ -661,7 +709,8 @@ qwx_set_key(struct ieee80211com *ic, struct ieee80211_ > > if (test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags) || > k->k_cipher == IEEE80211_CIPHER_WEP40 || > - k->k_cipher == IEEE80211_CIPHER_WEP104) > + k->k_cipher == IEEE80211_CIPHER_WEP104 || > + k->k_cipher == IEEE80211_CIPHER_BIP) > return ieee80211_set_key(ic, ni, k); > > return qwx_queue_setkey_cmd(ic, ni, k, QWX_ADD_KEY); > @@ -675,7 +724,8 @@ qwx_delete_key(struct ieee80211com *ic, struct ieee802 > > if (test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags) || > k->k_cipher == IEEE80211_CIPHER_WEP40 || > - k->k_cipher == IEEE80211_CIPHER_WEP104) { > + k->k_cipher == IEEE80211_CIPHER_WEP104 || > + k->k_cipher == IEEE80211_CIPHER_BIP) { > ieee80211_delete_key(ic, ni, k); > return; > } > @@ -13513,23 +13563,25 @@ qwx_mgmt_rx_event(struct qwx_softc *sc, struct mbuf *m > > wh = mtod(m, struct ieee80211_frame *); > ni = ieee80211_find_rxnode(ic, wh); > -#if 0 > + > /* In case of PMF, FW delivers decrypted frames with Protected Bit set. > * Don't clear that. Also, FW delivers broadcast management frames > * (ex: group privacy action frames in mesh) as encrypted payload. > */ > - if (ieee80211_has_protected(hdr->frame_control) && > - !is_multicast_ether_addr(ieee80211_get_DA(hdr))) { > - status->flag |= RX_FLAG_DECRYPTED; > - > + if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && > + !IEEE80211_IS_MULTICAST(wh->i_addr1)) { > + rxi.rxi_flags |= IEEE80211_RXI_HWDEC; > +#if 0 > if (!ieee80211_is_robust_mgmt_frame(skb)) { > status->flag |= RX_FLAG_IV_STRIPPED | > RX_FLAG_MMIC_STRIPPED; > hdr->frame_control = __cpu_to_le16(fc & > ~IEEE80211_FCTL_PROTECTED); > } > +#endif > } > > +#if 0 > if (ieee80211_is_beacon(hdr->frame_control)) > ath11k_mac_handle_beacon(ar, skb); > #endif > @@ -25122,6 +25174,18 @@ qwx_dp_tx(struct qwx_softc *sc, struct qwx_vif *arvif, > FIELD_PREP(DP_TX_DESC_ID_POOL_ID, pool_id); > ti.encap_type = qwx_dp_tx_get_encap_type(sc); > > + if (ti.encap_type != HAL_TCL_ENCAP_TYPE_RAW && > + IEEE80211_IS_MULTICAST(wh->i_addr1) && > + (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && > + (ni->ni_flags & IEEE80211_NODE_MFP) && > + k->k_cipher == IEEE80211_CIPHER_BIP) { > + /* BIP is done in software crypto. */ > + if ((m = ieee80211_encrypt(ic, m, k)) == NULL) > + return ENOBUFS; > + /* 802.11 header may have moved. */ > + wh = mtod(m, struct ieee80211_frame *); > + } > + > ti.meta_data_flags = arvif->tcl_metadata; > > if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && > @@ -25147,6 +25211,7 @@ qwx_dp_tx(struct qwx_softc *sc, struct qwx_vif *arvif, > return ENOSPC; > } > break; > + case IEEE80211_CIPHER_BIP: > default: > ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN; > break; > @@ -25342,32 +25407,53 @@ qwx_mac_mgmt_tx_wmi(struct qwx_softc *sc, struct qwx_v > { > struct qwx_txmgmt_queue *txmgmt = &arvif->txmgmt; > struct qwx_tx_data *tx_data; > + struct ieee80211_frame *wh; > int buf_id; > int ret; > + uint8_t subtype; > > buf_id = txmgmt->cur; > > DNPRINTF(QWX_D_MAC, "%s: tx mgmt frame, buf id %d\n", __func__, buf_id); > > - if (txmgmt->queued >= nitems(txmgmt->data)) > + if (txmgmt->queued >= nitems(txmgmt->data)) { > + m_freem(m); > return ENOSPC; > + } > > tx_data = &txmgmt->data[buf_id]; > -#if 0 > - if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) { > - if ((ieee80211_is_action(hdr->frame_control) || > - ieee80211_is_deauth(hdr->frame_control) || > - ieee80211_is_disassoc(hdr->frame_control)) && > - ieee80211_has_protected(hdr->frame_control)) { > - skb_put(skb, IEEE80211_CCMP_MIC_LEN); > + > + wh = mtod(m, struct ieee80211_frame *); > + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; > + > + if ((ni->ni_flags & IEEE80211_NODE_MFP) && > + (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && > + (subtype == IEEE80211_FC0_SUBTYPE_DISASSOC || > + subtype == IEEE80211_FC0_SUBTYPE_DEAUTH || > + subtype == IEEE80211_FC0_SUBTYPE_ACTION)) { > + int off; > + > + /* Make space for CCMP header. */ > + if (m_makespace(m, ieee80211_get_hdrlen(wh), > + IEEE80211_CCMP_HDRLEN, &off) == NULL) { > + m_freem(m); > + return ENOMEM; > } > + > + /* Add trailing space for CCMP MIC. */ > + if (m_makespace(m, m->m_pkthdr.len, > + IEEE80211_CCMP_MICLEN, &off) == NULL) { > + m_freem(m); > + return ENOMEM; > + } > } > -#endif > + > ret = bus_dmamap_load_mbuf(sc->sc_dmat, tx_data->map, > m, BUS_DMA_WRITE | BUS_DMA_NOWAIT); > if (ret && ret != EFBIG) { > printf("%s: failed to map mgmt Tx buffer: %d\n", > sc->sc_dev.dv_xname, ret); > + m_freem(m); > return ret; > } > if (ret) { > @@ -25390,6 +25476,7 @@ qwx_mac_mgmt_tx_wmi(struct qwx_softc *sc, struct qwx_v > if (ret) { > printf("%s: failed to send mgmt frame: %d\n", > sc->sc_dev.dv_xname, ret); > + m_freem(m); > goto err_unmap_buf; > } > tx_data->ni = ni; > @@ -26110,12 +26197,11 @@ qwx_peer_assoc_h_crypto(struct qwx_softc *sc, struct q > if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) > arg->need_gtk_2_way = 1; > } > -#if 0 > - if (sta->mfp) { > + > + if (ni->ni_flags & IEEE80211_NODE_MFP) { > /* TODO: Need to check if FW supports PMF? */ > arg->is_pmf_enabled = true; > } > -#endif > } > > int > blob - 19e9c240e0b896774314a28f9449f0eb164b9338 > blob + 0cb96e4436da925ea03241c598ba60f21d90e472 > --- sys/dev/ic/qwxvar.h > +++ sys/dev/ic/qwxvar.h > @@ -1835,6 +1835,8 @@ struct qwx_softc { > enum ieee80211_state ns_nstate; > int ns_arg; > > + int deauth_sent; > + > /* Task for setting encryption keys and its arguments. */ > struct task setkey_task; > /* > blob - 749a4c86102ca448faff79649f664196437bb043 > blob + c71bb809b6c58cb19f0b5ca2daf98799e62bd983 > --- sys/dev/pci/if_iwm.c > +++ sys/dev/pci/if_iwm.c > @@ -9124,6 +9124,8 @@ iwm_set_key_v1(struct ieee80211com *ic, struct ieee802 > IWM_STA_KEY_FLG_KEYID_MSK)); > if (k->k_flags & IEEE80211_KEY_GROUP) > cmd.common.key_flags |= htole16(IWM_STA_KEY_MULTICAST); > + if (ni->ni_flags & IEEE80211_NODE_MFP) > + cmd.common.key_flags |= htole16(IWM_STA_KEY_MFP); > > memcpy(cmd.common.key, k->k_key, MIN(sizeof(cmd.common.key), k->k_len)); > cmd.common.key_offset = 0; > @@ -9157,6 +9159,8 @@ iwm_set_key(struct ieee80211com *ic, struct ieee80211_ > IWM_STA_KEY_FLG_KEYID_MSK)); > if (k->k_flags & IEEE80211_KEY_GROUP) > cmd.common.key_flags |= htole16(IWM_STA_KEY_MULTICAST); > + if (ni->ni_flags & IEEE80211_NODE_MFP) > + cmd.common.key_flags |= htole16(IWM_STA_KEY_MFP); > > memcpy(cmd.common.key, k->k_key, MIN(sizeof(cmd.common.key), k->k_len)); > cmd.common.key_offset = 0; > @@ -10499,6 +10503,49 @@ iwm_start(struct ifnet *ifp) > } > > void > +iwm_mfp_leave_done(struct ieee80211com *ic, struct ieee80211_node *ni) > +{ > + struct iwm_softc *sc = ic->ic_softc; > + struct ifnet *ifp = IC2IFP(&sc->sc_ic); > + > + if ((ifp->if_flags & IFF_RUNNING) && > + ic->ic_state == IEEE80211_S_RUN && > + (ni->ni_flags & IEEE80211_NODE_MFP)) { > + sc->deauth_sent = 1; > + wakeup(&sc->deauth_sent); > + } > +} > + > +void > +iwm_mfp_leave(struct iwm_softc *sc) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + struct ieee80211_node *ni = (void *)ic->ic_bss; > + > + ic->ic_xflags |= IEEE80211_F_TX_MGMT_ONLY; > + sc->deauth_sent = 0; > + > + ni->ni_unref_cb = iwm_mfp_leave_done; > + ni->ni_unref_arg = NULL; > + ni->ni_unref_arg_size = 0; > + > + /* > + * Send an authenticated deauth frame in order to let our AP know we > + * are leaving. This allows our AP to tear down MFP state cleanly. > + * Otherwise we would remain locked out of this AP until a timeout > + * of stale MFP state occurs at the AP, which might take a while. > + */ > + if (IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, > + IEEE80211_REASON_AUTH_LEAVE) != 0) { > + ni->ni_unref_cb = NULL; > + return; > + } > + > + if (tsleep_nsec(&sc->deauth_sent, 0, "iwmlv", MSEC_TO_NSEC(500)) != 0) > + ni->ni_unref_cb = NULL; > +} > + > +void > iwm_stop(struct ifnet *ifp) > { > struct iwm_softc *sc = ifp->if_softc; > @@ -10520,6 +10567,11 @@ iwm_stop(struct ifnet *ifp) > KASSERT(sc->task_refs.r_refs >= 1); > refcnt_finalize(&sc->task_refs, "iwmstop"); > > + if (ic->ic_opmode == IEEE80211_M_STA && > + ic->ic_state == IEEE80211_S_RUN && > + (ic->ic_bss->ni_flags & IEEE80211_NODE_MFP)) > + iwm_mfp_leave(sc); > + > iwm_stop_device(sc); > > free(sc->bgscan_unref_arg, M_DEVBUF, sc->bgscan_unref_arg_size); > @@ -11943,7 +11995,8 @@ iwm_attach(struct device *parent, struct device *self, > IEEE80211_C_SCANALLBAND | /* device scans all bands at once */ > IEEE80211_C_MONITOR | /* monitor mode supported */ > IEEE80211_C_SHSLOT | /* short slot time supported */ > - IEEE80211_C_SHPREAMBLE; /* short preamble supported */ > + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ > + IEEE80211_C_MFP; /* management frame protection */ > > ic->ic_htcaps = IEEE80211_HTCAP_SGI20 | IEEE80211_HTCAP_SGI40; > ic->ic_htcaps |= IEEE80211_HTCAP_CBW20_40; > blob - 113a547137cac0aa956e8069f94dcc76918950f7 > blob + d951e4a81be77de01fbd0f13161c3fa63e893e9c > --- sys/dev/pci/if_iwmvar.h > +++ sys/dev/pci/if_iwmvar.h > @@ -487,6 +487,8 @@ struct iwm_softc { > enum ieee80211_state ns_nstate; > int ns_arg; > > + int deauth_sent; > + > /* Task for firmware BlockAck setup/teardown and its arguments. */ > struct task ba_task; > struct iwm_ba_task_data ba_rx; > blob - 8d48397c0ec35388af23de2c26b2087dffc650e2 > blob + 218cdbee9ea115d62962eb18b6efce158e501ebc > --- sys/dev/pci/if_iwx.c > +++ sys/dev/pci/if_iwx.c > @@ -6315,7 +6315,8 @@ iwx_tx(struct iwx_softc *sc, struct mbuf *m, struct ie > > if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { > k = ieee80211_get_txkey(ic, wh, ni); > - if (k->k_cipher != IEEE80211_CIPHER_CCMP) { > + if (k->k_cipher != IEEE80211_CIPHER_CCMP && > + (k->k_flags & IEEE80211_KEY_IGTK) == 0) { > if ((m = ieee80211_encrypt(ic, m, k)) == NULL) > return ENOBUFS; > /* 802.11 header may have moved. */ > @@ -6985,6 +6986,9 @@ iwx_mld_add_sta_cmd(struct iwx_softc *sc, struct iwx_n > sta_cmd.tx_ampdu_max_size = htole32(aggsize); > } > > + if (in->in_ni.ni_flags & IEEE80211_NODE_MFP) > + sta_cmd.mfp = htole32(1); > + > return iwx_send_cmd_pdu(sc, > IWX_WIDE_ID(IWX_MAC_CONF_GROUP, IWX_STA_CONFIG_CMD), > 0, sizeof(sta_cmd), &sta_cmd); > @@ -8765,7 +8769,8 @@ iwx_set_key(struct ieee80211com *ic, struct ieee80211_ > struct iwx_setkey_task_arg *a; > int err; > > - if (k->k_cipher != IEEE80211_CIPHER_CCMP) { > + if (k->k_cipher != IEEE80211_CIPHER_CCMP && > + (k->k_flags & IEEE80211_KEY_IGTK) == 0) { > /* Fallback to software crypto for other ciphers. */ > err = ieee80211_set_key(ic, ni, k); > if (!err && in != NULL && (k->k_flags & IEEE80211_KEY_GROUP)) > @@ -8787,8 +8792,9 @@ iwx_set_key(struct ieee80211com *ic, struct ieee80211_ > } > > int > -iwx_mld_add_sta_key_cmd(struct iwx_softc *sc, int sta_id, > - struct ieee80211_node *ni, struct ieee80211_key *k) > +iwx_mld_set_sta_key_cmd(struct iwx_softc *sc, int sta_id, > + struct ieee80211_node *ni, struct ieee80211_key *k, > + int remove_key) > { > struct ieee80211com *ic = &sc->sc_ic; > struct iwx_sec_key_cmd cmd; > @@ -8797,6 +8803,10 @@ iwx_mld_add_sta_key_cmd(struct iwx_softc *sc, int sta_ > > if (k->k_flags & IEEE80211_KEY_GROUP) > flags |= IWX_SEC_KEY_FLAG_MCAST_KEY; > + else if (k->k_flags & IEEE80211_KEY_IGTK) > + flags |= IWX_SEC_KEY_FLAG_MCAST_KEY | IWX_SEC_KEY_FLAG_MFP; > + else if (ni->ni_flags & IEEE80211_NODE_MFP) > + flags |= IWX_SEC_KEY_FLAG_MFP; > > memset(&cmd, 0, sizeof(cmd)); > cmd.u.add.sta_mask = htole32(1 << sta_id); > @@ -8804,12 +8814,18 @@ iwx_mld_add_sta_key_cmd(struct iwx_softc *sc, int sta_ > cmd.u.add.key_flags = htole32(flags); > cmd.u.add.tx_seq = htole64(k->k_tsc); > memcpy(cmd.u.add.key, k->k_key, k->k_len); > - cmd.action = IWX_FW_CTXT_ACTION_ADD; > + if (remove_key) > + cmd.action = IWX_FW_CTXT_ACTION_REMOVE; > + else > + cmd.action = IWX_FW_CTXT_ACTION_ADD; > > err = iwx_send_cmd_pdu(sc, > IWX_WIDE_ID(IWX_DATA_PATH_GROUP, IWX_SEC_KEY_CMD), > - 0, sizeof(cmd), &cmd); > + remove_key ? IWX_CMD_ASYNC : 0, sizeof(cmd), &cmd); > if (err) { > + if (remove_key) > + return err; > + > IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, > IEEE80211_REASON_AUTH_LEAVE); > ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); > @@ -8837,8 +8853,11 @@ iwx_add_sta_key_cmd(struct iwx_softc *sc, int sta_id, > if (k->k_flags & IEEE80211_KEY_GROUP) { > cmd.common.key_offset = 1; > cmd.common.key_flags |= htole16(IWX_STA_KEY_MULTICAST); > - } else > + } else { > cmd.common.key_offset = 0; > + if (ni->ni_flags & IEEE80211_NODE_MFP) > + cmd.common.key_flags |= htole16(IWX_STA_KEY_MFP); > + } > > memcpy(cmd.common.key, k->k_key, MIN(sizeof(cmd.common.key), k->k_len)); > cmd.common.sta_id = sta_id; > @@ -8863,16 +8882,81 @@ iwx_add_sta_key_cmd(struct iwx_softc *sc, int sta_id, > } > > int > +iwx_set_sta_igtk(struct iwx_softc *sc, int sta_id, struct ieee80211_node *ni, > + struct ieee80211_key *k, int remove_key) > +{ > + struct iwx_mgmt_mcast_key_cmd igtk_cmd = {}; > + > + /* Verify key details match the required command's expectations. */ > + if (k == &ni->ni_pairwise_key || > + (k->k_id != 4 && k->k_id != 5 && > + k->k_id != 6 && k->k_id != 7) || > + /* TODO: Other ciphers for WPA3? */ > + k->k_cipher != IEEE80211_CIPHER_BIP) > + return EINVAL; > + > + if (!isset(sc->sc_enabled_capa, > + IWX_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT) && > + k->k_cipher != IEEE80211_CIPHER_BIP) > + return EINVAL; > + > + igtk_cmd.key_id = htole32(k->k_id); > + igtk_cmd.sta_id = htole32(sta_id); > + > + if (remove_key) { > + igtk_cmd.ctrl_flags |= htole32(IWX_STA_KEY_NOT_VALID); > + } else { > + switch (k->k_cipher) { > + case IEEE80211_CIPHER_BIP: > + igtk_cmd.ctrl_flags |= htole32(IWX_STA_KEY_FLG_CCM); > + break; > + /* TODO: Other ciphers for WPA3? */ > + default: > + return EINVAL; > + } > + > + memcpy(igtk_cmd.igtk, k->k_key, k->k_len); > + igtk_cmd.receive_seq_cnt = htole64(k->k_mgmt_rsc); > + } > + > + DPRINTF(("%s %sIGTK (%d) for sta %u\n", > + remove_key ? "removing" : "installing", > + k->k_id >= 6 ? "B" : "", k->k_id, sta_id)); > + > + if (!isset(sc->sc_enabled_capa, > + IWX_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT)) { > + struct iwx_mgmt_mcast_key_cmd_v1 igtk_cmd_v1 = { > + .ctrl_flags = igtk_cmd.ctrl_flags, > + .key_id = igtk_cmd.key_id, > + .sta_id = igtk_cmd.sta_id, > + .receive_seq_cnt = igtk_cmd.receive_seq_cnt > + }; > + > + memcpy(igtk_cmd_v1.igtk, igtk_cmd.igtk, > + sizeof(igtk_cmd_v1.igtk)); > + return iwx_send_cmd_pdu(sc, IWX_MGMT_MCAST_KEY, > + remove_key ? IWX_CMD_ASYNC : 0, > + sizeof(igtk_cmd_v1), &igtk_cmd_v1); > + } > + > + return iwx_send_cmd_pdu(sc, IWX_MGMT_MCAST_KEY, > + remove_key ? IWX_CMD_ASYNC : 0, sizeof(igtk_cmd), &igtk_cmd); > +} > + > +int > iwx_add_sta_key(struct iwx_softc *sc, int sta_id, struct ieee80211_node *ni, > struct ieee80211_key *k) > { > struct ieee80211com *ic = &sc->sc_ic; > struct iwx_node *in = (void *)ni; > - const int want_keymask = (IWX_NODE_FLAG_HAVE_PAIRWISE_KEY | > + int want_keymask = (IWX_NODE_FLAG_HAVE_PAIRWISE_KEY | > IWX_NODE_FLAG_HAVE_GROUP_KEY); > uint8_t sec_key_ver; > int err; > > + if (ni->ni_flags & IEEE80211_NODE_MFP) > + want_keymask |= IWX_NODE_FLAG_HAVE_INTEGRITY_GROUP_KEY; > + > /* > * Keys are stored in 'ni' so 'k' is valid if 'ni' is valid. > * Currently we only implement station mode where 'ni' is always > @@ -8883,13 +8967,18 @@ iwx_add_sta_key(struct iwx_softc *sc, int sta_id, stru > sec_key_ver = iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP, > IWX_SEC_KEY_CMD); > if (sec_key_ver != 0 && sec_key_ver != IWX_FW_CMD_VER_UNKNOWN) > - err = iwx_mld_add_sta_key_cmd(sc, sta_id, ni, k); > + err = iwx_mld_set_sta_key_cmd(sc, sta_id, ni, k, 0); > + else if (k->k_flags & IEEE80211_KEY_IGTK) > + err = iwx_set_sta_igtk(sc, sta_id, ni, k, 0); > else > err = iwx_add_sta_key_cmd(sc, sta_id, ni, k); > if (err) > return err; > > - if (k->k_flags & IEEE80211_KEY_GROUP) > + if (k->k_flags & IEEE80211_KEY_IGTK) { > + in->in_flags |= IWX_NODE_FLAG_HAVE_INTEGRITY_GROUP_KEY; > + ic->ic_igtk_kid = k->k_id; > + } else if (k->k_flags & IEEE80211_KEY_GROUP) > in->in_flags |= IWX_NODE_FLAG_HAVE_GROUP_KEY; > else > in->in_flags |= IWX_NODE_FLAG_HAVE_PAIRWISE_KEY; > @@ -8897,6 +8986,8 @@ iwx_add_sta_key(struct iwx_softc *sc, int sta_id, stru > if ((in->in_flags & want_keymask) == want_keymask) { > DPRINTF(("marking port %s valid\n", > ether_sprintf(ni->ni_macaddr))); > + if (ni->ni_flags & IEEE80211_NODE_MFP) > + ni->ni_flags |= IEEE80211_NODE_TXMGMTPROT; > ni->ni_port_valid = 1; > ieee80211_set_link_state(ic, LINK_STATE_UP); > } > @@ -8934,8 +9025,10 @@ iwx_delete_key(struct ieee80211com *ic, struct ieee802 > { > struct iwx_softc *sc = ic->ic_softc; > struct iwx_add_sta_key_cmd cmd; > + uint8_t sec_key_ver; > > - if (k->k_cipher != IEEE80211_CIPHER_CCMP) { > + if (k->k_cipher != IEEE80211_CIPHER_CCMP && > + (k->k_flags & IEEE80211_KEY_IGTK) == 0) { > /* Fallback to software crypto for other ciphers. */ > ieee80211_delete_key(ic, ni, k); > return; > @@ -8944,6 +9037,18 @@ iwx_delete_key(struct ieee80211com *ic, struct ieee802 > if ((sc->sc_flags & IWX_FLAG_STA_ACTIVE) == 0) > return; > > + sec_key_ver = iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP, > + IWX_SEC_KEY_CMD); > + if (sec_key_ver != 0 && sec_key_ver != IWX_FW_CMD_VER_UNKNOWN) { > + iwx_mld_set_sta_key_cmd(sc, IWX_STATION_ID, ni, k, 1); > + return; > + } > + > + if (k->k_flags & IEEE80211_KEY_IGTK) { > + iwx_set_sta_igtk(sc, IWX_STATION_ID, ni, k, 1); > + return; > + } > + > memset(&cmd, 0, sizeof(cmd)); > > cmd.common.key_flags = htole16(IWX_STA_KEY_NOT_VALID | > @@ -9656,6 +9761,49 @@ iwx_start(struct ifnet *ifp) > } > > void > +iwx_mfp_leave_done(struct ieee80211com *ic, struct ieee80211_node *ni) > +{ > + struct iwx_softc *sc = ic->ic_softc; > + struct ifnet *ifp = IC2IFP(&sc->sc_ic); > + > + if ((ifp->if_flags & IFF_RUNNING) && > + ic->ic_state == IEEE80211_S_RUN && > + (ni->ni_flags & IEEE80211_NODE_MFP)) { > + sc->deauth_sent = 1; > + wakeup(&sc->deauth_sent); > + } > +} > + > +void > +iwx_mfp_leave(struct iwx_softc *sc) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + struct ieee80211_node *ni = (void *)ic->ic_bss; > + > + ic->ic_xflags |= IEEE80211_F_TX_MGMT_ONLY; > + sc->deauth_sent = 0; > + > + ni->ni_unref_cb = iwx_mfp_leave_done; > + ni->ni_unref_arg = NULL; > + ni->ni_unref_arg_size = 0; > + > + /* > + * Send an authenticated deauth frame in order to let our AP know we > + * are leaving. This allows our AP to tear down MFP state cleanly. > + * Otherwise we would remain locked out of this AP until a timeout > + * of stale MFP state occurs at the AP, which might take a while. > + */ > + if (IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, > + IEEE80211_REASON_AUTH_LEAVE) != 0) { > + ni->ni_unref_cb = NULL; > + return; > + } > + > + if (tsleep_nsec(&sc->deauth_sent, 0, "iwxlv", MSEC_TO_NSEC(500)) != 0) > + ni->ni_unref_cb = NULL; > +} > + > +void > iwx_stop(struct ifnet *ifp) > { > struct iwx_softc *sc = ifp->if_softc; > @@ -9680,6 +9828,11 @@ iwx_stop(struct ifnet *ifp) > KASSERT(sc->task_refs.r_refs >= 1); > refcnt_finalize(&sc->task_refs, "iwxstop"); > > + if (ic->ic_opmode == IEEE80211_M_STA && > + ic->ic_state == IEEE80211_S_RUN && > + (ic->ic_bss->ni_flags & IEEE80211_NODE_MFP)) > + iwx_mfp_leave(sc); > + > iwx_stop_device(sc); > > free(sc->bgscan_unref_arg, M_DEVBUF, sc->bgscan_unref_arg_size); > @@ -10348,6 +10501,7 @@ iwx_rx_pkt(struct iwx_softc *sc, struct iwx_rx_data *d > case IWX_WIDE_ID(IWX_REGULATORY_AND_NVM_GROUP, > IWX_NVM_GET_INFO): > case IWX_ADD_STA_KEY: > + case IWX_MGMT_MCAST_KEY: > case IWX_PHY_CONFIGURATION_CMD: > case IWX_TX_ANT_CONFIGURATION_CMD: > case IWX_ADD_STA: > @@ -11688,7 +11842,8 @@ iwx_attach(struct device *parent, struct device *self, > IEEE80211_C_SCANALLBAND | /* device scans all bands at once */ > IEEE80211_C_MONITOR | /* monitor mode supported */ > IEEE80211_C_SHSLOT | /* short slot time supported */ > - IEEE80211_C_SHPREAMBLE; /* short preamble supported */ > + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ > + IEEE80211_C_MFP; /* management frame protection */ > > ic->ic_htcaps = IEEE80211_HTCAP_SGI20 | IEEE80211_HTCAP_SGI40; > ic->ic_htcaps |= IEEE80211_HTCAP_CBW20_40; > blob - 9fefe2bf161c6ed371bbf80317a011cbe1f6ab20 > blob + 0eaa20b91b3fac87e6f3e135f0cea043dcead8df > --- sys/dev/pci/if_iwxreg.h > +++ sys/dev/pci/if_iwxreg.h > @@ -8040,16 +8040,34 @@ struct iwx_rm_sta_cmd { > * @key_id: > * @receive_seq_cnt: initial RSC/PN needed for replay check > */ > -struct iwx_mgmt_mcast_key_cmd { > +struct iwx_mgmt_mcast_key_cmd_v1 { > uint32_t ctrl_flags; > - uint8_t IGTK[16]; > - uint8_t K1[16]; > - uint8_t K2[16]; > + uint8_t igtk[16]; > + uint8_t k1[16]; > + uint8_t k2[16]; > uint32_t key_id; > uint32_t sta_id; > uint64_t receive_seq_cnt; > } __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */ > > +/** > + * struct iwx_mgmt_mcast_key_cmd - IGTK command > + * ( MGMT_MCAST_KEY = 0x1f ) > + * @ctrl_flags: &enum iwx_sta_key_flag > + * @igtk: IGTK master key > + * @sta_id: station ID that support IGTK > + * @key_id: key ID > + * @receive_seq_cnt: initial RSC/PN needed for replay check > + */ > +struct iwx_mgmt_mcast_key_cmd { > + uint32_t ctrl_flags; > + uint8_t igtk[32]; > + uint32_t key_id; > + uint32_t sta_id; > + uint64_t receive_seq_cnt; > +} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_2 */ > + > + > struct iwx_wep_key { > uint8_t key_index; > uint8_t key_offset; > blob - e82e5bf0fab076d18d1cb8399d9cb15e5bc9e703 > blob + f2b8b479a17f1feeccaae2fa7917247e6e2ecc77 > --- sys/dev/pci/if_iwxvar.h > +++ sys/dev/pci/if_iwxvar.h > @@ -652,6 +652,8 @@ struct iwx_softc { > enum ieee80211_state ns_nstate; > int ns_arg; > > + int deauth_sent; > + > /* Task for firmware BlockAck setup/teardown and its arguments. */ > struct task ba_task; > struct iwx_ba_task_data ba_rx; > @@ -660,13 +662,13 @@ struct iwx_softc { > /* 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. > + * At present we need to process at most three keys at once: > + * Our pairwise key, a group key, and an integrity 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]; > + struct iwx_setkey_task_arg setkey_arg[3]; > int setkey_cur; > int setkey_tail; > int setkey_nkeys; > @@ -843,9 +845,11 @@ struct iwx_node { > struct iwx_rxq_dup_data dup_data; > > int in_flags; > -#define IWX_NODE_FLAG_HAVE_PAIRWISE_KEY 0x01 > -#define IWX_NODE_FLAG_HAVE_GROUP_KEY 0x02 > +#define IWX_NODE_FLAG_HAVE_PAIRWISE_KEY 0x01 > +#define IWX_NODE_FLAG_HAVE_GROUP_KEY 0x02 > +#define IWX_NODE_FLAG_HAVE_INTEGRITY_GROUP_KEY 0x04 > }; > + > #define IWX_STATION_ID 0 > #define IWX_AUX_STA_ID 1 > #define IWX_MONITOR_STA_ID 2 > blob - a6aad7988908629238321cc6d0ce8380b37108a3 > blob + 4389af23dbd213e9cca3d237a22f11cdbd669095 > --- sys/dev/pci/if_qwx_pci.c > +++ sys/dev/pci/if_qwx_pci.c > @@ -1084,7 +1084,8 @@ unsupported_wcn6855_soc: > IEEE80211_C_MONITOR | /* monitor mode supported */ > #endif > IEEE80211_C_SHSLOT | /* short slot time supported */ > - IEEE80211_C_SHPREAMBLE; /* short preamble supported */ > + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ > + IEEE80211_C_MFP; /* management frame protection */ > > ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a; > ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; > blob - 8873924c2dbd2e7b8d122568425bbefd8d012d7c > blob + 08628c88b0cb9505f9e3ca32e8d88f62724d1010 > --- sys/net80211/ieee80211_crypto.c > +++ sys/net80211/ieee80211_crypto.c > @@ -386,8 +386,6 @@ ieee80211_derive_ptk(enum ieee80211_akm akm, const u_i > const u_int8_t *aa, const u_int8_t *spa, const u_int8_t *anonce, > const u_int8_t *snonce, struct ieee80211_ptk *ptk) > { > - void (*kdf)(const u_int8_t *, size_t, const u_int8_t *, size_t, > - const u_int8_t *, size_t, u_int8_t *, size_t); > u_int8_t buf[2 * IEEE80211_ADDR_LEN + 2 * EAPOL_KEY_NONCE_LEN]; > int ret; > > @@ -401,9 +399,17 @@ ieee80211_derive_ptk(enum ieee80211_akm akm, const u_i > memcpy(&buf[12], ret ? anonce : snonce, EAPOL_KEY_NONCE_LEN); > memcpy(&buf[44], ret ? snonce : anonce, EAPOL_KEY_NONCE_LEN); > > - kdf = ieee80211_is_sha256_akm(akm) ? ieee80211_kdf : ieee80211_prf; > - (*kdf)(pmk, IEEE80211_PMK_LEN, "Pairwise key expansion", 23, > - buf, sizeof buf, (u_int8_t *)ptk, sizeof(*ptk)); > + if (ieee80211_is_sha256_akm(akm)) { > + ieee80211_kdf(pmk, IEEE80211_PMK_LEN, "Pairwise key expansion", > + 22 /* KDF omits \0 */, buf, sizeof buf, (u_int8_t *)ptk, > + /* expected output size of 48 is mixed into hash */ > + MIN(48, sizeof(*ptk))); > + CTASSERT(sizeof(struct ieee80211_ptk) >= 48); > + } else { > + ieee80211_prf(pmk, IEEE80211_PMK_LEN, "Pairwise key expansion", > + 23 /* PRF uses \0 */, buf, sizeof buf, (u_int8_t *)ptk, > + sizeof(*ptk)); > + } > } > > static void > blob - 85825651f470812045f2a1ef79ce5d743485f892 > blob + 50278acd28854cf7e3e20fe94ecb3874adf5a0a9 > --- sys/net80211/ieee80211_input.c > +++ sys/net80211/ieee80211_input.c > @@ -637,26 +637,39 @@ ieee80211_inputm(struct ifnet *ifp, struct mbuf *m, st > } > > if (ni->ni_flags & IEEE80211_NODE_RXMGMTPROT) { > + int is_multicast, is_protected; > + > + is_multicast = IEEE80211_IS_MULTICAST(wh->i_addr1); > + is_protected = (wh->i_fc[1] & IEEE80211_FC1_PROTECTED); > + > /* MMPDU protection is on for Rx */ > if (subtype == IEEE80211_FC0_SUBTYPE_DISASSOC || > subtype == IEEE80211_FC0_SUBTYPE_DEAUTH || > subtype == IEEE80211_FC0_SUBTYPE_ACTION) { > - if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && > - !(wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) { > + if (rxi->rxi_flags & IEEE80211_RXI_HWDEC) { > + m = ieee80211_input_hwdecrypt(ic, ni, > + m, rxi); > + if (m == NULL) > + goto out; > + } else if (!is_multicast && !is_protected) { > /* unicast mgmt not encrypted */ > + ic->ic_stats.is_rx_unencrypted++; > goto out; > + } else { > + /* do software decryption */ > + m = ieee80211_decrypt(ic, m, ni); > + if (m == NULL) { > + ic->ic_stats.is_rx_wepfail++; > + goto out; > + } > } > - /* do software decryption */ > - m = ieee80211_decrypt(ic, m, ni); > - if (m == NULL) { > - /* XXX stats */ > - goto out; > - } > wh = mtod(m, struct ieee80211_frame *); > } > } else if ((ic->ic_flags & IEEE80211_F_RSNON) && > - (wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) { > + ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) || > + (rxi->rxi_flags & IEEE80211_RXI_HWDEC))) { > /* encrypted but MMPDU Rx protection off for TA */ > + ic->ic_stats.is_rx_nowep++; > goto out; > } > > blob - 07eda83a1077caebda97a8f7f8cbfe87eaf37328 > blob + 817cd7cd85a125caa127781c667ede83da328c63 > --- sys/net80211/ieee80211_ioctl.c > +++ sys/net80211/ieee80211_ioctl.c > @@ -200,6 +200,7 @@ void > ieee80211_disable_rsn(struct ieee80211com *ic) > { > ic->ic_flags &= ~(IEEE80211_F_PSK | IEEE80211_F_RSNON); > + ic->ic_flags &= ~IEEE80211_F_MFPR; > explicit_bzero(ic->ic_psk, sizeof(ic->ic_psk)); > ic->ic_rsnprotos = 0; > ic->ic_rsnakms = 0; > blob - 1511a34bbbdda3e021a8fa171ab2b2a0ebe7e0f3 > blob + 3f7e20bc8aafccf3af63d153f971646a0b5de213 > --- sys/net80211/ieee80211_node.c > +++ sys/net80211/ieee80211_node.c > @@ -2817,6 +2817,8 @@ ieee80211_node_join_rsn(struct ieee80211com *ic, struc > ni->ni_key_count = 0; > ni->ni_port_valid = 0; > ni->ni_flags &= ~IEEE80211_NODE_TXRXPROT; > + ni->ni_flags &= ~IEEE80211_NODE_RXMGMTPROT; > + ni->ni_flags &= ~IEEE80211_NODE_TXMGMTPROT; > ni->ni_flags &= ~IEEE80211_NODE_RSN_NEW_PTK; > ni->ni_replaycnt = -1; /* XXX */ > ni->ni_rsn_retries = 0; > @@ -3055,6 +3057,9 @@ ieee80211_node_leave_rsn(struct ieee80211com *ic, stru > > ni->ni_rsn_retries = 0; > ni->ni_flags &= ~IEEE80211_NODE_TXRXPROT; > + ni->ni_flags &= ~IEEE80211_NODE_RXMGMTPROT; > + ni->ni_flags &= ~IEEE80211_NODE_TXMGMTPROT; > + ni->ni_flags &= ~IEEE80211_NODE_RSN_NEW_PTK; > ni->ni_port_valid = 0; > (*ic->ic_delete_key)(ic, ni, &ni->ni_pairwise_key); > } > blob - 84dc77f23a3abfda7c4771ec9816695ac1b7e4b1 > blob + 54592092069f6dcc942deffe9b263665f8b4c61e > --- sys/net80211/ieee80211_output.c > +++ sys/net80211/ieee80211_output.c > @@ -208,7 +208,7 @@ ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80 > IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); > > /* check if protection is required for this mgmt frame */ > - if ((ic->ic_caps & IEEE80211_C_MFP) && > + if ((ni->ni_flags & IEEE80211_NODE_MFP) && > (type == IEEE80211_FC0_SUBTYPE_DISASSOC || > type == IEEE80211_FC0_SUBTYPE_DEAUTH || > type == IEEE80211_FC0_SUBTYPE_ACTION)) { > blob - daf550f591fdf675e073f82a968efbdbb49fdb46 > blob + ecdcb664e41815b81d32ab9d8be431b1319f4267 > --- sys/net80211/ieee80211_pae_input.c > +++ sys/net80211/ieee80211_pae_input.c > @@ -644,6 +644,8 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic, > /* install the IGTK */ > switch ((*ic->ic_set_key)(ic, ni, k)) { > case 0: > + ni->ni_flags |= IEEE80211_NODE_TXMGMTPROT; > + ic->ic_igtk_kid = kid; > break; > case EBUSY: > deferlink = 1; > @@ -652,6 +654,8 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic, > reason = IEEE80211_REASON_AUTH_LEAVE; > goto deauth; > } > + > + ni->ni_flags |= IEEE80211_NODE_RXMGMTPROT; > } > } > if (info & EAPOL_KEY_INSTALL) > @@ -932,12 +936,17 @@ ieee80211_recv_rsn_group_msg1(struct ieee80211com *ic, > /* install the IGTK */ > switch ((*ic->ic_set_key)(ic, ni, k)) { > case 0: > + ni->ni_flags |= IEEE80211_NODE_TXMGMTPROT; > + ic->ic_igtk_kid = kid; > + break; > case EBUSY: > break; > default: > reason = IEEE80211_REASON_AUTH_LEAVE; > goto deauth; > } > + > + ni->ni_flags |= IEEE80211_NODE_RXMGMTPROT; > } > } > if (info & EAPOL_KEY_SECURE) { > blob - 81fe56bdd53d0487bd5f2b0c69bfe588690e6caf > blob + 96b3a2e971f10089a61815886c9f5cb8a9d99544 > --- sys/net80211/ieee80211_proto.c > +++ sys/net80211/ieee80211_proto.c > @@ -948,6 +948,8 @@ ieee80211_auth_open(struct ieee80211com *ic, const str > if (ic->ic_flags & IEEE80211_F_RSNON) { > /* XXX not here! */ > ic->ic_bss->ni_flags &= ~IEEE80211_NODE_TXRXPROT; > + ic->ic_bss->ni_flags &= ~IEEE80211_NODE_RXMGMTPROT; > + ic->ic_bss->ni_flags &= ~IEEE80211_NODE_TXMGMTPROT; > ic->ic_bss->ni_port_valid = 0; > ic->ic_bss->ni_replaycnt_ok = 0; > (*ic->ic_delete_key)(ic, ic->ic_bss, >