* [PATCH wireless-next v3 3/7] wifi: cfg80211: add Control Integrity Protocol (CIP) APIs
2026-09-21 12:40 [PATCH wireless-next v3 0/7] Add support for Control Integrity Protocol (CIP) Benjamin Berg
2026-09-21 12:40 ` [PATCH wireless-next v3 1/7] wifi: mac80211: add key flags to debugfs Benjamin Berg
2026-09-21 12:40 ` [PATCH wireless-next v3 2/7] wifi: mac80211: add key link_id " Benjamin Berg
@ 2026-09-21 12:40 ` Benjamin Berg
2026-09-21 12:40 ` [PATCH wireless-next v3 4/7] wifi: mac80211: add cigtk parameter to ieee80211_gtk_rekey_add Benjamin Berg
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Benjamin Berg @ 2026-09-21 12:40 UTC (permalink / raw)
To: linux-wireless
Cc: Arend van Spriel, Brian Norris, Francesco Dolcini, Ajay Singh,
Claudiu Beznea, Jeff Chen, Igor Mitsyanko, Sergey Matyukevich,
Greg Kroah-Hartman, brcm80211, libertas-dev, Miri Korenblit,
Ping-Ke Shih, Benjamin Berg
From: Benjamin Berg <benjamin.berg@intel.com>
The Control Integrity Protocol consists of a set of capabilities for the
MIC padding as well as a new CIGTK that can be installed. The CIGTK uses
a fixed GMAC-256 cipher for which no RSN extension is defined as it is
bound to the pairwise cipher being GCMP-256. The CIGTK uses the key
index 0 and 1, making it necessary to add a new key type for it.
Add a new CIP feature flag and attributes for CIP Capabilities and
enabling the feature for stations and the association.
Also extend the cfg80211 API to pass the key type rather than a single
pairwise boolean.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
v2:
- Also adjust rtl8723bs staging driver
- Fix incorrect position of new nl80211 attributes
---
drivers/net/wireless/ath/ath6kl/cfg80211.c | 10 +-
drivers/net/wireless/ath/wil6210/cfg80211.c | 13 +-
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 11 +-
drivers/net/wireless/marvell/libertas/cfg.c | 6 +-
.../net/wireless/marvell/mwifiex/cfg80211.c | 12 +-
.../wireless/microchip/wilc1000/cfg80211.c | 13 +-
drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 11 +-
.../net/wireless/quantenna/qtnfmac/cfg80211.c | 8 +-
.../staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 9 +-
include/net/cfg80211.h | 19 ++-
include/uapi/linux/nl80211.h | 14 ++
net/mac80211/cfg.c | 19 ++-
net/wireless/core.h | 7 +-
net/wireless/ibss.c | 3 +-
net/wireless/nl80211.c | 124 ++++++++++++++----
net/wireless/rdev-ops.h | 23 ++--
net/wireless/sme.c | 10 +-
net/wireless/trace.h | 37 +++---
net/wireless/util.c | 40 +++++-
net/wireless/wext-compat.c | 8 +-
20 files changed, 295 insertions(+), 102 deletions(-)
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 8bbe554a9b36..a9a35476fecd 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -1130,13 +1130,15 @@ void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
}
static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr,
struct key_params *params)
{
struct ath6kl *ar = ath6kl_priv(wdev->netdev);
struct ath6kl_vif *vif = netdev_priv(wdev->netdev);
struct ath6kl_key *key = NULL;
+ bool pairwise = type == NL80211_KEYTYPE_PAIRWISE;
int seq_len;
u8 key_usage;
u8 key_type;
@@ -1255,7 +1257,8 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct wireless_dev *wde
}
static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr)
{
struct ath6kl *ar = ath6kl_priv(wdev->netdev);
@@ -1285,7 +1288,8 @@ static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct wireless_dev *wde
}
static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr, void *cookie,
void (*callback) (void *cookie,
struct key_params *))
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 9ac33d827f80..bf060281c9a2 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -1620,11 +1620,13 @@ static void wil_del_rx_key(u8 key_index, enum wmi_key_usage key_usage,
static int wil_cfg80211_add_key(struct wiphy *wiphy,
struct wireless_dev *wdev, int link_id,
- u8 key_index, bool pairwise,
+ u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr,
struct key_params *params)
{
int rc;
+ bool pairwise = type == NL80211_KEYTYPE_PAIRWISE;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
@@ -1695,12 +1697,14 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
static int wil_cfg80211_del_key(struct wiphy *wiphy,
struct wireless_dev *wdev, int link_id,
- u8 key_index, bool pairwise,
+ u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
- enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
+ enum wmi_key_usage key_usage =
+ wil_detect_key_usage(wdev, type == NL80211_KEYTYPE_PAIRWISE);
struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
key_usage,
mac_addr);
@@ -2071,7 +2075,8 @@ void wil_cfg80211_ap_recovery(struct wil6210_priv *wil)
key_params.seq_len = IEEE80211_GCMP_PN_LEN;
rc = wil_cfg80211_add_key(wiphy, vif_to_wdev(vif), -1,
vif->gtk_index,
- false, NULL, &key_params);
+ NL80211_KEYTYPE_PAIRWISE,
+ NULL, &key_params);
if (rc)
wil_err(wil, "vif %d recovery add key failed (%d)\n",
i, rc);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 9d8ce7bb046e..ede76a9e12c8 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -2802,7 +2802,8 @@ brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
static s32
brcmf_cfg80211_del_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_idx, bool pairwise,
+ int link_id, u8 key_idx,
+ enum nl80211_key_type type,
const u8 *mac_addr)
{
struct brcmf_if *ifp = netdev_priv(wdev->netdev);
@@ -2840,7 +2841,8 @@ brcmf_cfg80211_del_key(struct wiphy *wiphy, struct wireless_dev *wdev,
static s32
brcmf_cfg80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_idx, bool pairwise,
+ int link_id, u8 key_idx,
+ enum nl80211_key_type type,
const u8 *mac_addr, struct key_params *params)
{
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
@@ -2866,7 +2868,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
if (params->key_len == 0)
return brcmf_cfg80211_del_key(wiphy, wdev, -1, key_idx,
- pairwise, mac_addr);
+ type, mac_addr);
if (params->key_len > sizeof(key->data)) {
bphy_err(drvr, "Too long key length (%u)\n", params->key_len);
@@ -2962,7 +2964,8 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
static s32
brcmf_cfg80211_get_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_idx, bool pairwise,
+ int link_id, u8 key_idx,
+ enum nl80211_key_type type,
const u8 *mac_addr, void *cookie,
void (*callback)(void *cookie,
struct key_params *params))
diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c
index 72c92f72469d..e015cfa1950c 100644
--- a/drivers/net/wireless/marvell/libertas/cfg.c
+++ b/drivers/net/wireless/marvell/libertas/cfg.c
@@ -1508,7 +1508,8 @@ static int lbs_cfg_set_default_key(struct wiphy *wiphy,
static int lbs_cfg_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 idx, bool pairwise,
+ int link_id, u8 idx,
+ enum nl80211_key_type type,
const u8 *mac_addr, struct key_params *params)
{
struct lbs_private *priv = wiphy_priv(wiphy);
@@ -1569,7 +1570,8 @@ static int lbs_cfg_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
static int lbs_cfg_del_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr)
{
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index 293876397b67..1db1017ef76a 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -142,12 +142,14 @@ static void *mwifiex_cfg80211_get_adapter(struct wiphy *wiphy)
*/
static int
mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
static const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- const u8 *peer_mac = pairwise ? mac_addr : bc_mac;
+ const u8 *peer_mac =
+ (type == NL80211_KEYTYPE_PAIRWISE) ? mac_addr : bc_mac;
if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1)) {
mwifiex_dbg(priv->adapter, ERROR, "deleting the crypto keys\n");
@@ -480,13 +482,15 @@ mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
*/
static int
mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr, struct key_params *params)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
struct mwifiex_wep_key *wep_key;
static const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- const u8 *peer_mac = pairwise ? mac_addr : bc_mac;
+ const u8 *peer_mac =
+ (type == NL80211_KEYTYPE_PAIRWISE) ? mac_addr : bc_mac;
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP &&
(params->cipher == WLAN_CIPHER_SUITE_WEP40 ||
diff --git a/drivers/net/wireless/microchip/wilc1000/cfg80211.c b/drivers/net/wireless/microchip/wilc1000/cfg80211.c
index bb2748a19329..651a527f6881 100644
--- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c
+++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c
@@ -535,10 +535,13 @@ static int wilc_wfi_cfg_copy_wpa_info(struct wilc_wfi_key *key_info,
}
static int add_key(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id,
- u8 key_index, bool pairwise, const u8 *mac_addr,
+ u8 key_index,
+ enum nl80211_key_type type,
+ const u8 *mac_addr,
struct key_params *params)
{
+ bool pairwise = type == NL80211_KEYTYPE_PAIRWISE;
int ret = 0, keylen = params->key_len;
const u8 *rx_mic = NULL;
const u8 *tx_mic = NULL;
@@ -641,9 +644,10 @@ static int add_key(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id,
static int del_key(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id,
u8 key_index,
- bool pairwise,
+ enum nl80211_key_type type,
const u8 *mac_addr)
{
+ bool pairwise = type == NL80211_KEYTYPE_PAIRWISE;
struct wilc_vif *vif = netdev_priv(wdev->netdev);
struct wilc_priv *priv = &vif->priv;
@@ -681,10 +685,11 @@ static int del_key(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id,
}
static int get_key(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id,
- u8 key_index, bool pairwise, const u8 *mac_addr,
- void *cookie,
+ u8 key_index, enum nl80211_key_type type,
+ const u8 *mac_addr, void *cookie,
void (*callback)(void *cookie, struct key_params *))
{
+ bool pairwise = type == NL80211_KEYTYPE_PAIRWISE;
struct wilc_vif *vif = netdev_priv(wdev->netdev);
struct wilc_priv *priv = &vif->priv;
struct key_params key_params;
diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
index 5cc8cdf594d3..d4233f5a31df 100644
--- a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
+++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
@@ -99,12 +99,14 @@ static void *nxpwifi_cfg80211_get_adapter(struct wiphy *wiphy)
/* cfg80211 operation handler to delete a network key. */
static int
nxpwifi_cfg80211_del_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr)
{
struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev);
static const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- const u8 *peer_mac = pairwise ? mac_addr : bc_mac;
+ const u8 *peer_mac =
+ (type == NL80211_KEYTYPE_PAIRWISE) ? mac_addr : bc_mac;
int ret;
ret = nxpwifi_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1);
@@ -426,7 +428,8 @@ nxpwifi_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
/* cfg80211 handler for adding an 802.11 encryption key. */
static int
nxpwifi_cfg80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr, struct key_params *params)
{
struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev);
@@ -436,7 +439,7 @@ nxpwifi_cfg80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
int ret;
eth_broadcast_addr(bc_mac);
- peer_mac = pairwise ? mac_addr : bc_mac;
+ peer_mac = (type == NL80211_KEYTYPE_PAIRWISE) ? mac_addr : bc_mac;
if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP &&
(params->cipher == WLAN_CIPHER_SUITE_WEP40 ||
diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index 45e2b7ae9633..d9c84d5ce254 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -531,10 +531,12 @@ qtnf_dump_station(struct wiphy *wiphy, struct wireless_dev *wdev,
}
static int qtnf_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr, struct key_params *params)
{
struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev);
+ bool pairwise = type == NL80211_KEYTYPE_PAIRWISE;
int ret;
ret = qtnf_cmd_send_add_key(vif, key_index, pairwise, mac_addr, params);
@@ -547,10 +549,12 @@ static int qtnf_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
}
static int qtnf_del_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr)
{
struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev);
+ bool pairwise = type == NL80211_KEYTYPE_PAIRWISE;
int ret;
ret = qtnf_cmd_send_del_key(vif, key_index, pairwise, mac_addr);
diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
index 3468d4114f60..be233a471b43 100644
--- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
+++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
@@ -825,7 +825,8 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param
}
static int cfg80211_rtw_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr, struct key_params *params)
{
char *alg_name;
@@ -904,7 +905,8 @@ static int cfg80211_rtw_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
}
static int cfg80211_rtw_get_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr, void *cookie,
void (*callback)(void *cookie,
struct key_params*))
@@ -913,7 +915,8 @@ static int cfg80211_rtw_get_key(struct wiphy *wiphy, struct wireless_dev *wdev,
}
static int cfg80211_rtw_del_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr)
{
struct adapter *padapter = rtw_netdev_priv(wdev->netdev);
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 9aa7d2d4a184..1ef5f85a9376 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1821,6 +1821,8 @@ struct sta_txpwr {
* @s1g_capa: S1G capabilities of station
* @uhr_capa: UHR capabilities of the station
* @uhr_capa_len: the length of the UHR capabilities
+ * @cip_cap_set: If CIP capabilities are present, required for CIP stations
+ * @cip_cap: CIP capabilities of station
*/
struct link_station_parameters {
const u8 *mld_mac;
@@ -1842,6 +1844,8 @@ struct link_station_parameters {
const struct ieee80211_s1g_cap *s1g_capa;
const struct ieee80211_uhr_cap *uhr_capa;
u8 uhr_capa_len;
+ bool cip_cap_set;
+ u8 cip_cap;
};
/**
@@ -3402,6 +3406,7 @@ struct cfg80211_ml_reconf_req {
* flag is not set.
* @ASSOC_REQ_SPP_AMSDU: SPP A-MSDUs will be used on this connection (if any)
* @ASSOC_REQ_DISABLE_UHR: Disable UHR
+ * @ASSOC_REQ_CIP: Enable Control Integrity Protocol
*/
enum cfg80211_assoc_req_flags {
ASSOC_REQ_DISABLE_HT = BIT(0),
@@ -3413,6 +3418,7 @@ enum cfg80211_assoc_req_flags {
CONNECT_REQ_MLO_SUPPORT = BIT(6),
ASSOC_REQ_SPP_AMSDU = BIT(7),
ASSOC_REQ_DISABLE_UHR = BIT(8),
+ ASSOC_REQ_CIP = BIT(9),
};
/**
@@ -5304,14 +5310,17 @@ struct cfg80211_ops {
unsigned int link_id);
int (*add_key)(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr, struct key_params *params);
int (*get_key)(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr, void *cookie,
void (*callback)(void *cookie, struct key_params*));
int (*del_key)(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise,
+ int link_id, u8 key_index,
+ enum nl80211_key_type type,
const u8 *mac_addr);
int (*set_default_key)(struct wiphy *wiphy,
struct net_device *netdev, int link_id,
@@ -6045,6 +6054,8 @@ struct wiphy_vendor_command {
* @eml_capabilities: EML capabilities (for MLO)
* @mld_capa_and_ops: MLD capabilities and operations (for MLO)
* @ext_mld_capa_and_ops: Extended MLD capabilities and operations (for MLO)
+ * @cip_supported: CIP is supported and the capabilities are valid
+ * @cip_capabilities: CIP Capabilities element containing the MIC padding delay
*/
struct wiphy_iftype_ext_capab {
enum nl80211_iftype iftype;
@@ -6054,6 +6065,8 @@ struct wiphy_iftype_ext_capab {
u16 eml_capabilities;
u16 mld_capa_and_ops;
u16 ext_mld_capa_and_ops;
+ bool cip_supported;
+ u8 cip_capabilities;
};
/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 9a2ccb8d66b8..843d1dbb725d 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3190,6 +3190,11 @@ enum nl80211_commands {
* known station to transmit a frame. This is relevant to know whether
* MLD address translation happened or to disable it when sending a frame.
*
+ * @NL80211_ATTR_ASSOC_CIP: Enable Control Integrity Protocol for the
+ * association
+ * @NL80211_ATTR_CIP_CAPABILITIES: The Control Integrity Protocol for the
+ * station.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3792,6 +3797,9 @@ enum nl80211_attrs {
NL80211_ATTR_FRAME_NO_STA,
+ NL80211_ATTR_ASSOC_CIP,
+ NL80211_ATTR_CIP_CAPABILITIES,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -3942,6 +3950,7 @@ enum nl80211_iftype {
* that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
* previously added station into associated state
* @NL80211_STA_FLAG_SPP_AMSDU: station supports SPP A-MSDUs
+ * @NL80211_STA_FLAG_CIP: station has Control Integrity Protocol (CIP) enabled
* @NL80211_STA_FLAG_MAX: highest station flag number currently defined
* @__NL80211_STA_FLAG_AFTER_LAST: internal use
*/
@@ -3955,6 +3964,7 @@ enum nl80211_sta_flags {
NL80211_STA_FLAG_TDLS_PEER,
NL80211_STA_FLAG_ASSOCIATED,
NL80211_STA_FLAG_SPP_AMSDU,
+ NL80211_STA_FLAG_CIP,
/* keep last */
__NL80211_STA_FLAG_AFTER_LAST,
@@ -5786,12 +5796,16 @@ enum nl80211_auth_type {
* @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key
* @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key
* @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS)
+ * @NL80211_KEYTYPE_CIGTK: Control Integrity Group Temporal Key
+ * The cipher is GMAC-256 but passed as GCMP-256,
+ * same as the pairwise key when used for CIP.
* @NUM_NL80211_KEYTYPES: number of defined key types
*/
enum nl80211_key_type {
NL80211_KEYTYPE_GROUP,
NL80211_KEYTYPE_PAIRWISE,
NL80211_KEYTYPE_PEERKEY,
+ NL80211_KEYTYPE_CIGTK,
NUM_NL80211_KEYTYPES
};
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 23f4f9ec86d0..eaafb45ff11f 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -611,12 +611,14 @@ static int ieee80211_set_tx(struct ieee80211_sub_if_data *sdata,
}
static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_idx, bool pairwise,
+ int link_id, u8 key_idx,
+ enum nl80211_key_type type,
const u8 *mac_addr, struct key_params *params)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
struct ieee80211_link_data *link =
ieee80211_link_or_deflink(sdata, link_id, false);
+ bool pairwise = type == NL80211_KEYTYPE_PAIRWISE;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta = NULL;
struct ieee80211_key *key;
@@ -737,10 +739,12 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
static struct ieee80211_key *
ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id,
- u8 key_idx, bool pairwise, const u8 *mac_addr)
+ u8 key_idx, enum nl80211_key_type type,
+ const u8 *mac_addr)
{
struct ieee80211_local *local __maybe_unused = sdata->local;
struct ieee80211_link_data *link = &sdata->deflink;
+ bool pairwise = type == NL80211_KEYTYPE_PAIRWISE;
struct ieee80211_key *key;
if (link_id >= 0) {
@@ -795,8 +799,8 @@ ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id,
}
static int ieee80211_del_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_idx, bool pairwise,
- const u8 *mac_addr)
+ int link_id, u8 key_idx,
+ enum nl80211_key_type type, const u8 *mac_addr)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
struct ieee80211_local *local = sdata->local;
@@ -804,7 +808,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct wireless_dev *wdev,
lockdep_assert_wiphy(local->hw.wiphy);
- key = ieee80211_lookup_key(sdata, link_id, key_idx, pairwise, mac_addr);
+ key = ieee80211_lookup_key(sdata, link_id, key_idx, type, mac_addr);
if (!key)
return -ENOENT;
@@ -814,7 +818,8 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct wireless_dev *wdev,
}
static int ieee80211_get_key(struct wiphy *wiphy, struct wireless_dev *wdev,
- int link_id, u8 key_idx, bool pairwise,
+ int link_id, u8 key_idx,
+ enum nl80211_key_type type,
const u8 *mac_addr, void *cookie,
void (*callback)(void *cookie,
struct key_params *params))
@@ -833,7 +838,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct wireless_dev *wdev,
rcu_read_lock();
- key = ieee80211_lookup_key(sdata, link_id, key_idx, pairwise, mac_addr);
+ key = ieee80211_lookup_key(sdata, link_id, key_idx, type, mac_addr);
if (!key)
goto out;
diff --git a/net/wireless/core.h b/net/wireless/core.h
index b4610f6685dc..508d731fbf33 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -443,13 +443,16 @@ void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev);
/* internal helpers */
bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
+bool cfg80211_cigtk_supported(struct wireless_dev *wdev,
+ struct genl_info *info);
bool cfg80211_valid_key_idx(struct wireless_dev *wdev,
- int key_idx, bool pairwise,
+ int key_idx, enum nl80211_key_type type,
const u8 *mac_addr);
int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct key_params *params, int key_idx,
- bool pairwise, const u8 *mac_addr);
+ enum nl80211_key_type type,
+ const u8 *mac_addr);
void __cfg80211_scan_done(struct wiphy *wiphy, struct wiphy_work *wk);
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
bool send_message);
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index b1d748bdb504..6ae561ed7f37 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -172,7 +172,8 @@ void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
*/
if (rdev->ops->del_key)
for (i = 0; i < 6; i++)
- rdev_del_key(rdev, wdev, -1, i, false, NULL);
+ rdev_del_key(rdev, wdev, -1, i, NL80211_KEYTYPE_GROUP,
+ NULL);
if (wdev->u.ibss.current_bss) {
cfg80211_unhold_bss(wdev->u.ibss.current_bss);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index ab5a14f5fe28..bfba773c6d9c 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1098,6 +1098,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range),
[NL80211_ATTR_STA_DUMP_LINK_STATS] = { .type = NLA_FLAG },
[NL80211_ATTR_FRAME_NO_STA] = { .type = NLA_FLAG },
+ [NL80211_ATTR_ASSOC_CIP] = { .type = NLA_FLAG },
+ [NL80211_ATTR_CIP_CAPABILITIES] = { .type = NLA_U8 },
};
/* policy for the key attributes */
@@ -1763,6 +1765,11 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
GENL_SET_ERR_MSG(info, "def key idx not 0-3");
return -EINVAL;
}
+ } else if (k->type == NL80211_KEYTYPE_CIGTK) {
+ if (k->idx < 0 || k->idx > 1) {
+ GENL_SET_ERR_MSG(info, "CIGTK idx not 0-1");
+ return -EINVAL;
+ }
} else {
if (k->idx < 0 || k->idx > 7) {
GENL_SET_ERR_MSG(info, "key idx not 0-7");
@@ -1827,7 +1834,9 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
} else if (parse.defmgmt)
goto error;
err = cfg80211_validate_key_settings(rdev, wdev, &parse.p,
- parse.idx, false, NULL);
+ parse.idx,
+ NL80211_KEYTYPE_GROUP,
+ NULL);
if (err)
goto error;
if (parse.p.cipher != WLAN_CIPHER_SUITE_WEP40 &&
@@ -3617,6 +3626,11 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
NL80211_ATTR_EXT_MLD_CAPA_AND_OPS,
capab->ext_mld_capa_and_ops))
goto nla_put_failure;
+ if (capab->cip_supported &&
+ nla_put_u8(msg,
+ NL80211_ATTR_CIP_CAPABILITIES,
+ capab->cip_capabilities))
+ goto nla_put_failure;
nla_nest_end(msg, nested_ext_capab);
if (state->split)
@@ -5373,6 +5387,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
struct wireless_dev *wdev = info->user_ptr[1];
u8 key_idx = 0;
const u8 *mac_addr = NULL;
+ enum nl80211_key_type type;
bool pairwise;
struct get_key_cookie cookie = {
.error = 0,
@@ -5405,19 +5420,24 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
pairwise = !!mac_addr;
+ type = pairwise ? NL80211_KEYTYPE_PAIRWISE : NL80211_KEYTYPE_GROUP;
if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
- u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
+ type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
- if (kt != NL80211_KEYTYPE_GROUP &&
- kt != NL80211_KEYTYPE_PAIRWISE)
+ if (type != NL80211_KEYTYPE_GROUP &&
+ type != NL80211_KEYTYPE_PAIRWISE &&
+ type != NL80211_KEYTYPE_CIGTK) {
+ GENL_SET_ERR_MSG(info, "key type not pairwise, group or CIGTK");
return -EINVAL;
- pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
+ }
+
+ pairwise = type == NL80211_KEYTYPE_PAIRWISE;
}
if (!rdev->ops->get_key)
return -EOPNOTSUPP;
- if (!cfg80211_valid_key_idx(wdev, key_idx, pairwise, mac_addr))
+ if (!cfg80211_valid_key_idx(wdev, key_idx, type, mac_addr))
return -ENOENT;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -5446,7 +5466,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
if (err)
goto free_msg;
- err = rdev_get_key(rdev, wdev, link_id, key_idx, pairwise, mac_addr,
+ err = rdev_get_key(rdev, wdev, link_id, key_idx, type, mac_addr,
&cookie, get_key_callback);
if (err)
@@ -5605,8 +5625,9 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
/* for now */
if (key.type != NL80211_KEYTYPE_PAIRWISE &&
- key.type != NL80211_KEYTYPE_GROUP) {
- GENL_SET_ERR_MSG(info, "key type not pairwise or group");
+ key.type != NL80211_KEYTYPE_GROUP &&
+ key.type != NL80211_KEYTYPE_CIGTK) {
+ GENL_SET_ERR_MSG(info, "key type not pairwise, group or CIGTK");
return -EINVAL;
}
@@ -5618,8 +5639,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
return -EOPNOTSUPP;
if (cfg80211_validate_key_settings(rdev, wdev, &key.p, key.idx,
- key.type == NL80211_KEYTYPE_PAIRWISE,
- mac_addr)) {
+ key.type, mac_addr)) {
GENL_SET_ERR_MSG(info, "key setting validation failed");
return -EINVAL;
}
@@ -5633,8 +5653,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
key.type == NL80211_KEYTYPE_PAIRWISE);
if (!err) {
- err = rdev_add_key(rdev, wdev, link_id, key.idx,
- key.type == NL80211_KEYTYPE_PAIRWISE,
+ err = rdev_add_key(rdev, wdev, link_id, key.idx, key.type,
mac_addr, &key.p);
if (err)
GENL_SET_ERR_MSG(info, "key addition failed");
@@ -5668,12 +5687,13 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
/* for now */
if (key.type != NL80211_KEYTYPE_PAIRWISE &&
- key.type != NL80211_KEYTYPE_GROUP)
+ key.type != NL80211_KEYTYPE_GROUP &&
+ key.type != NL80211_KEYTYPE_CIGTK) {
+ GENL_SET_ERR_MSG(info, "key type not pairwise, group or CIGTK");
return -EINVAL;
+ }
- if (!cfg80211_valid_key_idx(wdev, key.idx,
- key.type == NL80211_KEYTYPE_PAIRWISE,
- mac_addr))
+ if (!cfg80211_valid_key_idx(wdev, key.idx, key.type, mac_addr))
return -EINVAL;
if (!rdev->ops->del_key)
@@ -5687,8 +5707,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
if (!err)
err = rdev_del_key(rdev, wdev, link_id, key.idx,
- key.type == NL80211_KEYTYPE_PAIRWISE,
- mac_addr);
+ key.type, mac_addr);
#ifdef CONFIG_CFG80211_WEXT
if (!err) {
@@ -8819,7 +8838,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
return -EINVAL;
/* When you run into this, adjust the code below for the new flag */
- BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 8);
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 9);
switch (statype) {
case CFG80211_STA_MESH_PEER_KERNEL:
@@ -8913,7 +8932,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
BIT(NL80211_STA_FLAG_WME) |
BIT(NL80211_STA_FLAG_MFP) |
- BIT(NL80211_STA_FLAG_SPP_AMSDU)))
+ BIT(NL80211_STA_FLAG_SPP_AMSDU) |
+ BIT(NL80211_STA_FLAG_CIP)))
return -EINVAL;
/* but authenticated/associated only if driver handles it */
@@ -9290,6 +9310,22 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
nla_get_u16(info->attrs[NL80211_ATTR_EML_CAPABILITY]);
}
+ if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_CIP) &&
+ params.sta_flags_set & BIT(NL80211_STA_FLAG_CIP)) {
+ if (!cfg80211_cigtk_supported(wdev, info))
+ return -EINVAL;
+
+ /* CIP capabilities are required if CIP is being enabled */
+ if (info->attrs[NL80211_ATTR_CIP_CAPABILITIES]) {
+ params.link_sta_params.cip_cap_set = true;
+ params.link_sta_params.cip_cap =
+ nla_get_u8(info->attrs[NL80211_ATTR_CIP_CAPABILITIES]);
+ } else {
+ GENL_SET_ERR_MSG(info, "No CIP capabilities provided");
+ return -EINVAL;
+ }
+ }
+
if (info->attrs[NL80211_ATTR_AIRTIME_WEIGHT])
params.airtime_weight =
nla_get_u16(info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]);
@@ -9489,6 +9525,22 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
nla_get_u16(info->attrs[NL80211_ATTR_EML_CAPABILITY]);
}
+ if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_CIP) &&
+ params.sta_flags_set & BIT(NL80211_STA_FLAG_CIP)) {
+ if (!cfg80211_cigtk_supported(wdev, info))
+ return -EINVAL;
+
+ /* CIP capabilities are required if CIP is enabled */
+ if (info->attrs[NL80211_ATTR_CIP_CAPABILITIES]) {
+ params.link_sta_params.cip_cap_set = true;
+ params.link_sta_params.cip_cap =
+ nla_get_u8(info->attrs[NL80211_ATTR_CIP_CAPABILITIES]);
+ } else {
+ GENL_SET_ERR_MSG(info, "No CIP capabilities provided");
+ return -EINVAL;
+ }
+ }
+
if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY])
params.link_sta_params.he_6ghz_capa =
nla_data(info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]);
@@ -9566,7 +9618,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
/* When you run into this, adjust the code below for the new flag */
- BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 8);
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 9);
switch (wdev->iftype) {
case NL80211_IFTYPE_AP:
@@ -9595,6 +9647,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
params.sta_flags_mask & BIT(NL80211_STA_FLAG_SPP_AMSDU))
return -EINVAL;
+ if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_CIP) &&
+ !cfg80211_cigtk_supported(wdev, info))
+ return -EINVAL;
+
/* Older userspace, or userspace wanting to be compatible with
* !NL80211_FEATURE_FULL_AP_CLIENT_STATE, will not set the auth
* and assoc flags in the mask, but assumes the station will be
@@ -13321,6 +13377,13 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
req.flags |= ASSOC_REQ_SPP_AMSDU;
}
+ if (nla_get_flag(info->attrs[NL80211_ATTR_ASSOC_CIP])) {
+ if (!cfg80211_cigtk_supported(dev->ieee80211_ptr, info))
+ return -EINVAL;
+
+ req.flags |= ASSOC_REQ_CIP;
+ }
+
req.link_id = nl80211_link_id_or_invalid(info->attrs);
if (info->attrs[NL80211_ATTR_MLO_LINKS]) {
@@ -19201,6 +19264,23 @@ nl80211_add_mod_link_station(struct sk_buff *skb, struct genl_info *info,
params.he_6ghz_capa =
nla_data(info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]);
+ if (info->attrs[NL80211_ATTR_CIP_CAPABILITIES]) {
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (!cfg80211_cigtk_supported(wdev, info))
+ return -EINVAL;
+
+ params.cip_cap_set = true;
+ params.cip_cap =
+ nla_get_u8(info->attrs[NL80211_ATTR_CIP_CAPABILITIES]);
+
+ /* Only permit CIP capabilities when adding link stations */
+ if (!add) {
+ GENL_SET_ERR_MSG(info, "Cannot modify CIP capabilities");
+ return -EINVAL;
+ }
+ }
+
if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) {
params.opmode_notif_used = true;
params.opmode_notif =
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 46849fe8d0b3..7653cd0da9b9 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -78,42 +78,43 @@ rdev_change_virtual_intf(struct cfg80211_registered_device *rdev,
static inline int rdev_add_key(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, int link_id,
- u8 key_index, bool pairwise, const u8 *mac_addr,
- struct key_params *params)
+ u8 key_index, enum nl80211_key_type type,
+ const u8 *mac_addr, struct key_params *params)
{
int ret;
- trace_rdev_add_key(&rdev->wiphy, wdev, link_id, key_index, pairwise,
+ trace_rdev_add_key(&rdev->wiphy, wdev, link_id, key_index, type,
mac_addr, params->mode);
ret = rdev->ops->add_key(&rdev->wiphy, wdev, link_id, key_index,
- pairwise, mac_addr, params);
+ type, mac_addr, params);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
static inline int
rdev_get_key(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev,
- int link_id, u8 key_index, bool pairwise, const u8 *mac_addr,
- void *cookie,
+ int link_id, u8 key_index, enum nl80211_key_type type,
+ const u8 *mac_addr, void *cookie,
void (*callback)(void *cookie, struct key_params*))
{
int ret;
- trace_rdev_get_key(&rdev->wiphy, wdev, link_id, key_index, pairwise,
+ trace_rdev_get_key(&rdev->wiphy, wdev, link_id, key_index, type,
mac_addr);
ret = rdev->ops->get_key(&rdev->wiphy, wdev, link_id, key_index,
- pairwise, mac_addr, cookie, callback);
+ type, mac_addr, cookie, callback);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
static inline int rdev_del_key(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, int link_id,
- u8 key_index, bool pairwise, const u8 *mac_addr)
+ u8 key_index, enum nl80211_key_type type,
+ const u8 *mac_addr)
{
int ret;
- trace_rdev_del_key(&rdev->wiphy, wdev, link_id, key_index, pairwise,
+ trace_rdev_del_key(&rdev->wiphy, wdev, link_id, key_index, type,
mac_addr);
ret = rdev->ops->del_key(&rdev->wiphy, wdev, link_id, key_index,
- pairwise, mac_addr);
+ type, mac_addr);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 2a719b5c487e..458f72d8351a 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -1387,7 +1387,15 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
max_key_idx = 7;
for (i = 0; i <= max_key_idx; i++)
- rdev_del_key(rdev, wdev, -1, i, false, NULL);
+ rdev_del_key(rdev, wdev, -1, i, NL80211_KEYTYPE_GROUP,
+ NULL);
+
+ if (cfg80211_cigtk_supported(wdev, NULL)) {
+ rdev_del_key(rdev, wdev, -1, 0, NL80211_KEYTYPE_CIGTK,
+ NULL);
+ rdev_del_key(rdev, wdev, -1, 1, NL80211_KEYTYPE_CIGTK,
+ NULL);
+ }
}
rdev_set_qos_map(rdev, dev, NULL);
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 8c2a91b85c39..c210ca1e44e2 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -557,15 +557,15 @@ TRACE_EVENT(rdev_change_virtual_intf,
DECLARE_EVENT_CLASS(key_handle,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id,
- u8 key_index, bool pairwise, const u8 *mac_addr),
- TP_ARGS(wiphy, wdev, link_id, key_index, pairwise, mac_addr),
+ u8 key_index, enum nl80211_key_type type, const u8 *mac_addr),
+ TP_ARGS(wiphy, wdev, link_id, key_index, type, mac_addr),
TP_STRUCT__entry(
WIPHY_ENTRY
WDEV_ENTRY
MAC_ENTRY(mac_addr)
__field(int, link_id)
__field(u8, key_index)
- __field(bool, pairwise)
+ __field(u8, type)
),
TP_fast_assign(
WIPHY_ASSIGN;
@@ -573,38 +573,40 @@ DECLARE_EVENT_CLASS(key_handle,
MAC_ASSIGN(mac_addr, mac_addr);
__entry->link_id = link_id;
__entry->key_index = key_index;
- __entry->pairwise = pairwise;
+ __entry->type = type;
),
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", link_id: %d, "
- "key_index: %u, pairwise: %s, mac addr: %pM",
+ "key_index: %u, pairwise: %s, type: %d, mac addr: %pM",
WIPHY_PR_ARG, WDEV_PR_ARG, __entry->link_id,
- __entry->key_index, BOOL_TO_STR(__entry->pairwise),
- __entry->mac_addr)
+ __entry->key_index,
+ BOOL_TO_STR(__entry->type == NL80211_KEYTYPE_PAIRWISE),
+ __entry->type, __entry->mac_addr)
);
DEFINE_EVENT(key_handle, rdev_get_key,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id,
- u8 key_index, bool pairwise, const u8 *mac_addr),
- TP_ARGS(wiphy, wdev, link_id, key_index, pairwise, mac_addr)
+ u8 key_index, enum nl80211_key_type type, const u8 *mac_addr),
+ TP_ARGS(wiphy, wdev, link_id, key_index, type, mac_addr)
);
DEFINE_EVENT(key_handle, rdev_del_key,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id,
- u8 key_index, bool pairwise, const u8 *mac_addr),
- TP_ARGS(wiphy, wdev, link_id, key_index, pairwise, mac_addr)
+ u8 key_index, enum nl80211_key_type type, const u8 *mac_addr),
+ TP_ARGS(wiphy, wdev, link_id, key_index, type, mac_addr)
);
TRACE_EVENT(rdev_add_key,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id,
- u8 key_index, bool pairwise, const u8 *mac_addr, u8 mode),
- TP_ARGS(wiphy, wdev, link_id, key_index, pairwise, mac_addr, mode),
+ u8 key_index, enum nl80211_key_type type, const u8 *mac_addr,
+ u8 mode),
+ TP_ARGS(wiphy, wdev, link_id, key_index, type, mac_addr, mode),
TP_STRUCT__entry(
WIPHY_ENTRY
WDEV_ENTRY
MAC_ENTRY(mac_addr)
__field(int, link_id)
__field(u8, key_index)
- __field(bool, pairwise)
+ __field(u8, type)
__field(u8, mode)
),
TP_fast_assign(
@@ -613,15 +615,16 @@ TRACE_EVENT(rdev_add_key,
MAC_ASSIGN(mac_addr, mac_addr);
__entry->link_id = link_id;
__entry->key_index = key_index;
- __entry->pairwise = pairwise;
+ __entry->type = type;
__entry->mode = mode;
),
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", link_id: %d, "
- "key_index: %u, mode: %u, pairwise: %s, "
+ "key_index: %u, mode: %u, pairwise: %s, type: %d, "
"mac addr: %pM",
WIPHY_PR_ARG, WDEV_PR_ARG, __entry->link_id,
__entry->key_index, __entry->mode,
- BOOL_TO_STR(__entry->pairwise), __entry->mac_addr)
+ BOOL_TO_STR(__entry->type == NL80211_KEYTYPE_PAIRWISE),
+ __entry->type, __entry->mac_addr)
);
TRACE_EVENT(rdev_set_default_key,
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 3e584d0ca3e2..dd166c352910 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -258,8 +258,24 @@ static bool cfg80211_igtk_cipher_supported(struct wiphy *wiphy)
return false;
}
+bool cfg80211_cigtk_supported(struct wireless_dev *wdev,
+ struct genl_info *info)
+{
+ const struct wiphy_iftype_ext_capab *ext_capab =
+ cfg80211_get_iftype_ext_capa(wdev->wiphy, wdev->iftype);
+
+ if (ext_capab && ext_capab->cip_supported)
+ return true;
+
+ if (info)
+ GENL_SET_ERR_MSG(info,
+ "Control Integrity Protocol not supported");
+
+ return false;
+}
+
bool cfg80211_valid_key_idx(struct wireless_dev *wdev,
- int key_idx, bool pairwise,
+ int key_idx, enum nl80211_key_type type,
const u8 *mac_addr)
{
if (WARN_ON(!wdev))
@@ -272,13 +288,24 @@ bool cfg80211_valid_key_idx(struct wireless_dev *wdev,
* Can't differentiate ciphers here so allow 0..3.
* Pairwise keys must be for a station (MAC address given).
*/
- if (pairwise) {
+ if (type == NL80211_KEYTYPE_PAIRWISE) {
if (!mac_addr)
return false;
return key_idx < 4;
}
+ /* Check the wdev/iftype supports CIGTK */
+ if (type == NL80211_KEYTYPE_CIGTK) {
+ if (!cfg80211_cigtk_supported(wdev, NULL))
+ return false;
+
+ if (key_idx >= 2)
+ return false;
+
+ /* fallthrough for mac_addr checks */
+ }
+
/*
* For group keys, mac_addr==NULL means setting a group key
* for TX, which is only supported on some interface types,
@@ -343,9 +370,12 @@ bool cfg80211_valid_key_idx(struct wireless_dev *wdev,
int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct key_params *params, int key_idx,
- bool pairwise, const u8 *mac_addr)
+ enum nl80211_key_type type,
+ const u8 *mac_addr)
{
- if (!cfg80211_valid_key_idx(wdev, key_idx, pairwise, mac_addr))
+ bool pairwise = type == NL80211_KEYTYPE_PAIRWISE;
+
+ if (!cfg80211_valid_key_idx(wdev, key_idx, type, mac_addr))
return -EINVAL;
switch (params->cipher) {
@@ -1172,7 +1202,7 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
for (i = 0; i < 4; i++) {
if (!wdev->connect_keys->params[i].cipher)
continue;
- if (rdev_add_key(rdev, wdev, -1, i, false, NULL,
+ if (rdev_add_key(rdev, wdev, -1, i, NL80211_KEYTYPE_GROUP, NULL,
&wdev->connect_keys->params[i])) {
netdev_err(dev, "failed to set key %d\n", i);
continue;
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index d45bc08c0de4..ec2389f6b8b1 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -401,6 +401,8 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
int idx, struct key_params *params)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
+ enum nl80211_key_type key_type =
+ pairwise ? NL80211_KEYTYPE_PAIRWISE : NL80211_KEYTYPE_GROUP;
int err, i;
bool rejoin = false;
@@ -454,11 +456,11 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
rejoin = true;
}
- if (!cfg80211_valid_key_idx(wdev, idx, pairwise, addr))
+ if (!cfg80211_valid_key_idx(wdev, idx, key_type, addr))
err = -ENOENT;
else
- err = rdev_del_key(rdev, wdev, -1, idx, pairwise,
- addr);
+ err = rdev_del_key(rdev, wdev, -1, idx,
+ key_type, addr);
}
wdev->wext.connect.privacy = false;
/*
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH wireless-next v3 6/7] wifi: mac80211: add Control Integrity Protocol handling
2026-09-21 12:40 [PATCH wireless-next v3 0/7] Add support for Control Integrity Protocol (CIP) Benjamin Berg
` (4 preceding siblings ...)
2026-09-21 12:40 ` [PATCH wireless-next v3 5/7] wifi: mac80211: add helpers to parse/generate CIP Capabilities Benjamin Berg
@ 2026-09-21 12:40 ` Benjamin Berg
2026-09-21 12:40 ` [PATCH wireless-next v3 7/7] wifi: mac80211_hwsim: claim support for Control Integrity Protocol Benjamin Berg
6 siblings, 0 replies; 8+ messages in thread
From: Benjamin Berg @ 2026-09-21 12:40 UTC (permalink / raw)
To: linux-wireless
Cc: Arend van Spriel, Brian Norris, Francesco Dolcini, Ajay Singh,
Claudiu Beznea, Jeff Chen, Igor Mitsyanko, Sergey Matyukevich,
Greg Kroah-Hartman, brcm80211, libertas-dev, Miri Korenblit,
Ping-Ke Shih, Benjamin Berg
From: Benjamin Berg <benjamin.berg@intel.com>
Add the handling of CIP during association, when adding a station in AP
mode and also add some checks whether the keys are being installed
correctly.
For internal use, add a new key flag CIP that is used for both the
pairwise key and CIGTK. The CIGTK will then have the MCAST_KEY and CIP
flags set.
As the CIGTK uses key indices 0 and 1, add a new variable to track it
separately from the other GTKs.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
v3:
- Store the CIP capa in the link STA as it contains multiple values now
---
include/net/mac80211.h | 8 ++++
net/mac80211/cfg.c | 22 ++++++++-
net/mac80211/ieee80211_i.h | 2 +
net/mac80211/key.c | 94 +++++++++++++++++++++++++++++---------
net/mac80211/key.h | 3 +-
net/mac80211/mlme.c | 20 +++++++-
net/mac80211/sta_info.h | 2 +
7 files changed, 126 insertions(+), 25 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index c9d0e1203d1b..15ac95048ce5 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -2395,6 +2395,8 @@ static inline bool lockdep_vif_wiphy_mutex_held(struct ieee80211_vif *vif)
* number generation only
* @IEEE80211_KEY_FLAG_SPP_AMSDU: SPP A-MSDUs can be used with this key
* (set by mac80211 from the sta->spp_amsdu flag)
+ * @IEEE80211_KEY_FLAG_CIP: This key is used for the Control Integrity
+ * Protocol, and is either a pairwise key or a CIGTK.
*/
enum ieee80211_key_flags {
IEEE80211_KEY_FLAG_GENERATE_IV_MGMT = BIT(0),
@@ -2409,6 +2411,7 @@ enum ieee80211_key_flags {
IEEE80211_KEY_FLAG_NO_AUTO_TX = BIT(9),
IEEE80211_KEY_FLAG_GENERATE_MMIE = BIT(10),
IEEE80211_KEY_FLAG_SPP_AMSDU = BIT(11),
+ IEEE80211_KEY_FLAG_CIP = BIT(12),
};
/**
@@ -2629,6 +2632,7 @@ struct ieee80211_sta_aggregates {
* @eht_cap: EHT capabilities of this STA
* @uhr_cap: UHR capabilities of this STA
* @s1g_cap: S1G capabilities of this STA
+ * @cip_cap: the CIP capabilities of this STA (or zero)
* @agg: per-link data for multi-link aggregation
* @bandwidth: current bandwidth the station can receive with.
* This is the minimum between the peer's capabilities and our own
@@ -2657,6 +2661,7 @@ struct ieee80211_link_sta {
struct ieee80211_sta_eht_cap eht_cap;
struct ieee80211_sta_uhr_cap uhr_cap;
struct ieee80211_sta_s1g_cap s1g_cap;
+ u8 cip_cap;
struct ieee80211_sta_aggregates agg;
@@ -2720,6 +2725,7 @@ struct ieee80211_link_sta {
* @valid_links: bitmap of valid links, or 0 for non-MLO
* @spp_amsdu: indicates whether the STA uses SPP A-MSDU or not.
* @epp_peer: indicates that the peer is an EPP peer.
+ * @cip: indicates whether the STA uses control frame protection or not.
* @nmi: For NDI stations, pointer to the NMI station of the peer.
* @nan_sched: NAN peer schedule for this station. Valid only for NMI stations.
* @ext_mld_capa_ops: the MLD's extended MLD capabilities and operations
@@ -2741,6 +2747,8 @@ struct ieee80211_sta {
u8 max_amsdu_subframes;
u16 eml_cap;
+ bool cip;
+
struct ieee80211_sta_aggregates *cur;
bool support_p2p_ps;
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index eaafb45ff11f..3714f8ccdf82 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -619,6 +619,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_link_data *link =
ieee80211_link_or_deflink(sdata, link_id, false);
bool pairwise = type == NL80211_KEYTYPE_PAIRWISE;
+ bool cigtk = type == NL80211_KEYTYPE_CIGTK;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta = NULL;
struct ieee80211_key *key;
@@ -662,6 +663,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
key->conf.link_id = -1;
} else {
key->conf.link_id = link->link_id;
+
+ if (cigtk)
+ key->conf.flags |= IEEE80211_KEY_FLAG_CIP;
}
if (params->mode == NL80211_KEY_NO_TX)
@@ -745,6 +749,7 @@ ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id,
struct ieee80211_local *local __maybe_unused = sdata->local;
struct ieee80211_link_data *link = &sdata->deflink;
bool pairwise = type == NL80211_KEYTYPE_PAIRWISE;
+ bool cigtk = type == NL80211_KEYTYPE_CIGTK;
struct ieee80211_key *key;
if (link_id >= 0) {
@@ -774,7 +779,11 @@ ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id,
return wiphy_dereference(local->hw.wiphy,
sta->ptk[key_idx]);
- if (!pairwise &&
+ if (cigtk && key_idx < NUM_CTRL_KEYS)
+ return wiphy_dereference(local->hw.wiphy,
+ link_sta->cigtk[key_idx]);
+
+ if (!pairwise && !cigtk &&
key_idx < NUM_DEFAULT_KEYS +
NUM_DEFAULT_MGMT_KEYS +
NUM_DEFAULT_BEACON_KEYS)
@@ -787,6 +796,9 @@ ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id,
if (pairwise && key_idx < NUM_DEFAULT_KEYS)
return wiphy_dereference(local->hw.wiphy, sdata->keys[key_idx]);
+ if (cigtk)
+ return wiphy_dereference(local->hw.wiphy, link->cigtk[key_idx]);
+
key = wiphy_dereference(local->hw.wiphy, link->gtk[key_idx]);
if (key)
return key;
@@ -2294,6 +2306,8 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
case STA_LINK_MODE_NEW:
if (!params->link_mac)
return -EINVAL;
+ if (sta->sta.cip && !params->cip_cap_set)
+ return -EINVAL;
break;
case STA_LINK_MODE_LINK_MODIFY:
break;
@@ -2416,6 +2430,9 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
ieee80211_s1g_cap_to_sta_s1g_cap(sdata, params->s1g_capa,
link_sta);
+ if (params->cip_cap_set)
+ link_sta->pub->cip_cap = params->cip_cap;
+
switch (sdata->vif.type) {
case NL80211_IFTYPE_NAN:
case NL80211_IFTYPE_NAN_DATA:
@@ -2578,6 +2595,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
if (params->eml_cap_present)
sta->sta.eml_cap = params->eml_cap;
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_CIP))
+ sta->sta.cip = true;
+
ret = sta_link_apply_parameters(local, sta, STA_LINK_MODE_STA_MODIFY,
¶ms->link_sta_params);
if (ret)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 674a4676a972..66231ea8a985 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -486,6 +486,7 @@ struct ieee80211_mgd_assoc_data {
bool comeback; /* whether the AP has requested association comeback */
bool s1g;
bool spp_amsdu;
+ bool cip;
s8 assoc_link_id;
@@ -1113,6 +1114,7 @@ struct ieee80211_link_data {
struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS +
NUM_DEFAULT_MGMT_KEYS +
NUM_DEFAULT_BEACON_KEYS];
+ struct ieee80211_key __rcu *cigtk[NUM_CTRL_KEYS];
struct ieee80211_key __rcu *default_multicast_key;
struct ieee80211_key __rcu *default_mgmt_key;
struct ieee80211_key __rcu *default_beacon_key;
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 9ac7a5c4b861..4c9788ec0b74 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -205,6 +205,10 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
sta ? sta->sta.addr : bcast_addr, ret);
out_unsupported:
+ /* Control Integrity Protocol can only be done in hardware */
+ if (key->conf.flags & IEEE80211_KEY_FLAG_CIP)
+ return -EINVAL;
+
switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
@@ -440,11 +444,13 @@ void ieee80211_set_default_beacon_key(struct ieee80211_link_data *link,
static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
struct ieee80211_link_data *link,
struct sta_info *sta,
- bool pairwise,
+ enum ieee80211_key_flags flags,
struct ieee80211_key *old,
struct ieee80211_key *new)
{
struct link_sta_info *link_sta = sta ? &sta->deflink : NULL;
+ bool pairwise = flags & IEEE80211_KEY_FLAG_PAIRWISE;
+ bool cip = flags & IEEE80211_KEY_FLAG_CIP;
int link_id;
int idx;
int ret = 0;
@@ -535,6 +541,8 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
if (new &&
!(new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX))
_ieee80211_set_tx_key(new, true);
+ } else if (cip) {
+ rcu_assign_pointer(link_sta->cigtk[idx], new);
} else {
rcu_assign_pointer(link_sta->gtk[idx], new);
}
@@ -569,6 +577,8 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
if (is_wep || pairwise)
rcu_assign_pointer(sdata->keys[idx], new);
+ else if (cip)
+ rcu_assign_pointer(link->cigtk[idx], new);
else
rcu_assign_pointer(link->gtk[idx], new);
@@ -883,6 +893,10 @@ int ieee80211_key_link(struct ieee80211_key *key,
ret = -EOPNOTSUPP;
goto out;
}
+
+ /* Set CIP flag if enabled for the station */
+ if (sta->sta.cip)
+ key->conf.flags |= IEEE80211_KEY_FLAG_CIP;
} else if (sta) {
struct link_sta_info *link_sta = &sta->deflink;
int link_id = key->conf.link_id;
@@ -896,15 +910,25 @@ int ieee80211_key_link(struct ieee80211_key *key,
}
}
- old_key = wiphy_dereference(sdata->local->hw.wiphy,
- link_sta->gtk[idx]);
- } else {
- if (idx < NUM_DEFAULT_KEYS)
+ if (key->conf.flags & IEEE80211_KEY_FLAG_CIP)
old_key = wiphy_dereference(sdata->local->hw.wiphy,
- sdata->keys[idx]);
- if (!old_key)
+ link_sta->cigtk[idx]);
+ else
old_key = wiphy_dereference(sdata->local->hw.wiphy,
- link->gtk[idx]);
+ link_sta->gtk[idx]);
+ } else {
+ if (key->conf.flags & IEEE80211_KEY_FLAG_CIP) {
+ old_key = wiphy_dereference(sdata->local->hw.wiphy,
+ link->cigtk[idx]);
+ } else {
+ if (idx < NUM_DEFAULT_KEYS)
+ old_key = wiphy_dereference(sdata->local->hw.wiphy,
+ sdata->keys[idx]);
+
+ if (!old_key)
+ old_key = wiphy_dereference(sdata->local->hw.wiphy,
+ link->gtk[idx]);
+ }
}
/* Non-pairwise keys must also not switch the cipher on rekey */
@@ -938,9 +962,17 @@ int ieee80211_key_link(struct ieee80211_key *key,
if (sta && sta->sta.spp_amsdu)
key->conf.flags |= IEEE80211_KEY_FLAG_SPP_AMSDU;
+ /* A CIP related key must be GCMP-256 (really GMAC-256) */
+ if ((key->conf.flags & IEEE80211_KEY_FLAG_CIP) &&
+ key->conf.cipher != WLAN_CIPHER_SUITE_GCMP_256) {
+ ret = -EINVAL;
+ goto out;
+ }
+
increment_tailroom_need_count(sdata);
- ret = ieee80211_key_replace(sdata, link, sta, pairwise, old_key, key);
+ ret = ieee80211_key_replace(sdata, link, sta, key->conf.flags,
+ old_key, key);
if (!ret) {
ieee80211_debugfs_key_add(key);
@@ -966,8 +998,7 @@ void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom)
*/
if (key->sdata)
ieee80211_key_replace(key->sdata, NULL, key->sta,
- key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
- key, NULL);
+ key->conf.flags, key, NULL);
ieee80211_key_destroy(key, delay_tailroom);
}
@@ -1098,8 +1129,7 @@ static void ieee80211_free_keys_iface(struct ieee80211_sub_if_data *sdata,
list_for_each_entry_safe(key, tmp, &sdata->key_list, list) {
ieee80211_key_replace(key->sdata, NULL, key->sta,
- key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
- key, NULL);
+ key->conf.flags, key, NULL);
list_add_tail(&key->list, keys);
}
@@ -1119,8 +1149,7 @@ void ieee80211_remove_link_keys(struct ieee80211_link_data *link,
if (key->conf.link_id != link->link_id)
continue;
ieee80211_key_replace(key->sdata, link, key->sta,
- key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
- key, NULL);
+ key->conf.flags, key, NULL);
list_add_tail(&key->list, keys);
}
}
@@ -1196,8 +1225,17 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local,
if (!key)
continue;
ieee80211_key_replace(key->sdata, NULL, key->sta,
- key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
- key, NULL);
+ key->conf.flags, key, NULL);
+ __ieee80211_key_destroy(key, key->sdata->vif.type ==
+ NL80211_IFTYPE_STATION);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sta->deflink.cigtk); i++) {
+ key = wiphy_dereference(local->hw.wiphy, sta->deflink.cigtk[i]);
+ if (!key)
+ continue;
+ ieee80211_key_replace(key->sdata, NULL, key->sta,
+ key->conf.flags, key, NULL);
__ieee80211_key_destroy(key, key->sdata->vif.type ==
NL80211_IFTYPE_STATION);
}
@@ -1207,8 +1245,7 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local,
if (!key)
continue;
ieee80211_key_replace(key->sdata, NULL, key->sta,
- key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
- key, NULL);
+ key->conf.flags, key, NULL);
__ieee80211_key_destroy(key, key->sdata->vif.type ==
NL80211_IFTYPE_STATION);
}
@@ -1390,10 +1427,20 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
NUM_DEFAULT_BEACON_KEYS))
return ERR_PTR(-EINVAL);
- prev_key = wiphy_dereference(local->hw.wiphy,
- link_data->gtk[idx]);
+ if (WARN_ON(cigtk && idx >= NUM_CTRL_KEYS))
+ return ERR_PTR(-EINVAL);
+
+ if (cigtk)
+ prev_key = wiphy_dereference(local->hw.wiphy,
+ link_data->cigtk[idx]);
+ else
+ prev_key = wiphy_dereference(local->hw.wiphy,
+ link_data->gtk[idx]);
if (!prev_key) {
- if (idx < NUM_DEFAULT_KEYS) {
+ if (cigtk) {
+ prev_key = wiphy_dereference(local->hw.wiphy,
+ link_data->cigtk[idx ^ 1]);
+ } else if (idx < NUM_DEFAULT_KEYS) {
for (int i = 0; i < NUM_DEFAULT_KEYS; i++) {
if (i == idx)
continue;
@@ -1424,6 +1471,9 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+ if (prev_key->conf.flags & IEEE80211_KEY_FLAG_CIP)
+ key->conf.flags |= IEEE80211_KEY_FLAG_CIP;
+
key->conf.link_id = link_data->link_id;
err = ieee80211_key_link(key, link_data, NULL);
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 826e4e9387c5..c85992493afd 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -2,7 +2,7 @@
/*
* Copyright 2002-2004, Instant802 Networks, Inc.
* Copyright 2005, Devicescape Software, Inc.
- * Copyright (C) 2019, 2022-2023 Intel Corporation
+ * Copyright (C) 2019, 2022-2023, 2026 Intel Corporation
*/
#ifndef IEEE80211_KEY_H
@@ -19,6 +19,7 @@
#define NUM_DEFAULT_KEYS 4
#define NUM_DEFAULT_MGMT_KEYS 2
#define NUM_DEFAULT_BEACON_KEYS 2
+#define NUM_CTRL_KEYS 2
#define INVALID_PTK_KEYIDX 2 /* Keyidx always pointing to a NULL key for PTK */
struct ieee80211_local;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index cf3873d8855b..76c45a0eea62 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -2337,6 +2337,10 @@ ieee80211_add_link_elems(struct ieee80211_sub_if_data *sdata,
ieee80211_put_eht_cap(skb, sdata, sband,
&assoc_data->link[link_id].conn);
+ /* Insert CIP only on the assoc link (it will be inherited) */
+ if (link_id == assoc_data->assoc_link_id && assoc_data->cip)
+ ieee80211_put_cip_cap(skb, sdata);
+
if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_UHR)
ieee80211_put_uhr_cap(skb, sdata, sband);
@@ -2613,7 +2617,8 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
assoc_data->ie_len + /* extra IEs */
(assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) +
9 /* WMM */ +
- 4 /* regulatory connectivity, if 6 GHz is supported */;
+ 4 /* regulatory connectivity, if 6 GHz is supported */ +
+ (assoc_data->cip ? 4 /* CIP capabilities */ : 0);
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
struct cfg80211_bss *cbss = assoc_data->link[link_id].bss;
@@ -6274,6 +6279,17 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
/* TODO: OPEN: what happens if BSS color disable is set? */
}
+ if (assoc_data->cip) {
+ if (elems->cip_cap) {
+ link_sta->pub->cip_cap = elems->cip_cap->v;
+ } else {
+ sdata_info(sdata,
+ "CIP Capabilities not included in association response\n");
+ ret = false;
+ goto out;
+ }
+ }
+
if (cbss->transmitted_bss) {
bss_conf->nontransmitted = true;
ether_addr_copy(bss_conf->transmitter_bssid,
@@ -6999,6 +7015,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
goto out_err;
sta->sta.spp_amsdu = assoc_data->spp_amsdu;
+ sta->sta.cip = assoc_data->cip;
if (ieee80211_vif_is_mld(&sdata->vif)) {
if (!elems->ml_basic)
@@ -10536,6 +10553,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
}
assoc_data->spp_amsdu = req->flags & ASSOC_REQ_SPP_AMSDU;
+ assoc_data->cip = req->flags & ASSOC_REQ_CIP;
if (ifmgd->auth_data && !ifmgd->auth_data->done) {
err = -EBUSY;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index ee0d32877c5b..f409f6a3afa1 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -485,6 +485,7 @@ struct ieee80211_fragment_cache {
* @link_hash_node: hash node for rhashtable
* @sta: Points to the STA info
* @gtk: group keys negotiated with this station, if any
+ * @cigtk: control integrity group keys negotiated with this station, if any
* @tx_stats: TX statistics
* @tx_stats.packets: # of packets transmitted
* @tx_stats.bytes: # of bytes in all packets transmitted
@@ -535,6 +536,7 @@ struct link_sta_info {
struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS +
NUM_DEFAULT_MGMT_KEYS +
NUM_DEFAULT_BEACON_KEYS];
+ struct ieee80211_key __rcu *cigtk[NUM_CTRL_KEYS];
struct ieee80211_sta_rx_stats __percpu *pcpu_rx_stats;
/* Updated from RX path only, no locking requirements */
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread