Download raw body.
wifi protected management frame (PMF) support
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,
>
wifi protected management frame (PMF) support