From: Carella Chen Drivers can offload FT or OKC roaming without necessarily advertising full 802.1X 4-way handshake offload. Add an extended feature bit for that capability and allow PMK configuration when it is present. This gives userspace a way to provide PMK or PMK-R0 material needed by fast roaming firmware implementations while keeping the existing 4-way handshake offload capability unchanged. Assisted-by: GitHub-Copilot-CLI:gpt-5.5 Signed-off-by: Carella Chen Signed-off-by: Jason Huang --- include/uapi/linux/nl80211.h | 5 +++++ net/wireless/nl80211.c | 10 ++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 020387d76412..920d5e2d9966 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -7110,6 +7110,10 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_PROBE_AP: Driver supports probing the associated AP * in STA mode using @NL80211_CMD_PROBE_PEER. * + * @NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD: Driver supports fast roaming + * offload in station mode, including Fast Transition or Opportunistic + * Key Caching. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -7192,6 +7196,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_ROC_ADDR_FILTER, NL80211_EXT_FEATURE_SET_KEY_LTF_SEED, NL80211_EXT_FEATURE_PROBE_AP, + NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 44f2bad08670..e4850129a582 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -18434,6 +18434,7 @@ static int nl80211_set_multicast_to_unicast(struct sk_buff *skb, static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wiphy *wiphy = &rdev->wiphy; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_pmk_conf pmk_conf = {}; @@ -18442,7 +18443,9 @@ static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - if (!wiphy_ext_feature_isset(&rdev->wiphy, + if (!wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD) && + !wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) return -EOPNOTSUPP; @@ -18472,6 +18475,7 @@ static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wiphy *wiphy = &rdev->wiphy; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; const u8 *aa; @@ -18480,7 +18484,9 @@ static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info) wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - if (!wiphy_ext_feature_isset(&rdev->wiphy, + if (!wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD) && + !wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) return -EOPNOTSUPP; -- 2.25.1 From: Carella Chen Some firmware advertises Fast BSS Transition and Opportunistic Key Caching support through the firmware capability string. Track those capabilities so later roaming offload handling can be enabled only when firmware reports support. Assisted-by: GitHub-Copilot-CLI:gpt-5.5 Signed-off-by: Carella Chen Signed-off-by: Jason Huang --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c | 2 ++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index 488364ef8ff2..cc19e5e7ebf5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -45,6 +45,8 @@ static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = { { BRCMF_FEAT_SAE, "sae " }, { BRCMF_FEAT_FWAUTH, "idauth" }, { BRCMF_FEAT_SAE_EXT, "sae_ext" }, + { BRCMF_FEAT_FBT, "fbt " }, + { BRCMF_FEAT_OKC, "okc" }, }; #ifdef DEBUG diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index 31f8695ca417..8165a286b630 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -59,7 +59,9 @@ BRCMF_FEAT_DEF(SCAN_V2) \ BRCMF_FEAT_DEF(PMKID_V2) \ BRCMF_FEAT_DEF(PMKID_V3) \ - BRCMF_FEAT_DEF(SAE_EXT) + BRCMF_FEAT_DEF(SAE_EXT) \ + BRCMF_FEAT_DEF(FBT) \ + BRCMF_FEAT_DEF(OKC) /* * Quirks: -- 2.25.1 From: Darren Li FT and OKC roaming offload need PMK material in firmware even when the initial 802.1X exchange is handled by userspace. Add a roaming firmware-supplicant profile state for those connections and allow PMK configuration while firmware FT or OKC state is active. Program the OKC PMK through the okc_info_pmk iovar before setting the regular PMK when firmware reports OKC state. Only select the roaming firmware-supplicant state when firmware advertises FBT or OKC support; devices without those capabilities keep the profile in the userspace supplicant state and do not issue roaming-offload specific iovars on every connect. For FT-PSK with firmware supplicant support, keep the profile in the PSK firmware-supplicant state. Assisted-by: GitHub-Copilot-CLI:gpt-5.5 Signed-off-by: Darren Li Signed-off-by: Chung-Hsien Hsu Signed-off-by: Chi-hsien Lin Co-developed-by: Carella Chen Signed-off-by: Carella Chen Co-developed-by: Shelley Yang Signed-off-by: Shelley Yang Signed-off-by: Jason Huang --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 56 +++++++++++++++++-- .../broadcom/brcm80211/brcmfmac/cfg80211.h | 5 +- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 872c48806d09..f2916cc9e5fc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -2112,9 +2112,11 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) struct brcmf_pub *drvr = ifp->drvr; s32 val; s32 err; + s32 okc_enable; const struct brcmf_tlv *rsn_ie; const u8 *ie; u32 ie_len; + bool fwsup_roam; u32 offset; u16 rsn_cap; u32 mfp; @@ -2122,6 +2124,9 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE; profile->is_ft = false; + profile->is_okc = false; + fwsup_roam = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FBT) || + brcmf_feat_is_enabled(ifp, BRCMF_FEAT_OKC); if (!sme->crypto.n_akm_suites) return 0; @@ -2138,6 +2143,8 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) val = WPA_AUTH_UNSPECIFIED; if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else if (fwsup_roam) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_PSK: val = WPA_AUTH_PSK; @@ -2153,11 +2160,15 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) val = WPA2_AUTH_UNSPECIFIED; if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else if (fwsup_roam) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_8021X_SHA256: val = WPA2_AUTH_1X_SHA256; if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else if (fwsup_roam) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_PSK_SHA256: val = WPA2_AUTH_PSK_SHA256; @@ -2170,10 +2181,16 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) profile->is_ft = true; if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else if (fwsup_roam) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_FT_PSK: val = WPA2_AUTH_PSK | WPA2_AUTH_FT; profile->is_ft = true; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; + else if (fwsup_roam) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_WFA_DPP: val = WFA_AUTH_DPP; @@ -2204,8 +2221,22 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X) brcmf_dbg(INFO, "using 1X offload\n"); + + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X || + profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM) { + err = brcmf_fil_bsscfg_int_get(ifp, "okc_enable", + &okc_enable); + if (err) { + bphy_err(drvr, "get okc_enable failed (%d)\n", err); + } else { + brcmf_dbg(INFO, "okc_enable (%d)\n", okc_enable); + profile->is_okc = okc_enable; + } + } if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) brcmf_dbg(INFO, "using SAE offload\n"); + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM) + brcmf_dbg(INFO, "using roaming offload\n"); if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) goto skip_mfp_config; @@ -2494,13 +2525,14 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, if (sme->crypto.psk && !is_sae_akm && profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) { - if (WARN_ON(profile->use_fwsup != - BRCMF_PROFILE_FWSUP_NONE)) { + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_NONE) { + brcmf_dbg(INFO, "using PSK offload\n"); + profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; + } else if (WARN_ON(profile->use_fwsup != + BRCMF_PROFILE_FWSUP_PSK)) { err = -EINVAL; goto done; } - brcmf_dbg(INFO, "using PSK offload\n"); - profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; } if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { /* enable firmware supplicant for this interface */ @@ -5953,17 +5985,29 @@ static int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev, const struct cfg80211_pmk_conf *conf) { struct brcmf_if *ifp; + struct brcmf_pub *drvr; + int ret; brcmf_dbg(TRACE, "enter\n"); - /* expect using firmware supplicant for 1X */ ifp = netdev_priv(dev); - if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X)) + drvr = ifp->drvr; + if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X && + !ifp->vif->profile.is_ft && + !ifp->vif->profile.is_okc)) return -EINVAL; if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN) return -ERANGE; + if (ifp->vif->profile.is_okc) { + ret = brcmf_fil_iovar_data_set(ifp, "okc_info_pmk", + conf->pmk, conf->pmk_len); + if (ret < 0) + bphy_err(drvr, "okc_info_pmk iovar failed: ret=%d\n", + ret); + } + return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 63e534523f51..1aa99390a951 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -127,7 +127,8 @@ enum brcmf_profile_fwsup { BRCMF_PROFILE_FWSUP_NONE, BRCMF_PROFILE_FWSUP_PSK, BRCMF_PROFILE_FWSUP_1X, - BRCMF_PROFILE_FWSUP_SAE + BRCMF_PROFILE_FWSUP_SAE, + BRCMF_PROFILE_FWSUP_ROAM }; /** @@ -164,6 +165,7 @@ enum brcmf_mgmt_tx_status { * @bssid: bssid of joined/joining ibss. * @sec: security information. * @key: key information + * @is_okc: OKC is used for current connection. */ struct brcmf_cfg80211_profile { u8 bssid[ETH_ALEN]; @@ -172,6 +174,7 @@ struct brcmf_cfg80211_profile { enum brcmf_profile_fwsup use_fwsup; u16 use_fwauth; bool is_ft; + bool is_okc; }; /** -- 2.25.1 From: Darren Li Firmware can complete FT or OKC roaming before the driver reports the connect or roam event to cfg80211. Detect those successful offloaded cases when the profile uses firmware 1X or roaming offload and the association request carries PMK cache state, FT is in use, or OKC is enabled. After reporting the connect or roam event, call cfg80211_port_authorized() so nl80211 emits the dedicated NL80211_CMD_PORT_AUTHORIZED event instead of reusing the reserved NL80211_ATTR_PORT_AUTHORIZED flag in CONNECT/ROAM notifications. Assisted-by: GitHub-Copilot-CLI:gpt-5.5 Signed-off-by: Darren Li Signed-off-by: Chung-Hsien Hsu Signed-off-by: Chi-hsien Lin Signed-off-by: Carella Chen Signed-off-by: Jason Huang --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 65 +++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index f2916cc9e5fc..c11b3eac9d47 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6466,6 +6466,47 @@ static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg, return err; } +static bool brcmf_has_pmkid(const u8 *parse, u32 len) +{ + const struct brcmf_tlv *rsn_ie; + const u8 *ie; + u32 ie_len; + u32 offset; + u16 count; + + if (!parse) + return false; + + rsn_ie = brcmf_parse_tlvs(parse, len, WLAN_EID_RSN); + if (!rsn_ie) + return false; + + ie = (const u8 *)rsn_ie; + ie_len = rsn_ie->len + TLV_HDR_LEN; + + offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN; + if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) + return false; + + count = ie[offset] + (ie[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN + count * WPA_IE_MIN_OUI_LEN; + if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) + return false; + + count = ie[offset] + (ie[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN + count * WPA_IE_MIN_OUI_LEN; + if (offset + RSN_CAP_LEN >= ie_len) + return false; + + offset += RSN_CAP_LEN; + if (offset + RSN_PMKID_COUNT_LEN > ie_len) + return false; + + count = ie[offset] + (ie[offset + 1] << 8); + + return count > 0; +} + static s32 brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, @@ -6480,6 +6521,7 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, struct brcmf_bss_info_le *bi; struct brcmu_chan ch; struct cfg80211_roam_info roam_info = {}; + bool authorized = false; u32 freq; s32 err = 0; u8 *buf; @@ -6526,14 +6568,18 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, roam_info.resp_ie = conn_info->resp_ie; roam_info.resp_ie_len = conn_info->resp_ie_len; + if ((profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X || + profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM) && + (brcmf_has_pmkid(roam_info.req_ie, roam_info.req_ie_len) || + profile->is_ft || profile->is_okc)) + authorized = true; + cfg80211_roamed(ndev, &roam_info, GFP_KERNEL); + if (authorized) + cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, + GFP_KERNEL); brcmf_dbg(CONN, "Report roaming result\n"); - if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && profile->is_ft) { - cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, GFP_KERNEL); - brcmf_dbg(CONN, "Report port authorized\n"); - } - set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); brcmf_dbg(TRACE, "Exit\n"); return err; @@ -6548,6 +6594,7 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); struct cfg80211_connect_resp_params conn_params; + bool authorized; brcmf_dbg(TRACE, "Enter\n"); @@ -6572,7 +6619,15 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, conn_params.req_ie_len = conn_info->req_ie_len; conn_params.resp_ie = conn_info->resp_ie; conn_params.resp_ie_len = conn_info->resp_ie_len; + authorized = completed && + (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X || + profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM) && + brcmf_has_pmkid(conn_params.req_ie, + conn_params.req_ie_len); cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL); + if (authorized) + cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, + GFP_KERNEL); brcmf_dbg(CONN, "Report connect result - connection %s\n", completed ? "succeeded" : "failed"); } -- 2.25.1 From: Carella Chen Advertise NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD only when firmware reports FBT or OKC support. That lets userspace provide PMK material for firmware fast roaming without enabling the path on devices that cannot complete the offloaded roam. Assisted-by: GitHub-Copilot-CLI:gpt-5.5 Signed-off-by: Carella Chen Signed-off-by: Jason Huang --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index c11b3eac9d47..519de96b042e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -7830,6 +7830,10 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) } if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE_EXT)) wiphy->features |= NL80211_FEATURE_SAE; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FBT) || + brcmf_feat_is_enabled(ifp, BRCMF_FEAT_OKC)) + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD); wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) { -- 2.25.1