Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH wireless-next v3 0/7] Add support for Control Integrity Protocol (CIP)
@ 2026-09-21 12:40 Benjamin Berg
  2026-09-21 12:40 ` [PATCH wireless-next v3 1/7] wifi: mac80211: add key flags to debugfs Benjamin Berg
                   ` (6 more replies)
  0 siblings, 7 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>

This adds the mac80211 support for the new key type and using the
pairwise key for control frame protection (CFP).

v3:
- Add debugfs patches for hwsim testing
- Update for REVmf D3.0
- Configure CIP in hwsim for further interface types

v2:
- adjust rtl8723bs stating driver
- fix nl80211 attribute insertion

Benjamin Berg (6):
  wifi: mac80211: add key link_id to debugfs
  wifi: cfg80211: add Control Integrity Protocol (CIP) APIs
  wifi: mac80211: add cigtk parameter to ieee80211_gtk_rekey_add
  wifi: mac80211: add helpers to parse/generate CIP Capabilities
  wifi: mac80211: add Control Integrity Protocol handling
  wifi: mac80211_hwsim: claim support for Control Integrity Protocol

Johannes Berg (1):
  wifi: mac80211: add key flags to debugfs

 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/intel/iwlwifi/mld/d3.c   |   6 +-
 drivers/net/wireless/intel/iwlwifi/mvm/d3.c   |   6 +-
 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 +-
 drivers/net/wireless/realtek/rtw89/wow.c      |   6 +-
 .../wireless/virtual/mac80211_hwsim_main.c    |  35 +++++
 .../staging/rtl8723bs/os_dep/ioctl_cfg80211.c |   9 +-
 include/linux/ieee80211.h                     |  22 ++++
 include/net/cfg80211.h                        |  19 ++-
 include/net/mac80211.h                        |  11 +-
 include/uapi/linux/nl80211.h                  |  14 ++
 net/mac80211/cfg.c                            |  41 ++++--
 net/mac80211/debugfs_key.c                    |  18 ++-
 net/mac80211/ieee80211_i.h                    |   5 +
 net/mac80211/key.c                            |  96 ++++++++++----
 net/mac80211/key.h                            |   3 +-
 net/mac80211/mlme.c                           |  20 ++-
 net/mac80211/parse.c                          |   4 +
 net/mac80211/sta_info.h                       |   2 +
 net/mac80211/util.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 +-
 34 files changed, 532 insertions(+), 140 deletions(-)

-- 
2.55.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH wireless-next v3 1/7] wifi: mac80211: add key flags to debugfs
  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 ` Benjamin Berg
  2026-09-21 12:40 ` [PATCH wireless-next v3 2/7] wifi: mac80211: add key link_id " Benjamin Berg
                   ` (5 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, Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

Since there are two sets of key flags, one for the driver
and one internal to mac80211, add the first set in debugfs
as well. This will be used by hwsim tests to determine if
a key is pairwise or not, since the next changes will make
mac80211 use only per-STA GTKs.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 net/mac80211/debugfs_key.c | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 117f58af5ff9..79ae3552c55b 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -4,7 +4,7 @@
  * Copyright (c) 2006	Jiri Benc <jbenc@suse.cz>
  * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
  * Copyright (C) 2015	Intel Deutschland GmbH
- * Copyright (C) 2021-2023   Intel Corporation
+ * Copyright (C) 2021-2023, 2026 Intel Corporation
  */
 
 #include <linux/kobject.h>
@@ -42,9 +42,10 @@ static const struct debugfs_short_fops key_ ##name## _ops = {		\
 		 KEY_READ_##format(name)				\
 		 KEY_OPS(name)
 
-#define KEY_CONF_READ(name, format_string)				\
-	KEY_READ(conf_##name, conf.name, format_string)
-#define KEY_CONF_READ_D(name) KEY_CONF_READ(name, "%d\n")
+#define KEY_CONF_READ(name, val, format_string)				\
+	KEY_READ(conf_##name, conf.val, format_string)
+#define KEY_CONF_READ_D(name, val) KEY_CONF_READ(name, val, "%d\n")
+#define KEY_CONF_READ_X(name, val) KEY_CONF_READ(name, val, "0x%x\n")
 
 #define KEY_CONF_OPS(name)						\
 static const struct debugfs_short_fops key_ ##name## _ops = {		\
@@ -53,12 +54,16 @@ static const struct debugfs_short_fops key_ ##name## _ops = {		\
 }
 
 #define KEY_CONF_FILE(name, format)					\
-		 KEY_CONF_READ_##format(name)				\
+		 KEY_CONF_READ_##format(name, name)			\
+		 KEY_CONF_OPS(name)
+#define KEY_CONF_FILE_NAMED(name, val, format)				\
+		 KEY_CONF_READ_##format(name, val)			\
 		 KEY_CONF_OPS(name)
 
 KEY_CONF_FILE(keylen, D);
 KEY_CONF_FILE(keyidx, D);
 KEY_CONF_FILE(hw_key_idx, D);
+KEY_CONF_FILE_NAMED(conf_flags, flags, X);
 KEY_FILE(flags, X);
 KEY_READ(ifindex, sdata->name, "%s\n");
 KEY_OPS(ifindex);
@@ -356,6 +361,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
 	DEBUGFS_ADD(mic_failures);
 	DEBUGFS_ADD(key);
 	DEBUGFS_ADD(ifindex);
+	DEBUGFS_ADD(conf_flags);
 };
 
 void ieee80211_debugfs_key_remove(struct ieee80211_key *key)
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH wireless-next v3 2/7] wifi: mac80211: add key link_id to debugfs
  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 ` Benjamin Berg
  2026-09-21 12:40 ` [PATCH wireless-next v3 3/7] wifi: cfg80211: add Control Integrity Protocol (CIP) APIs Benjamin Berg
                   ` (4 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>

Add the link_id to the debugfs. This is useful for hwsim tests to
validate the per-link keys in multi-link tests.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
 net/mac80211/debugfs_key.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 79ae3552c55b..3a4f8e9e54dc 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -65,6 +65,7 @@ KEY_CONF_FILE(keyidx, D);
 KEY_CONF_FILE(hw_key_idx, D);
 KEY_CONF_FILE_NAMED(conf_flags, flags, X);
 KEY_FILE(flags, X);
+KEY_CONF_FILE(link_id, D);
 KEY_READ(ifindex, sdata->name, "%s\n");
 KEY_OPS(ifindex);
 
@@ -362,6 +363,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
 	DEBUGFS_ADD(key);
 	DEBUGFS_ADD(ifindex);
 	DEBUGFS_ADD(conf_flags);
+	DEBUGFS_ADD(link_id);
 };
 
 void ieee80211_debugfs_key_remove(struct ieee80211_key *key)
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [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 4/7] wifi: mac80211: add cigtk parameter to ieee80211_gtk_rekey_add
  2026-09-21 12:40 [PATCH wireless-next v3 0/7] Add support for Control Integrity Protocol (CIP) Benjamin Berg
                   ` (2 preceding siblings ...)
  2026-09-21 12:40 ` [PATCH wireless-next v3 3/7] wifi: cfg80211: add Control Integrity Protocol (CIP) APIs Benjamin Berg
@ 2026-09-21 12:40 ` Benjamin Berg
  2026-09-21 12:40 ` [PATCH wireless-next v3 5/7] wifi: mac80211: add helpers to parse/generate CIP Capabilities Benjamin Berg
                   ` (2 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 CIGTK keys use a key index of 0 or 1, which is not unique. As such,
a new parameter is needed to differentiate the key type. Add the
parameter to prepare for handling the CIGTK in the future.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/d3.c | 6 ++++--
 drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 6 ++++--
 drivers/net/wireless/realtek/rtw89/wow.c    | 6 ++++--
 include/net/mac80211.h                      | 3 ++-
 net/mac80211/key.c                          | 2 +-
 5 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
index 55f9b809e764..6d8ede336d59 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
@@ -1138,7 +1138,8 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif,
 		return;
 
 	key_config = ieee80211_gtk_rekey_add(vif, key_data->id, key_data->key,
-					     sizeof(key_data->key), link_id);
+					     sizeof(key_data->key), link_id,
+					     false);
 	if (IS_ERR(key_config))
 		return;
 
@@ -1220,7 +1221,8 @@ static void iwl_mld_mlo_rekey(struct iwl_mld *mld,
 				 mlo_key->idx, link_id);
 
 		key = ieee80211_gtk_rekey_add(vif, mlo_key->idx, mlo_key->key,
-					      sizeof(mlo_key->key), link_id);
+					      sizeof(mlo_key->key), link_id,
+					      false);
 
 		if (IS_ERR(key))
 			continue;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index 6b11fa32ea5c..bd52f2ebd3ba 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -1887,7 +1887,8 @@ static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status,
 
 		key = ieee80211_gtk_rekey_add(vif, status->gtk[i].id,
 					      status->gtk[i].key,
-					      sizeof(status->gtk[i].key), -1);
+					      sizeof(status->gtk[i].key), -1,
+					      false);
 		if (IS_ERR(key)) {
 			/* FW may send also the old keys */
 			if (PTR_ERR(key) == -EALREADY)
@@ -1922,7 +1923,8 @@ iwl_mvm_d3_igtk_bigtk_rekey_add(struct iwl_wowlan_status_data *status,
 		return true;
 
 	key_config = ieee80211_gtk_rekey_add(vif, keyidx, key_data->key,
-					     sizeof(key_data->key), -1);
+					     sizeof(key_data->key), -1,
+					     false);
 	if (IS_ERR(key_config)) {
 		/* FW may send also the old keys */
 		return PTR_ERR(key_config) == -EALREADY;
diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c
index 2dfa24c54e7d..2e37e1fafbef 100644
--- a/drivers/net/wireless/realtek/rtw89/wow.c
+++ b/drivers/net/wireless/realtek/rtw89/wow.c
@@ -669,10 +669,12 @@ static struct ieee80211_key_conf *rtw89_wow_gtk_rekey(struct rtw89_dev *rtwdev,
 	if (ieee80211_vif_is_mld(wow_vif))
 		key = ieee80211_gtk_rekey_add(wow_vif, keyidx, gtk,
 					      cipher_info->len,
-					      rtwvif_link->link_id);
+					      rtwvif_link->link_id,
+					      false);
 	else
 		key = ieee80211_gtk_rekey_add(wow_vif, keyidx, gtk,
-					      cipher_info->len, -1);
+					      cipher_info->len, -1,
+					      false);
 
 	kfree(rekey_conf);
 	if (IS_ERR(key)) {
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 1172d7b83ed1..c9d0e1203d1b 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -6278,6 +6278,7 @@ void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
  * @key_len: the key data. Might be bigger than the actual key length,
  *	but not smaller (for the driver convinence)
  * @link_id: the link id of the key or -1 for non-MLO
+ * @cigtk: whether this is a CIGTK
  *
  * When GTK rekeying was done while the system was suspended, (a) new
  * key(s) will be available. These will be needed by mac80211 for proper
@@ -6304,7 +6305,7 @@ void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
 struct ieee80211_key_conf *
 ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
 			u8 idx, u8 *key_data, u8 key_len,
-			int link_id);
+			int link_id, bool cigtk);
 
 /**
  * ieee80211_gtk_rekey_notify - notify userspace supplicant of rekeying
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index f45e792abede..9ac7a5c4b861 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -1366,7 +1366,7 @@ EXPORT_SYMBOL_GPL(ieee80211_set_key_rx_seq);
 struct ieee80211_key_conf *
 ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
 			u8 idx, u8 *key_data, u8 key_len,
-			int link_id)
+			int link_id, bool cigtk)
 {
 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 	struct ieee80211_local *local = sdata->local;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH wireless-next v3 5/7] wifi: mac80211: add helpers to parse/generate CIP Capabilities
  2026-09-21 12:40 [PATCH wireless-next v3 0/7] Add support for Control Integrity Protocol (CIP) Benjamin Berg
                   ` (3 preceding siblings ...)
  2026-09-21 12:40 ` [PATCH wireless-next v3 4/7] wifi: mac80211: add cigtk parameter to ieee80211_gtk_rekey_add Benjamin Berg
@ 2026-09-21 12:40 ` Benjamin Berg
  2026-09-21 12:40 ` [PATCH wireless-next v3 6/7] wifi: mac80211: add Control Integrity Protocol handling 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>

The Control Integrity Protocol (CIP) Capabilities need to be
added to and parsed from the association request and response.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>

---

v3:
- Add new bit defined in REVmf D3.0
---
 include/linux/ieee80211.h  | 22 ++++++++++++++++++++++
 net/mac80211/ieee80211_i.h |  3 +++
 net/mac80211/parse.c       |  4 ++++
 net/mac80211/util.c        | 19 +++++++++++++++++++
 4 files changed, 48 insertions(+)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 7fbc51f873ff..73ac5336a17a 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1828,6 +1828,7 @@ enum ieee80211_eid_ext {
 	WLAN_EID_EXT_BANDWIDTH_INDICATION = 135,
 	WLAN_EID_EXT_KNOWN_STA_IDENTIFCATION = 136,
 	WLAN_EID_EXT_NON_AP_STA_REG_CON = 137,
+	WLAN_EID_EXT_CIP_CAPA = 150,
 	WLAN_EID_EXT_UHR_OPER = 151,
 	WLAN_EID_EXT_UHR_CAPA = 152,
 	WLAN_EID_EXT_MACP = 153,
@@ -2899,4 +2900,25 @@ static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim,
 		     __ieee80211_check_tim(tim, tim_len, aid);
 }
 
+/**
+ * enum ieee80211_cip_cap_fields - CIP Capabilities element fields
+ * @IEEE80211_CIP_CAP_MIC_PADDING: The MIC padding subfield in the CIP
+ *	capabilities
+ * @IEEE80211_CIP_CAP_PROTECTED_CTRL_FRAME_ONLY: The MIC Padding For Protected
+ *	Control Frames Only bit in CIP capabilities
+ */
+enum ieee80211_cip_cap_fields {
+	IEEE80211_CIP_CAP_MIC_PADDING			= 0x0F,
+	IEEE80211_CIP_CAP_PROTECTED_CTRL_FRAME_ONLY	= 0x10,
+};
+
+/**
+ * struct ieee80211_cip_cap - CIP Capabilities element
+ *
+ * @v: The value of the Control Integrity Protocol Capability element
+ */
+struct ieee80211_cip_cap {
+	u8 v;
+} __packed;
+
 #endif /* LINUX_IEEE80211_H */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 9514f01778be..674a4676a972 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1878,6 +1878,7 @@ struct ieee802_11_elems {
 	const struct ieee80211_ttlm_elem *ttlm[IEEE80211_TTLM_MAX_CNT];
 	const struct ieee80211_uhr_cap *uhr_cap;
 	const struct ieee80211_uhr_operation *uhr_operation;
+	const struct ieee80211_cip_cap *cip_cap;
 
 	/* not the order in the psd values is per element, not per chandef */
 	struct ieee80211_parsed_tpe tpe;
@@ -2778,6 +2779,8 @@ int ieee80211_put_uhr_cap(struct sk_buff *skb,
 			  const struct ieee80211_supported_band *sband);
 void ieee80211_put_reg_conn(struct ieee80211_sub_if_data *sdata,
 			    struct sk_buff *skb);
+int ieee80211_put_cip_cap(struct sk_buff *skb,
+			  struct ieee80211_sub_if_data *sdata);
 
 /* channel management */
 bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index cb2be167cde7..3b66ca7d7a5c 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -205,6 +205,10 @@ ieee80211_parse_extension_element(u32 *crc,
 			elems->ttlm_num++;
 		}
 		break;
+	case WLAN_EID_EXT_CIP_CAPA:
+		if (len >= sizeof(*elems->cip_cap))
+			elems->cip_cap = data;
+		break;
 	case WLAN_EID_EXT_UHR_OPER:
 		if (params->mode < IEEE80211_CONN_MODE_UHR)
 			break;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index e2b5b2b21c34..c193a75ef06e 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -4680,6 +4680,25 @@ int ieee80211_put_uhr_cap(struct sk_buff *skb,
 	return 0;
 }
 
+int ieee80211_put_cip_cap(struct sk_buff *skb,
+			  struct ieee80211_sub_if_data *sdata)
+{
+	const struct wiphy_iftype_ext_capab *ift_ext_capa =
+		cfg80211_get_iftype_ext_capa(sdata->local->hw.wiphy,
+					     ieee80211_vif_type_p2p(&sdata->vif));
+	u8 cip_capabilities = ift_ext_capa ? ift_ext_capa->cip_capabilities : 0;
+
+	if (skb_tailroom(skb) < 4)
+		return -ENOBUFS;
+
+	skb_put_u8(skb, WLAN_EID_EXTENSION);
+	skb_put_u8(skb, 2);
+	skb_put_u8(skb, WLAN_EID_EXT_CIP_CAPA);
+	skb_put_u8(skb, cip_capabilities);
+
+	return 0;
+}
+
 const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode)
 {
 	static const char * const modes[] = {
-- 
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,
 					&params->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

* [PATCH wireless-next v3 7/7] wifi: mac80211_hwsim: claim support for Control Integrity Protocol
  2026-09-21 12:40 [PATCH wireless-next v3 0/7] Add support for Control Integrity Protocol (CIP) Benjamin Berg
                   ` (5 preceding siblings ...)
  2026-09-21 12:40 ` [PATCH wireless-next v3 6/7] wifi: mac80211: add Control Integrity Protocol handling Benjamin Berg
@ 2026-09-21 12:40 ` 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>

The mac80211_hwsim driver never actually generates control frames. As
such, it can simply claim the Control Integrity protocol (CIP).

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>

---

v3:
- Enable CIP for more interface types (requires extended capabilities)
---
 .../wireless/virtual/mac80211_hwsim_main.c    | 35 +++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 1a12261f3e8e..0cf9ef8959e1 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -5645,6 +5645,14 @@ static void mac80211_hwsim_sband_capab(struct ieee80211_supported_band *sband)
 	 BIT(NL80211_IFTYPE_MESH_POINT) | \
 	 BIT(NL80211_IFTYPE_OCB))
 
+static const u8 iftypes_ext_capa[] = {
+	 [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
+	 [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
+	 [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF |
+	       WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB,
+	 [8] = WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB,
+};
+
 static const u8 iftypes_ext_capa_ap[] = {
 	 [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
 	 [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
@@ -5669,6 +5677,33 @@ static const struct wiphy_iftype_ext_capab mac80211_hwsim_iftypes_ext_capa[] = {
 		.eml_capabilities = IEEE80211_EML_CAP_EMLSR_SUPP |
 				    IEEE80211_EML_CAP_EMLMR_SUPPORT,
 		.mld_capa_and_ops = MAC80211_HWSIM_MLD_CAPA_OPS,
+		/* CIP works as hwsim does not have control frames */
+		.cip_supported = true,
+		.cip_capabilities = 0,
+	},
+	{
+		.iftype = NL80211_IFTYPE_STATION,
+		.extended_capabilities = iftypes_ext_capa,
+		.extended_capabilities_mask = iftypes_ext_capa,
+		.extended_capabilities_len = sizeof(iftypes_ext_capa),
+		.cip_supported = true,
+		.cip_capabilities = 0,
+	},
+	{
+		.iftype = NL80211_IFTYPE_P2P_CLIENT,
+		.extended_capabilities = iftypes_ext_capa,
+		.extended_capabilities_mask = iftypes_ext_capa,
+		.extended_capabilities_len = sizeof(iftypes_ext_capa),
+		.cip_supported = true,
+		.cip_capabilities = 0,
+	},
+	{
+		.iftype = NL80211_IFTYPE_P2P_GO,
+		.extended_capabilities = iftypes_ext_capa,
+		.extended_capabilities_mask = iftypes_ext_capa,
+		.extended_capabilities_len = sizeof(iftypes_ext_capa),
+		.cip_supported = true,
+		.cip_capabilities = 0,
 	},
 };
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-09-21 12:41 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH wireless-next v3 3/7] wifi: cfg80211: add Control Integrity Protocol (CIP) APIs 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
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 ` [PATCH wireless-next v3 6/7] wifi: mac80211: add Control Integrity Protocol handling Benjamin Berg
2026-09-21 12:40 ` [PATCH wireless-next v3 7/7] wifi: mac80211_hwsim: claim support for Control Integrity Protocol Benjamin Berg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox