From 01c15a8b936d319f3daf7ffaddfe44ca5bd40504 Mon Sep 17 00:00:00 2001 From: Manish Dharanenthiran Date: Wed, 5 Aug 2026 14:56:02 +0530 Subject: wifi: nl80211: add support to configure 6 GHz non-HT duplicate transmission As per IEEE Std 802.11-2024, subclause 26.17.2.2, a 6 GHz AP can transmit a Beacon frame using a non-HT duplicate PPDU, so that stations scanning only PSC channels can discover it. Currently, there is no mechanism to request the driver to transmit non-HT duplicate Beacon frames. In [1], changes were made to parse the Beacon from user-space to set the non-HT duplicate flag in the kernel. However, it was suggested that non-HT transmission be part of the Beacon TX rate settings. Hence, add NL80211_TXRATE_6GHZ_NON_HT_DUP as a flag attribute under NL80211_ATTR_TX_RATES to let user-space request the driver to transmit non-HT duplicate Beacons. Also add validation to reject the Beacon if non-HT duplicate Beacon transmission is enabled when legacy rates are not configured. [1] https://lore.kernel.org/all/1644914581-21682-1-git-send-email-quic_ramess@quicinc.com/ Signed-off-by: Manish Dharanenthiran Link: https://patch.msgid.link/20260805-dup-beacon-v1-1-68574869c896@oss.qualcomm.com Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 020387d76412..d8fefefc11a9 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -5900,6 +5900,9 @@ enum nl80211_key_attributes { * see &struct nl80211_txrate_eht * @NL80211_TXRATE_EHT_GI: configure EHT GI, (u8, see &enum nl80211_eht_gi) * @NL80211_TXRATE_EHT_LTF: configure EHT LTF, (u8, see &enum nl80211_eht_ltf) + * @NL80211_TXRATE_6GHZ_NON_HT_DUP: configure 6 GHz non-HT duplicate Beacon + * transmission. This flag is applicable only in Beacon TX rate setting + * and must be accompanied by a non-HT (legacy) Beacon rate. * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ @@ -5915,6 +5918,7 @@ enum nl80211_tx_rate_attributes { NL80211_TXRATE_EHT, NL80211_TXRATE_EHT_GI, NL80211_TXRATE_EHT_LTF, + NL80211_TXRATE_6GHZ_NON_HT_DUP, /* keep last */ __NL80211_TXRATE_AFTER_LAST, -- cgit v1.2.3 From 4e2554eab15a6ef6030008a411c3208e9cc54a1a Mon Sep 17 00:00:00 2001 From: Hemanth Selam Date: Fri, 4 Sep 2026 16:11:05 +0530 Subject: mii: fix typo "asymetric" in comment Correct "asymetric" to "asymmetric", reported by scripts/checkpatch.pl using the misspelling list in scripts/spelling.txt. Only touches comments, no code changes. Signed-off-by: Hemanth Selam Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260904104107.12832-3-hemanth.selam@gmail.com Signed-off-by: Jakub Kicinski --- include/uapi/linux/mii.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/mii.h b/include/uapi/linux/mii.h index 61d6edad4b94..d662c10ee282 100644 --- a/include/uapi/linux/mii.h +++ b/include/uapi/linux/mii.h @@ -81,7 +81,7 @@ #define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */ #define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ #define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */ -#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */ +#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymmetric pause */ #define ADVERTISE_XNP 0x1000 /* Extended Next Page */ #define ADVERTISE_RESV ADVERTISE_XNP /* Used to be reserved */ #define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ -- cgit v1.2.3 From bc6fe9d301d59fe5cb236d646abe792bffae5fa6 Mon Sep 17 00:00:00 2001 From: Hemanth Selam Date: Fri, 4 Sep 2026 18:06:37 +0530 Subject: atm: fix typo "protcol" in comment Correct "protcol" to "protocol", reported by scripts/checkpatch.pl using the misspelling list in scripts/spelling.txt. Only touches comments, no code changes. Signed-off-by: Hemanth Selam Link: https://patch.msgid.link/20260904123638.39273-1-hemanth.selam@gmail.com Signed-off-by: Jakub Kicinski --- include/uapi/linux/atm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/atm.h b/include/uapi/linux/atm.h index 95ebdcf4fe88..7a3789f39f52 100644 --- a/include/uapi/linux/atm.h +++ b/include/uapi/linux/atm.h @@ -39,7 +39,7 @@ #define ATM_MAX_VCI 65535 /* maximum VCI */ -/* "protcol" values for the socket system call */ +/* "protocol" values for the socket system call */ #define ATM_NO_AAL 0 /* AAL not specified */ #define ATM_AAL0 13 /* "raw" ATM cells */ #define ATM_AAL1 1 /* AAL1 (CBR) */ -- cgit v1.2.3 From fd0291fad9e763c42ea908e89589c94f1a06eea5 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 4 Sep 2026 12:04:10 -0700 Subject: netlink: specs: handshake: do not accept the handler-class sentinel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "max" is the exclusive upper bound of enum handshake_handler_class, not a class a handler can ask for, but the spec lists it as a plain entry. _init_checks() derives the policy limit from the highest entry, so the generated policy came out as NLA_POLICY_MAX(NLA_U32, 2) and handshake_nl_accept_doit() takes class 2 all the way into handshake_req_next(), which walks hn_requests under hn_lock before returning -EAGAIN instead of the -EINVAL a bad class deserves. render-max is how YNL spells this, and unlike a hand written limit it stays correct when a second handler class is added. It does change what HANDSHAKE_HANDLER_CLASS_MAX means - the highest valid class rather than one past it, as in every other Netlink/YNL family - with the count left as __HANDSHAKE_HANDLER_CLASS_MAX. The constant is uAPI, but I could not find any user space user, this constant seems to have been added for kernel's benefit. I think the risk of changing this is worth taking, having MAX with different semantics than the rest of Netlink is very confusing. Acked-by: Chuck Lever Reviewed-by: Asbjørn Sloth Tønnesen Link: https://patch.msgid.link/20260904190410.3864660-2-kuba@kernel.org Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/handshake.yaml | 3 ++- include/uapi/linux/handshake.h | 5 ++++- net/handshake/genl.c | 2 +- net/handshake/handshake-test.c | 2 +- net/handshake/request.c | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/handshake.yaml b/Documentation/netlink/specs/handshake.yaml index 9ab46da04c49..0498ada2dd97 100644 --- a/Documentation/netlink/specs/handshake.yaml +++ b/Documentation/netlink/specs/handshake.yaml @@ -22,7 +22,8 @@ definitions: type: enum name: handler-class value-start: 0 - entries: [none, tlshd, max] + entries: [none, tlshd] + render-max: true - type: enum name: msg-type diff --git a/include/uapi/linux/handshake.h b/include/uapi/linux/handshake.h index d7e40f594888..f907aa082520 100644 --- a/include/uapi/linux/handshake.h +++ b/include/uapi/linux/handshake.h @@ -13,7 +13,10 @@ enum handshake_handler_class { HANDSHAKE_HANDLER_CLASS_NONE, HANDSHAKE_HANDLER_CLASS_TLSHD, - HANDSHAKE_HANDLER_CLASS_MAX, + + /* private: */ + __HANDSHAKE_HANDLER_CLASS_MAX, + HANDSHAKE_HANDLER_CLASS_MAX = (__HANDSHAKE_HANDLER_CLASS_MAX - 1) }; enum handshake_msg_type { diff --git a/net/handshake/genl.c b/net/handshake/genl.c index 58606c2a4600..26e3efec64ca 100644 --- a/net/handshake/genl.c +++ b/net/handshake/genl.c @@ -14,7 +14,7 @@ /* HANDSHAKE_CMD_ACCEPT - do */ static const struct nla_policy handshake_accept_nl_policy[HANDSHAKE_A_ACCEPT_HANDLER_CLASS + 1] = { - [HANDSHAKE_A_ACCEPT_HANDLER_CLASS] = NLA_POLICY_MAX(NLA_U32, 2), + [HANDSHAKE_A_ACCEPT_HANDLER_CLASS] = NLA_POLICY_MAX(NLA_U32, 1), }; /* HANDSHAKE_CMD_DONE - do */ diff --git a/net/handshake/handshake-test.c b/net/handshake/handshake-test.c index 3dd507470d5f..4b99acd9f9c7 100644 --- a/net/handshake/handshake-test.c +++ b/net/handshake/handshake-test.c @@ -42,7 +42,7 @@ static struct handshake_proto handshake_req_alloc_proto_2 = { }; static struct handshake_proto handshake_req_alloc_proto_3 = { - .hp_handler_class = HANDSHAKE_HANDLER_CLASS_MAX, + .hp_handler_class = __HANDSHAKE_HANDLER_CLASS_MAX, }; static struct handshake_proto handshake_req_alloc_proto_4 = { diff --git a/net/handshake/request.c b/net/handshake/request.c index cd30d54d0501..ff739ee81cb8 100644 --- a/net/handshake/request.c +++ b/net/handshake/request.c @@ -115,7 +115,7 @@ struct handshake_req *handshake_req_alloc(const struct handshake_proto *proto, return NULL; if (proto->hp_handler_class <= HANDSHAKE_HANDLER_CLASS_NONE) return NULL; - if (proto->hp_handler_class >= HANDSHAKE_HANDLER_CLASS_MAX) + if (proto->hp_handler_class > HANDSHAKE_HANDLER_CLASS_MAX) return NULL; if (!proto->hp_accept || !proto->hp_done) return NULL; -- cgit v1.2.3 From f2109e90a5aaedb10b4ac7927ec074820a452d57 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 10 Sep 2026 13:10:20 -0400 Subject: net: rtnetlink: add pacing_offload attribute to net_device Add a new flag to administratively control pacing offload. The feature is disabled by default. That does not cause regressions, as no driver advertises max_pacing_offload_horizon yet. Also add NLA_REJECT for max_pacing_offload_horizon, in line with other such read-only members of link-attrs. Both fields can be read with ynl --family rt-link --do getlink \ --json '{"ifname": "eth0"}' | grep pacing And pacing offload enabled with ynl --family rt-link --do setlink \ --json '{"ifname": "eth0", "pacing-offload": 1}' Signed-off-by: Willem de Bruijn Link: https://patch.msgid.link/20260910171131.2532487-2-willemdebruijn.kernel@gmail.com Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/rt-link.yaml | 5 +++++ .../networking/net_cachelines/net_device.rst | 1 + include/linux/netdevice.h | 2 ++ include/uapi/linux/if_link.h | 1 + net/core/rtnetlink.c | 22 ++++++++++++++++++++++ 5 files changed, 31 insertions(+) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index 5217d0bb4a3a..7a72cd1b7e1e 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -1091,6 +1091,10 @@ attribute-sets: - name: tailroom type: u16 + - + name: pacing-offload + type: u32 + doc: Enable EDT pacing offload (0 - disabled, 1 - enabled). - name: prop-list-link-attrs subset-of: link-attrs @@ -2559,6 +2563,7 @@ operations: - devlink-port - gso-ipv4-max-size - gro-ipv4-max-size + - pacing-offload dump: request: value: 18 diff --git a/Documentation/networking/net_cachelines/net_device.rst b/Documentation/networking/net_cachelines/net_device.rst index 512f6d6fa3d8..8eceaa80b686 100644 --- a/Documentation/networking/net_cachelines/net_device.rst +++ b/Documentation/networking/net_cachelines/net_device.rst @@ -11,6 +11,7 @@ Type Name fastpath_tx_acce unsigned_long:32 priv_flags read_mostly __dev_queue_xmit(tx) unsigned_long:1 lltx read_mostly HARD_TX_LOCK,HARD_TX_TRYLOCK,HARD_TX_UNLOCK(tx) unsigned_long:2 netmem_tx:2; read_mostly +unsigned_long:1 pacing_offload read_mostly sch_fq char name[16] struct netdev_name_node* name_node struct dev_ifalias* ifalias diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 707b2e51c2b9..1f0710eef185 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1840,6 +1840,7 @@ enum netdev_reg_state { * drivers. Mainly used by logical interfaces, such as * bonding and tunnels * @netmem_tx: device netmem TX mode + * @pacing_offload: enable EDT pacing offload. * * @name: This is the first field of the "visible" part of this structure * (i.e. as seen by users in the "Space.c" file). It is the name @@ -2170,6 +2171,7 @@ struct net_device { unsigned long priv_flags:32; unsigned long lltx:1; unsigned long netmem_tx:2; + unsigned long pacing_offload:1; ); const struct net_device_ops *netdev_ops; const struct header_ops *header_ops; diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 43cecca49f01..245b36204525 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -381,6 +381,7 @@ enum { IFLA_NETNS_IMMUTABLE, IFLA_HEADROOM, IFLA_TAILROOM, + IFLA_PACING_OFFLOAD, __IFLA_MAX }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index be9d1625bac3..e3444fd24061 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1396,6 +1396,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + rtnl_devlink_port_size(dev) + rtnl_dpll_pin_size() + nla_total_size(8) /* IFLA_MAX_PACING_OFFLOAD_HORIZON */ + + nla_total_size(4) /* IFLA_PACING_OFFLOAD */ + nla_total_size(2) /* IFLA_HEADROOM */ + nla_total_size(2) /* IFLA_TAILROOM */ + rtnl_dev_parent_size(dev) @@ -2176,6 +2177,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, READ_ONCE(dev->tso_max_segs)) || nla_put_uint(skb, IFLA_MAX_PACING_OFFLOAD_HORIZON, READ_ONCE(dev->max_pacing_offload_horizon)) || + nla_put_u32(skb, IFLA_PACING_OFFLOAD, + dev->pacing_offload) || #ifdef CONFIG_RPS nla_put_u32(skb, IFLA_NUM_RX_QUEUES, READ_ONCE(dev->num_rx_queues)) || @@ -2349,9 +2352,11 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_ALLMULTI] = { .type = NLA_REJECT }, [IFLA_GSO_IPV4_MAX_SIZE] = NLA_POLICY_MIN(NLA_U32, MAX_TCP_HEADER + 1), [IFLA_GRO_IPV4_MAX_SIZE] = { .type = NLA_U32 }, + [IFLA_MAX_PACING_OFFLOAD_HORIZON] = { .type = NLA_REJECT }, [IFLA_NETNS_IMMUTABLE] = { .type = NLA_REJECT }, [IFLA_HEADROOM] = { .type = NLA_REJECT }, [IFLA_TAILROOM] = { .type = NLA_REJECT }, + [IFLA_PACING_OFFLOAD] = NLA_POLICY_MAX(NLA_U32, 1), }; static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { @@ -2825,6 +2830,14 @@ static int validate_linkmsg(struct net_device *dev, struct nlattr *tb[], return -EINVAL; } + if (tb[IFLA_PACING_OFFLOAD]) { + if (nla_get_u32(tb[IFLA_PACING_OFFLOAD]) && + !dev->max_pacing_offload_horizon) { + NL_SET_ERR_MSG(extack, "pacing offload not supported by device"); + return -EOPNOTSUPP; + } + } + if (tb[IFLA_AF_SPEC]) { struct nlattr *af; int rem, err; @@ -3342,6 +3355,15 @@ static int do_setlink(const struct sk_buff *skb, struct net_device *dev, } } + if (tb[IFLA_PACING_OFFLOAD]) { + bool val = nla_get_u32(tb[IFLA_PACING_OFFLOAD]); + + if (dev->pacing_offload != val) { + dev->pacing_offload = val; + status |= DO_SETLINK_MODIFIED; + } + } + if (tb[IFLA_OPERSTATE]) set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE])); -- cgit v1.2.3 From c924efeda80aa098599815319c57d6c937ad24f5 Mon Sep 17 00:00:00 2001 From: Carella Chen Date: Fri, 11 Sep 2026 14:56:52 +0800 Subject: wifi: nl80211: add fast roam offload extended feature Drivers can offload FT or OKC roaming without necessarily advertising full 802.1X 4-way handshake offload. Add an extended feature bit for that capability and allow PMK configuration when it is present. This gives userspace a way to provide PMK or PMK-R0 material needed by fast roaming firmware implementations while keeping the existing 4-way handshake offload capability unchanged. Assisted-by: GitHub-Copilot-CLI:gpt-5.5 Signed-off-by: Carella Chen Signed-off-by: Jason Huang Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260911065656.1269623-2-Jason.Huang2@infineon.com Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 5 +++++ net/wireless/nl80211.c | 10 ++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index d8fefefc11a9..32553cacf988 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -7114,6 +7114,10 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_PROBE_AP: Driver supports probing the associated AP * in STA mode using @NL80211_CMD_PROBE_PEER. * + * @NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD: Driver supports fast roaming + * offload in station mode, including Fast Transition or Opportunistic + * Key Caching. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -7196,6 +7200,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_ROC_ADDR_FILTER, NL80211_EXT_FEATURE_SET_KEY_LTF_SEED, NL80211_EXT_FEATURE_PROBE_AP, + NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e585da1d79c9..b081eeaf7016 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -18447,6 +18447,7 @@ static int nl80211_set_multicast_to_unicast(struct sk_buff *skb, static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wiphy *wiphy = &rdev->wiphy; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_pmk_conf pmk_conf = {}; @@ -18455,7 +18456,9 @@ static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - if (!wiphy_ext_feature_isset(&rdev->wiphy, + if (!wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD) && + !wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) return -EOPNOTSUPP; @@ -18485,6 +18488,7 @@ static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wiphy *wiphy = &rdev->wiphy; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; const u8 *aa; @@ -18493,7 +18497,9 @@ static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info) wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - if (!wiphy_ext_feature_isset(&rdev->wiphy, + if (!wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD) && + !wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) return -EOPNOTSUPP; -- cgit v1.2.3 From b785f5c56fb3dbe70635592b11547a2c828e95b3 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 12 Sep 2026 13:24:01 -0700 Subject: netlink: policy: report the big endian attributes Paolo pointed out an issue flagged at low priority by Sashiko - we're currently not handling BE{16,32} attributes in policy dumps. Commit 3f4285d741b4 ("netlink: specs: fou: local-v4 and peer-v4 are big endian") flipped two fou attributes from NLA_U32 to NLA_BE32. This made them vanish from the policy dump. Follow the YAML spec format and treat byte order as a property of a u16 / u32 rather than a type of its own. I don't have a strong preference either way. The YNL format "feels cleaner" but the kernel's separate type is easier when handling decoding. I don't think that the policy type is actually usable for decoding (since it only contains input types) so I went with YNL and added the separate attr. A missing byte order means host order, again like in the YAML specs. Link: https://lore.kernel.org/ab90f970-0ebb-4c07-b7b1-db3f91395116@redhat.com Link: https://patch.msgid.link/20260912202401.141336-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/nlctrl.yaml | 15 +++++++++++++++ include/uapi/linux/netlink.h | 14 ++++++++++++++ net/netlink/policy.c | 16 ++++++++++++++-- tools/net/ynl/Makefile.deps | 3 ++- tools/net/ynl/pyynl/lib/ynl.py | 8 +++++++- 5 files changed, 52 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/nlctrl.yaml b/Documentation/netlink/specs/nlctrl.yaml index 8b4472a6aa36..7e7c158e3e73 100644 --- a/Documentation/netlink/specs/nlctrl.yaml +++ b/Documentation/netlink/specs/nlctrl.yaml @@ -42,6 +42,16 @@ definitions: - bitfield32 - sint - uint + - + name: policy-byte-order + doc: | + Byte order of an integer attribute. Zero is left unused so that it + can be taken to mean host byte order. + enum-name: netlink-policy-byte-order + type: enum + value-start: 1 + entries: + - big-endian attribute-sets: - @@ -152,6 +162,11 @@ attribute-sets: - name: pad type: pad + - + name: byte-order + doc: Byte order of the attribute, absent means host byte order. + type: u32 + enum: policy-byte-order - name: op-policy-attrs name-prefix: ctrl-attr-policy- diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h index f87aaf28a649..82c41aeb4357 100644 --- a/include/uapi/linux/netlink.h +++ b/include/uapi/linux/netlink.h @@ -329,6 +329,17 @@ enum netlink_attribute_type { NL_ATTR_TYPE_UINT, }; +/** + * enum netlink_policy_byte_order - byte order of an integer attribute + * @NL_POLICY_BYTE_ORDER_BIG_ENDIAN: big endian (network byte order) + * + * Zero is left unassigned so that it keeps meaning host byte order, + * which is also what a missing byte order means. + */ +enum netlink_policy_byte_order { + NL_POLICY_BYTE_ORDER_BIG_ENDIAN = 1, +}; + /** * enum netlink_policy_type_attr - policy type attributes * @NL_POLICY_TYPE_ATTR_UNSPEC: unused @@ -356,6 +367,8 @@ enum netlink_attribute_type { * bitfield32 type (U32) * @NL_POLICY_TYPE_ATTR_MASK: mask of valid bits for unsigned integers (U64) * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment + * @NL_POLICY_TYPE_ATTR_BYTE_ORDER: byte order of an integer attribute, + * &enum netlink_policy_byte_order, absent if host byte order (U32) * * @__NL_POLICY_TYPE_ATTR_MAX: number of attributes * @NL_POLICY_TYPE_ATTR_MAX: highest attribute number @@ -374,6 +387,7 @@ enum netlink_policy_type_attr { NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, NL_POLICY_TYPE_ATTR_PAD, NL_POLICY_TYPE_ATTR_MASK, + NL_POLICY_TYPE_ATTR_BYTE_ORDER, /* keep last */ __NL_POLICY_TYPE_ATTR_MAX, diff --git a/net/netlink/policy.c b/net/netlink/policy.c index 08b006c48f06..574d44b7d518 100644 --- a/net/netlink/policy.c +++ b/net/netlink/policy.c @@ -234,6 +234,11 @@ int netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt) /* maximum is common, u64 min/max with padding */ return common + 2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64))); + case NLA_BE16: + case NLA_BE32: + /* same as the unsigned types, plus the byte order */ + return common + nla_attr_size(sizeof(u32)) + + 2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64))); case NLA_BITFIELD32: return common + nla_attr_size(sizeof(u32)); case NLA_STRING: @@ -289,21 +294,28 @@ __netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state, case NLA_U16: case NLA_U32: case NLA_U64: + case NLA_BE16: + case NLA_BE32: case NLA_UINT: case NLA_MSECS: { struct netlink_range_validation range; if (pt->type == NLA_U8) type = NL_ATTR_TYPE_U8; - else if (pt->type == NLA_U16) + else if (pt->type == NLA_U16 || pt->type == NLA_BE16) type = NL_ATTR_TYPE_U16; - else if (pt->type == NLA_U32) + else if (pt->type == NLA_U32 || pt->type == NLA_BE32) type = NL_ATTR_TYPE_U32; else if (pt->type == NLA_U64) type = NL_ATTR_TYPE_U64; else type = NL_ATTR_TYPE_UINT; + if ((pt->type == NLA_BE16 || pt->type == NLA_BE32) && + nla_put_u32(skb, NL_POLICY_TYPE_ATTR_BYTE_ORDER, + NL_POLICY_BYTE_ORDER_BIG_ENDIAN)) + goto nla_put_failure; + if (pt->validation_type == NLA_VALIDATE_MASK) { if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MASK, pt->mask, diff --git a/tools/net/ynl/Makefile.deps b/tools/net/ynl/Makefile.deps index 2771375339d9..1e746e25e2bc 100644 --- a/tools/net/ynl/Makefile.deps +++ b/tools/net/ynl/Makefile.deps @@ -30,7 +30,8 @@ CFLAGS_mptcp_pm:=$(call get_hdr_inc,_LINUX_MPTCP_PM_H,mptcp_pm.h) CFLAGS_net_shaper:=$(call get_hdr_inc,_LINUX_NET_SHAPER_H,net_shaper.h) CFLAGS_netdev:=$(call get_hdr_inc,_LINUX_NETDEV_H,netdev.h) CFLAGS_nl80211:=$(call get_hdr_inc,__LINUX_NL802121_H,nl80211.h) -CFLAGS_nlctrl:=$(call get_hdr_inc,__LINUX_GENERIC_NETLINK_H,genetlink.h) +CFLAGS_nlctrl:=$(call get_hdr_inc,__LINUX_GENERIC_NETLINK_H,genetlink.h) \ + $(call get_hdr_inc,__LINUX_NETLINK_H,netlink.h) CFLAGS_nfsd:=$(call get_hdr_inc,_LINUX_NFSD_NETLINK_H,nfsd_netlink.h) CFLAGS_ovpn:=$(call get_hdr_inc,_LINUX_OVPN_H,ovpn.h) CFLAGS_ovs_datapath:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h) diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py index 8682bf588e1f..fb5acb8acded 100644 --- a/tools/net/ynl/pyynl/lib/ynl.py +++ b/tools/net/ynl/pyynl/lib/ynl.py @@ -115,6 +115,7 @@ class Netlink: NL_POLICY_TYPE_ATTR_BITFIELD32_MASK = 10 NL_POLICY_TYPE_ATTR_PAD = 11 NL_POLICY_TYPE_ATTR_MASK = 12 + NL_POLICY_TYPE_ATTR_BYTE_ORDER = 13 AttrType = Enum('AttrType', ['flag', 'u8', 'u16', 'u32', 'u64', 's8', 's16', 's32', 's64', @@ -122,6 +123,8 @@ class Netlink: 'nested', 'nested-array', 'bitfield32', 'sint', 'uint']) + ByteOrder = Enum('ByteOrder', ['big-endian'], start=1) + class NlError(Exception): def __init__(self, nl_msg): self.nl_msg = nl_msg @@ -158,7 +161,7 @@ class NlPolicy: Each policy entry always has a 'type' attribute (e.g. u32, string, nested). Optional attributes depending on the 'type': min-value, - max-value, min-length, max-length, mask. + max-value, min-length, max-length, mask, byte-order. Policies can form infinite nesting loops. These loops are trimmed when policy is converted to a dict with pol.to_dict(). @@ -454,6 +457,9 @@ def _genl_decode_policy(raw): policy['bitfield32-mask'] = attr.as_scalar('u32') elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_MASK: policy['mask'] = attr.as_scalar('u64') + elif attr.type == Netlink.NL_POLICY_TYPE_ATTR_BYTE_ORDER: + byte_order = attr.as_scalar('u32') + policy['byte-order'] = Netlink.ByteOrder(byte_order).name return policy -- cgit v1.2.3 From de5f5ca3d3bfcdcc38c10eb9246eca3687cd2261 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 15 Sep 2026 15:19:31 +0200 Subject: wifi: cfg80211: add attribute for TX/RX denoting there is no station For MLD stations, userspace may need to explicitly transmit a frame to a specific link address. In that case, it needs to ensure that no address translation happens. In the reverse case of an RX, userspace may need to know the link address for a frame. By passing the information whether a STA is known for the frame, userspace knows whether link translation happened and can do the reverse lookup when needed. This is important for flows where a STA is still registered but the connection has been lost and it is returning again. Signed-off-by: Benjamin Berg Link: https://patch.msgid.link/20260915151925.f12af80b1972.I4cfd3db83ba3dc0267575f919ba613ea5d10abd5@changeid Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ include/uapi/linux/nl80211.h | 7 +++++++ net/wireless/nl80211.c | 8 +++++++- 3 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 6461a7bd0169..9aa7d2d4a184 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4009,6 +4009,7 @@ struct cfg80211_update_ft_ies_params { * @link_id: for MLO, the link ID to transmit on, -1 if not given; note * that the link ID isn't validated (much), it's in range but the * link might not exist (or be used by the receiver STA) + * @no_sta: set if the frame should not be transmitted using an existing STA */ struct cfg80211_mgmt_tx_params { struct ieee80211_channel *chan; @@ -4021,6 +4022,7 @@ struct cfg80211_mgmt_tx_params { int n_csa_offsets; const u16 *csa_offsets; int link_id; + bool no_sta; }; /** @@ -9501,6 +9503,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, * @flags: flags, as defined in &enum nl80211_rxmgmt_flags * @rx_tstamp: Hardware timestamp of frame RX in nanoseconds * @ack_tstamp: Hardware timestamp of ack TX in nanoseconds + * @no_sta: set if no station is known for the frame (relevant for MLD) */ struct cfg80211_rx_info { int freq; @@ -9512,6 +9515,7 @@ struct cfg80211_rx_info { u32 flags; u64 rx_tstamp; u64 ack_tstamp; + bool no_sta; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 32553cacf988..510102febc26 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3185,6 +3185,11 @@ enum nl80211_commands { * The aggregated message always precedes the per-link messages for the * same station within a dump sequence. * + * @NL80211_ATTR_FRAME_NO_STA: Valid for @NL80211_CMD_FRAME to denote that + * the kernel had no station for a received frame or should not use a + * known station to transmit a frame. This is relevant to know whether + * MLD address translation happened or to disable it when sending a frame. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3785,6 +3790,8 @@ enum nl80211_attrs { NL80211_ATTR_STA_DUMP_LINK_STATS, + NL80211_ATTR_FRAME_NO_STA, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b081eeaf7016..e77a4c81d031 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1097,6 +1097,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_NPCA_PUNCT_BITMAP] = 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 }, }; /* policy for the key attributes */ @@ -14873,6 +14874,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) !(wdev->valid_links & BIT(params.link_id))) return -EINVAL; + params.no_sta = + nla_get_flag(info->attrs[NL80211_ATTR_FRAME_NO_STA]); + params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]); @@ -22042,7 +22046,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, (info->ack_tstamp && nla_put_u64_64bit(msg, NL80211_ATTR_TX_HW_TIMESTAMP, info->ack_tstamp, - NL80211_ATTR_PAD))) + NL80211_ATTR_PAD)) || + (info->no_sta && + nla_put_flag(msg, NL80211_ATTR_FRAME_NO_STA))) goto nla_put_failure; genlmsg_end(msg, hdr); -- cgit v1.2.3 From 8050e0efd4cf6276605bc3cfa60ebd543896fecf Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 15 Sep 2026 19:16:08 -0700 Subject: netlink: specs: netdev: update doc for xsk tx-checksum and zc-max-segs Minor touch ups to the XSK-related doc strings in the netdev spec. Tx checksum is (obviously?) Layer 4 (TCP/UDP), not Layer 3 (IP). The xdp_zc_max_segs change is more subtle, I guess, but to kernel devs saying "frags" may sound like the number of skb frags. The value includes the head in this case. The default of 1 means single buffer, XDP_USE_SG sockets are refused when it is 1. Let's use the word "buffer" instead of "frag". Link: https://patch.msgid.link/20260916021608.1702731-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/netdev.yaml | 5 +++-- include/uapi/linux/netdev.h | 2 +- tools/include/uapi/linux/netdev.h | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml index 3e3f03bd5c29..e13461cf3fc3 100644 --- a/Documentation/netlink/specs/netdev.yaml +++ b/Documentation/netlink/specs/netdev.yaml @@ -80,7 +80,7 @@ definitions: - name: tx-checksum doc: >- - L3 checksum HW offload is supported by the driver. + L4 checksum HW offload is supported by the driver. - name: tx-launch-time-fifo doc: >- @@ -118,7 +118,8 @@ attribute-sets: enum: xdp-act - name: xdp-zc-max-segs - doc: max fragment count supported by ZC driver + doc: max buffer count per packet supported by ZC driver, + including the head buffer type: u32 checks: min: 1 diff --git a/include/uapi/linux/netdev.h b/include/uapi/linux/netdev.h index 35ff083221c7..9026e0414fbe 100644 --- a/include/uapi/linux/netdev.h +++ b/include/uapi/linux/netdev.h @@ -58,7 +58,7 @@ enum netdev_xdp_rx_metadata { * enum netdev_xsk_flags * @NETDEV_XSK_FLAGS_TX_TIMESTAMP: HW timestamping egress packets is supported * by the driver. - * @NETDEV_XSK_FLAGS_TX_CHECKSUM: L3 checksum HW offload is supported by the + * @NETDEV_XSK_FLAGS_TX_CHECKSUM: L4 checksum HW offload is supported by the * driver. * @NETDEV_XSK_FLAGS_TX_LAUNCH_TIME_FIFO: Launch time HW offload is supported * by the driver. diff --git a/tools/include/uapi/linux/netdev.h b/tools/include/uapi/linux/netdev.h index 35ff083221c7..9026e0414fbe 100644 --- a/tools/include/uapi/linux/netdev.h +++ b/tools/include/uapi/linux/netdev.h @@ -58,7 +58,7 @@ enum netdev_xdp_rx_metadata { * enum netdev_xsk_flags * @NETDEV_XSK_FLAGS_TX_TIMESTAMP: HW timestamping egress packets is supported * by the driver. - * @NETDEV_XSK_FLAGS_TX_CHECKSUM: L3 checksum HW offload is supported by the + * @NETDEV_XSK_FLAGS_TX_CHECKSUM: L4 checksum HW offload is supported by the * driver. * @NETDEV_XSK_FLAGS_TX_LAUNCH_TIME_FIFO: Launch time HW offload is supported * by the driver. -- cgit v1.2.3 From 73cdc6018f4947c1c29fc274d78edff6223336d1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 Sep 2026 12:06:20 +0200 Subject: wifi: nl80211: rename no-ACK bitmap to TID bitmap Rename the NL80211_ATTR_NOACK_MAP attribute to be called NL80211_ATTR_TID_BITMAP, allowing it to be reused in more contexts. The interpretation stays for the only command using it now (NL80211_CMD_SET_NOACK_MAP.) Signed-off-by: Johannes Berg Reviewed-by: Jeff Johnson Link: https://patch.msgid.link/20260916120620.823bfa05f5f5.Ibb334016129512921b0aa5236ee4c670d83d0f95@changeid Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 11 ++++++----- net/wireless/nl80211.c | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 510102febc26..9a2ccb8d66b8 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -937,8 +937,8 @@ * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME * messages. Note that per PHY only one application may register. * - * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether - * No Acknowledgement Policy should be applied. + * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap (%NL80211_ATTR_TID_BITMAP) + * indicating for which TIDs No Acknowledgment Policy should be applied. * * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels * independently of the userspace SME, send this event indicating @@ -2288,8 +2288,8 @@ enum nl80211_commands { * abides to when initiating radiation on DFS channels. A country maps * to one DFS region. * - * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of - * up to 16 TIDs. + * @NL80211_ATTR_TID_BITMAP: A TID bitmap (u16) whose meaning depends + * on the command. * * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be * used by the drivers which has MLME in firmware and does not have support @@ -3434,7 +3434,7 @@ enum nl80211_attrs { NL80211_ATTR_DISABLE_HT, NL80211_ATTR_HT_CAPABILITY_MASK, - NL80211_ATTR_NOACK_MAP, + NL80211_ATTR_TID_BITMAP, NL80211_ATTR_INACTIVITY_TIMEOUT, @@ -3807,6 +3807,7 @@ enum nl80211_attrs { #define NL80211_ATTR_CSA_C_OFF_BEACON NL80211_ATTR_CNTDWN_OFFS_BEACON #define NL80211_ATTR_CSA_C_OFF_PRESP NL80211_ATTR_CNTDWN_OFFS_PRESP #define NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS NL80211_ATTR_EXT_MLD_CAPA_AND_OPS +#define NL80211_ATTR_NOACK_MAP NL80211_ATTR_TID_BITMAP /* * Allow user space programs to use #ifdef on new attributes by defining them diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e77a4c81d031..ab5a14f5fe28 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -877,7 +877,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_HT_CAPABILITY_MASK] = { .len = NL80211_HT_CAPABILITY_LEN }, - [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 }, + [NL80211_ATTR_TID_BITMAP] = { .type = NLA_U16 }, [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, @@ -5282,13 +5282,13 @@ static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info) struct net_device *dev = info->user_ptr[1]; u16 noack_map; - if (!info->attrs[NL80211_ATTR_NOACK_MAP]) + if (!info->attrs[NL80211_ATTR_TID_BITMAP]) return -EINVAL; if (!rdev->ops->set_noack_map) return -EOPNOTSUPP; - noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]); + noack_map = nla_get_u16(info->attrs[NL80211_ATTR_TID_BITMAP]); return rdev_set_noack_map(rdev, dev, noack_map); } -- cgit v1.2.3 From 863da457b407f84beb8d60f8d13384e2b13e2b13 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 24 Sep 2026 16:12:11 -0700 Subject: netlink: specs: dpll: drop the reference to DPLL_MODE_DETACHED The lock-status doc tells userspace that DPLL_LOCK_STATUS_UNLOCKED can be forced by setting DPLL_A_MODE to DPLL_MODE_DETACHED. But no such value has been defined so far. enum dpll_mode has only MANUAL and AUTOMATIC. Let's make sure the doc reflects current reality. Reviewed-by: Ivan Vecera Link: https://patch.msgid.link/20260924231211.568213-4-kuba@kernel.org Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/dpll.yaml | 3 +-- include/uapi/linux/dpll.h | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml index 7009a64473b1..8fa789a50273 100644 --- a/Documentation/netlink/specs/dpll.yaml +++ b/Documentation/netlink/specs/dpll.yaml @@ -31,8 +31,7 @@ definitions: - name: unlocked doc: | - dpll was not yet locked to any valid input (or forced by setting - DPLL_A_MODE to DPLL_MODE_DETACHED) + dpll was not yet locked to any valid input value: 1 - name: locked diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h index 85b898b1db5e..8c71fc3f9863 100644 --- a/include/uapi/linux/dpll.h +++ b/include/uapi/linux/dpll.h @@ -29,8 +29,7 @@ enum dpll_mode { /** * enum dpll_lock_status - provides information of dpll device lock status, * valid values for DPLL_A_LOCK_STATUS attribute - * @DPLL_LOCK_STATUS_UNLOCKED: dpll was not yet locked to any valid input (or - * forced by setting DPLL_A_MODE to DPLL_MODE_DETACHED) + * @DPLL_LOCK_STATUS_UNLOCKED: dpll was not yet locked to any valid input * @DPLL_LOCK_STATUS_LOCKED: dpll is locked to a valid signal, but no holdover * available * @DPLL_LOCK_STATUS_LOCKED_HO_ACQ: dpll is locked and holdover acquired -- cgit v1.2.3 From 46da6029bf468ce3c426cb8b4abf96976ed9d4c8 Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Thu, 10 Sep 2026 12:54:13 +0200 Subject: netfilter: conntrack: make filtering by zone discoverable Conntrack flush supports filtering by zone using CTA_ZONE, but this attribute is really hard to use from user space applications. The reason is that it is not possible to tell if it's supported or not. Older kernels silently ignore CTA_ZONE. And in that case they just happily flush all the entries from all zones breaking all the existing connections. So, applications have to infer support from the kernel version. While it works in most cases, it's not a particularly reliable or desired way to check kernel capabilities from applications that aim to be portable. There should be a better way to probe or discover features in the kernel. The CTA_FILTER interface on the other hand is simple enough to probe. We can check for NLM_F_DUMP_FILTERED in the dump to see if filtering is supported. And unknown sub-attributes in CTA_FILTER are rejected explicitly since strict validation is in use there. Let's add new CTA_FILTER_ZONE that signals that CTA_ZONE should be filtered on. It is a flag, since everything in the CTA_FILTER is a bit mask, i.e., a form of a flag. If set, it means that CTA_ZONE must be present and be used for filtering. If the flag is not set however, the filtering on CTA_ZONE will still take place to ensure backwards compatibility. So, the flag doesn't really change the filtering behavior, but it allows user space applications to properly discover support for CTA_ZONE filtering without need to rely on kernel version parsing or risk accidental flushes of the entire conntrack table, and also without modifying the kernel state. A new test variant is added to test with and without the new flag. Since the setup code is moved into a shared function, expectations replaced with assertions to bail early if the base setup fails to avoid the cascade of secondary failures that can be misleading. Error return is only for the SKIP cases. Signed-off-by: Ilya Maximets Signed-off-by: Pablo Neira Ayuso --- Documentation/netlink/specs/conntrack.yaml | 6 + include/uapi/linux/netfilter/nfnetlink_conntrack.h | 1 + net/netfilter/nf_conntrack_netlink.c | 11 ++ .../selftests/net/netfilter/conntrack_dump_flush.c | 145 ++++++++++++++------- 4 files changed, 117 insertions(+), 46 deletions(-) (limited to 'include/uapi/linux') diff --git a/Documentation/netlink/specs/conntrack.yaml b/Documentation/netlink/specs/conntrack.yaml index 1d163130241a..56c13705243b 100644 --- a/Documentation/netlink/specs/conntrack.yaml +++ b/Documentation/netlink/specs/conntrack.yaml @@ -393,6 +393,12 @@ attribute-sets: name: reply-flags type: u32 doc: bitmask of tuple fields to filter on, reply direction + - + name: zone + type: flag + doc: >- + Filter on conntrack zone id; requires the top-level zone + (``CTA_ZONE``) attribute. - name: conntrack-attrs attributes: diff --git a/include/uapi/linux/netfilter/nfnetlink_conntrack.h b/include/uapi/linux/netfilter/nfnetlink_conntrack.h index 43233af75b9d..985f9c08d3e9 100644 --- a/include/uapi/linux/netfilter/nfnetlink_conntrack.h +++ b/include/uapi/linux/netfilter/nfnetlink_conntrack.h @@ -285,6 +285,7 @@ enum ctattr_filter { CTA_FILTER_UNSPEC, CTA_FILTER_ORIG_FLAGS, CTA_FILTER_REPLY_FLAGS, + CTA_FILTER_ZONE, __CTA_FILTER_MAX }; #define CTA_FILTER_MAX (__CTA_FILTER_MAX - 1) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index e8477f792c95..4b6abe36028e 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -911,6 +911,7 @@ struct ctnetlink_filter { static const struct nla_policy cta_filter_nla_policy[CTA_FILTER_MAX + 1] = { [CTA_FILTER_ORIG_FLAGS] = NLA_POLICY_MASK(NLA_U32, CTA_FILTER_F_ALL), [CTA_FILTER_REPLY_FLAGS] = NLA_POLICY_MASK(NLA_U32, CTA_FILTER_F_ALL), + [CTA_FILTER_ZONE] = { .type = NLA_FLAG }, }; static int ctnetlink_parse_filter(const struct nlattr *attr, @@ -930,6 +931,9 @@ static int ctnetlink_parse_filter(const struct nlattr *attr, if (tb[CTA_FILTER_REPLY_FLAGS]) filter->reply_flags = nla_get_u32(tb[CTA_FILTER_REPLY_FLAGS]); + if (tb[CTA_FILTER_ZONE]) + filter->zone_filter = true; + return 0; } @@ -1006,6 +1010,7 @@ ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family) if (err) goto err_filter; + /* CTA_ZONE is allowed without CTA_FILTER_ZONE. */ if (cda[CTA_ZONE]) { err = ctnetlink_parse_zone(cda[CTA_ZONE], &filter->zone); if (err < 0) @@ -1020,6 +1025,12 @@ ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family) if (err < 0) goto err_filter; + /* CTA_FILTER_ZONE cannot be set without CTA_ZONE. */ + if (filter->zone_filter && !cda[CTA_ZONE]) { + err = -EINVAL; + goto err_filter; + } + if (filter->orig_flags) { if (!cda[CTA_TUPLE_ORIG]) { err = -EINVAL; diff --git a/tools/testing/selftests/net/netfilter/conntrack_dump_flush.c b/tools/testing/selftests/net/netfilter/conntrack_dump_flush.c index 31b8250ddc53..0c777323f4ba 100644 --- a/tools/testing/selftests/net/netfilter/conntrack_dump_flush.c +++ b/tools/testing/selftests/net/netfilter/conntrack_dump_flush.c @@ -215,7 +215,22 @@ static int count_entries(const struct nlmsghdr *nlh, void *data) return MNL_CB_OK; } -static int conntrack_count_zone(struct mnl_socket *sock, uint16_t zone) +static void put_zone_attr(struct nlmsghdr *nlh, uint16_t zone, + bool use_cta_filter) +{ + struct nlattr *nest; + + mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone)); + + if (use_cta_filter) { + nest = mnl_attr_nest_start(nlh, CTA_FILTER); + mnl_attr_put(nlh, CTA_FILTER_ZONE, 0, NULL); + mnl_attr_nest_end(nlh, nest); + } +} + +static int conntrack_count_zone(struct mnl_socket *sock, uint16_t zone, + bool use_cta_filter) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; @@ -235,7 +250,7 @@ static int conntrack_count_zone(struct mnl_socket *sock, uint16_t zone) nfh->version = NFNETLINK_V0; nfh->res_id = 0; - mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone)); + put_zone_attr(nlh, zone, use_cta_filter); ret = mnl_socket_sendto(sock, nlh, nlh->nlmsg_len); if (ret < 0) { @@ -261,7 +276,8 @@ static int conntrack_count_zone(struct mnl_socket *sock, uint16_t zone) return reply_counter; } -static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone) +static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone, + bool use_cta_filter) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; @@ -281,7 +297,7 @@ static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone) nfh->version = NFNETLINK_V0; nfh->res_id = 0; - mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone)); + put_zone_attr(nlh, zone, use_cta_filter); ret = mnl_socket_sendto(sock, nlh, nlh->nlmsg_len); if (ret < 0) { @@ -304,43 +320,40 @@ static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone) return 0; } -FIXTURE(conntrack_dump_flush) -{ - struct mnl_socket *sock; -}; - -FIXTURE_SETUP(conntrack_dump_flush) +static int conntrack_zone_setup(struct __test_metadata *_metadata, + struct mnl_socket **sock) { struct in6_addr src, dst; int ret; - self->sock = mnl_socket_open(NETLINK_NETFILTER); - if (!self->sock) { + *sock = mnl_socket_open(NETLINK_NETFILTER); + if (!*sock) { perror("mnl_socket_open"); - SKIP(return, "cannot open netlink_netfilter socket"); + SKIP(return -1, "cannot open netlink_netfilter socket"); } - ret = mnl_socket_bind(self->sock, 0, MNL_SOCKET_AUTOPID); - EXPECT_EQ(ret, 0); + ret = mnl_socket_bind(*sock, 0, MNL_SOCKET_AUTOPID); + ASSERT_EQ(ret, 0); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID); + ret = conntrack_count_zone(*sock, TEST_ZONE_ID, false); if (ret < 0 && errno == EPERM) - SKIP(return, "Needs to be run as root"); + SKIP(return -1, "Needs to be run as root"); else if (ret < 0 && errno == EOPNOTSUPP) - SKIP(return, "Kernel does not seem to support conntrack zones"); + SKIP(return -1, + "Kernel does not seem to support conntrack zones"); - ret = conntrack_data_generate_v4(self->sock, 0xf0f0f0f0, 0xf1f1f1f1, + ret = conntrack_data_generate_v4(*sock, 0xf0f0f0f0, 0xf1f1f1f1, TEST_ZONE_ID); - EXPECT_EQ(ret, 0); - ret = conntrack_data_generate_v4(self->sock, 0xf2f2f2f2, 0xf3f3f3f3, + ASSERT_EQ(ret, 0); + ret = conntrack_data_generate_v4(*sock, 0xf2f2f2f2, 0xf3f3f3f3, TEST_ZONE_ID + 1); - EXPECT_EQ(ret, 0); - ret = conntrack_data_generate_v4(self->sock, 0xf4f4f4f4, 0xf5f5f5f5, + ASSERT_EQ(ret, 0); + ret = conntrack_data_generate_v4(*sock, 0xf4f4f4f4, 0xf5f5f5f5, TEST_ZONE_ID + 2); - EXPECT_EQ(ret, 0); - ret = conntrack_data_generate_v4(self->sock, 0xf6f6f6f6, 0xf7f7f7f7, + ASSERT_EQ(ret, 0); + ret = conntrack_data_generate_v4(*sock, 0xf6f6f6f6, 0xf7f7f7f7, NF_CT_DEFAULT_ZONE_ID); - EXPECT_EQ(ret, 0); + ASSERT_EQ(ret, 0); src = (struct in6_addr) {{ .__u6_addr32 = { @@ -358,9 +371,9 @@ FIXTURE_SETUP(conntrack_dump_flush) 0x02000000 } }}; - ret = conntrack_data_generate_v6(self->sock, src, dst, + ret = conntrack_data_generate_v6(*sock, src, dst, TEST_ZONE_ID); - EXPECT_EQ(ret, 0); + ASSERT_EQ(ret, 0); src = (struct in6_addr) {{ .__u6_addr32 = { 0xb80d0120, @@ -377,9 +390,9 @@ FIXTURE_SETUP(conntrack_dump_flush) 0x04000000 } }}; - ret = conntrack_data_generate_v6(self->sock, src, dst, + ret = conntrack_data_generate_v6(*sock, src, dst, TEST_ZONE_ID + 1); - EXPECT_EQ(ret, 0); + ASSERT_EQ(ret, 0); src = (struct in6_addr) {{ .__u6_addr32 = { 0xb80d0120, @@ -396,9 +409,9 @@ FIXTURE_SETUP(conntrack_dump_flush) 0x06000000 } }}; - ret = conntrack_data_generate_v6(self->sock, src, dst, + ret = conntrack_data_generate_v6(*sock, src, dst, TEST_ZONE_ID + 2); - EXPECT_EQ(ret, 0); + ASSERT_EQ(ret, 0); src = (struct in6_addr) {{ .__u6_addr32 = { @@ -416,14 +429,51 @@ FIXTURE_SETUP(conntrack_dump_flush) 0x08000000 } }}; - ret = conntrack_data_generate_v6(self->sock, src, dst, + ret = conntrack_data_generate_v6(*sock, src, dst, NF_CT_DEFAULT_ZONE_ID); - EXPECT_EQ(ret, 0); + ASSERT_EQ(ret, 0); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID); + return 0; +} + +FIXTURE(conntrack_dump_flush) +{ + struct mnl_socket *sock; +}; + +FIXTURE_VARIANT(conntrack_dump_flush) +{ + bool use_cta_filter; +}; + +FIXTURE_VARIANT_ADD(conntrack_dump_flush, cta_zone) +{ + .use_cta_filter = false, +}; + +FIXTURE_VARIANT_ADD(conntrack_dump_flush, cta_filter) +{ + .use_cta_filter = true, +}; + +FIXTURE_SETUP(conntrack_dump_flush) +{ + int ret; + + if (conntrack_zone_setup(_metadata, &self->sock)) + return; + + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID, false); EXPECT_GE(ret, 2); if (ret > 2) SKIP(return, "kernel does not support filtering by zone"); + + if (variant->use_cta_filter) { + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID, true); + if (ret < 0 && errno == EINVAL) + SKIP(return, "kernel does not support CTA_FILTER_ZONE"); + ASSERT_GE(ret, 0); + } } FIXTURE_TEARDOWN(conntrack_dump_flush) @@ -434,39 +484,42 @@ TEST_F(conntrack_dump_flush, test_dump_by_zone) { int ret; - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID, + variant->use_cta_filter); EXPECT_EQ(ret, 2); } TEST_F(conntrack_dump_flush, test_flush_by_zone) { + bool filter = variant->use_cta_filter; int ret; - ret = conntrack_flush_zone(self->sock, TEST_ZONE_ID); + ret = conntrack_flush_zone(self->sock, TEST_ZONE_ID, filter); EXPECT_EQ(ret, 0); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID, filter); EXPECT_EQ(ret, 0); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 1); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 1, filter); EXPECT_EQ(ret, 2); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 2); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 2, filter); EXPECT_EQ(ret, 2); - ret = conntrack_count_zone(self->sock, NF_CT_DEFAULT_ZONE_ID); + ret = conntrack_count_zone(self->sock, NF_CT_DEFAULT_ZONE_ID, filter); EXPECT_EQ(ret, 2); } TEST_F(conntrack_dump_flush, test_flush_by_zone_default) { + bool filter = variant->use_cta_filter; int ret; - ret = conntrack_flush_zone(self->sock, NF_CT_DEFAULT_ZONE_ID); + ret = conntrack_flush_zone(self->sock, NF_CT_DEFAULT_ZONE_ID, filter); EXPECT_EQ(ret, 0); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID, filter); EXPECT_EQ(ret, 2); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 1); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 1, filter); EXPECT_EQ(ret, 2); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 2); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 2, filter); EXPECT_EQ(ret, 2); - ret = conntrack_count_zone(self->sock, NF_CT_DEFAULT_ZONE_ID); + ret = conntrack_count_zone(self->sock, NF_CT_DEFAULT_ZONE_ID, filter); EXPECT_EQ(ret, 0); } -- cgit v1.2.3 From 3191dfbf0779337292526bacb53ac442650099da Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 21 Sep 2026 14:40:52 +0200 Subject: wifi: cfg80211: add Control Integrity Protocol (CIP) APIs 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 Link: https://patch.msgid.link/20260921144048.aa7939493375.I49c0be0a1b05d625cc307e9bdd13c32a6c18667f@changeid Signed-off-by: Johannes Berg --- 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 +- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 12 +- drivers/net/wireless/microchip/wilc1000/cfg80211.c | 13 ++- drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 11 +- drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 8 +- drivers/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(-) (limited to 'include/uapi/linux') 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 @@ done: 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 @@ done: 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 d7f20ec0bb3e..e6e7739c4a3b 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 9ad21d41e999..3db290875ff4 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 86304ea3331c..8805439c0b4b 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 @@ exit: } 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 @@ addkey_end: } 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 a1753335eb9d..4c3a432b44ce 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -615,12 +615,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; @@ -741,10 +743,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) { @@ -799,8 +803,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; @@ -808,7 +812,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; @@ -818,7 +822,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)) @@ -837,7 +842,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 6138d207caf4..de23518451ca 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -453,13 +453,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 7f6779d326b8..81dff0bace28 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -176,7 +176,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 85c010793c53..ef241b6da245 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 */ @@ -9298,6 +9318,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]); @@ -9507,6 +9543,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]); @@ -9584,7 +9636,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: @@ -9617,6 +9669,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 @@ -13339,6 +13395,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]) { @@ -19216,6 +19279,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 adcfd0278da3..0d4bbe0e5c5d 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 f2464d2ce0d5..9d5456defc90 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) { @@ -1190,7 +1220,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; /* -- cgit v1.2.3 From 025ef5f4f63b5ecf38bb8f4eb80941c0d55c943f Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Mon, 21 Sep 2026 22:00:22 +0300 Subject: wifi: cfg80211: add NL80211_CMD_NAN_SET_NON_EVAC_CHANNELS Add a new NL80211_CMD_NAN_SET_NON_EVAC_CHANNELS command that lets user space mark a set of NAN channels as non-evacuable. The command carries the channels as nested NL80211_ATTR_NAN_CHANNEL attributes (chandef only) and replaces the entire set of non-evacuable channels; channels not included in the list become evacuable again. All provided channels must already exist in the current local schedule, otherwise -ENOENT is returned. The command is rejected with -EBUSY while a schedule update is pending. This prevents the kernel from evacuating channels that carry NDC or immutable schedules, so evacuating these channels would break existing NDP connections. Fixes: 154b0296c0ec ("wifi: nl80211: Add a notification to notify NAN channel evacuation") Signed-off-by: Andrei Otcheretianski Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260921215936.2efc32011e73.Ice8839058e114f6f9783079807df446388187dda@changeid Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 24 +++++++++++++++ include/uapi/linux/nl80211.h | 31 ++++++++++++++++++++ net/wireless/core.c | 35 ++++++++++++++++++++++ net/wireless/core.h | 4 +++ net/wireless/nl80211.c | 69 ++++++++++++++++++++++++++++++++++++++++++++ net/wireless/rdev-ops.h | 17 +++++++++++ net/wireless/trace.h | 25 ++++++++++++++++ 7 files changed, 205 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1ef5f85a9376..0b3fcb2a9576 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4264,6 +4264,21 @@ struct cfg80211_nan_local_sched { struct cfg80211_nan_channel nan_channels[] __counted_by(n_channels); }; +/** + * struct cfg80211_nan_non_evac_channels - NAN non-evacuable channels + * + * This struct defines the set of NAN local schedule channels that must not + * be evacuated for concurrent operations. + * + * @n_channels: number of channel definitions in %chandefs. + * @chandefs: array of channel definitions that must not be evacuated. Each + * must match a channel of the current local schedule. + */ +struct cfg80211_nan_non_evac_channels { + u8 n_channels; + struct cfg80211_chan_def chandefs[] __counted_by(n_channels); +}; + /** * struct cfg80211_nan_peer_map - NAN peer schedule map * @@ -5212,6 +5227,12 @@ struct mgmt_frame_regs { * schedule, the full new schedule is provided - partial updates are not * supported, and the new schedule completely replaces the previous one. * + * @nan_set_non_evac_channels: set the list of local schedule channels that + * must not be evacuated for concurrent operations. The provided list + * replaces the previous set; channels of the current schedule that are + * not included become evacuable again. All provided channels are + * guaranteed by cfg80211 to belong to the current local schedule. + * * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS * * @get_txq_stats: Get TXQ stats for interface or phy. If wdev is %NULL, this @@ -5601,6 +5622,9 @@ struct cfg80211_ops { int (*nan_set_peer_sched)(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_nan_peer_sched *sched); + int (*nan_set_non_evac_channels)(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_nan_non_evac_channels *channels); int (*set_multicast_to_unicast)(struct wiphy *wiphy, struct net_device *dev, const bool enabled); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 843d1dbb725d..be9e2842a736 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1383,6 +1383,13 @@ * from the device to perform an announced schedule update. See * %NL80211_ATTR_NAN_SCHED_DEFERRED for more details. * If not set, the schedule should be applied immediately. + * Setting a new schedule is always allowed and is never treated as an + * evacuation, even if it removes channels that were previously marked as + * non-evacuable with %NL80211_CMD_NAN_SET_NON_EVAC_CHANNELS. The + * non-evacuable marking is a per-channel property of the schedule: + * channels that remain in the new schedule keep their marking, channels + * that are removed simply lose it, and newly added channels are + * evacuable by default. * @NL80211_CMD_NAN_SCHED_UPDATE_DONE: Event sent to user space to notify that * a deferred local NAN schedule update (requested with * %NL80211_CMD_NAN_SET_LOCAL_SCHED and %NL80211_ATTR_NAN_SCHED_DEFERRED) @@ -1427,6 +1434,28 @@ * @NL80211_CMD_STOP_PD: Stop the PD operation, identified by * its %NL80211_ATTR_WDEV interface. * + * @NL80211_CMD_NAN_SET_NON_EVAC_CHANNELS: Set the list of NAN local schedule + * channels that must not be evacuated. NAN must be operational + * (%NL80211_CMD_START_NAN was executed) and a local schedule must have + * been set (%NL80211_CMD_NAN_SET_LOCAL_SCHED). The command carries zero + * or more nested %NL80211_ATTR_NAN_CHANNEL attributes, each identifying a + * channel (by its channel definition) of the current local schedule that + * must not be evacuated for concurrent operations. The provided list + * replaces the previous set of non-evacuable channels; channels of the + * current schedule that are not included become evacuable again. All + * provided channels must exist in the current local schedule, otherwise + * the command fails. This is used to protect channels carrying NDC or + * immutable schedules, whose evacuation would break existing NDP + * connections. + * The non-evacuable marking is a per-channel property of the current + * local schedule and only affects evacuation for concurrent operations; + * it does not prevent the schedule itself from being changed. Removing a + * channel from the schedule with %NL80211_CMD_NAN_SET_LOCAL_SCHED is a + * user-initiated change, not an evacuation, and is allowed even for a + * non-evacuable channel. Across a schedule update, channels that remain + * keep their non-evacuable marking, removed channels lose it, and newly + * added channels are evacuable by default; issue this command again to + * change the non-evacuable set. * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1705,6 +1734,8 @@ enum nl80211_commands { NL80211_CMD_START_PD, NL80211_CMD_STOP_PD, + NL80211_CMD_NAN_SET_NON_EVAC_CHANNELS, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/net/wireless/core.c b/net/wireless/core.c index cde3ca85494d..3ac3b2a67a9c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -348,6 +348,41 @@ int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev, return 0; } +int cfg80211_nan_set_non_evac_channels(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_nan_non_evac_channels *channels) +{ + lockdep_assert_held(&rdev->wiphy.mtx); + + if (wdev->iftype != NL80211_IFTYPE_NAN || !wdev_running(wdev)) + return -EINVAL; + + /* + * Don't allow updating the non-evacuable channels while a deferred + * schedule update is pending, as the set of channels may still change. + */ + if (wdev->u.nan.sched_update_pending) + return -EBUSY; + + /* All provided channels must belong to the current local schedule. */ + for (int i = 0; i < channels->n_channels; i++) { + bool found = false; + + for (int j = 0; j < wdev->u.nan.n_channels; j++) { + if (cfg80211_chandef_identical(&wdev->u.nan.chandefs[j], + &channels->chandefs[i])) { + found = true; + break; + } + } + + if (!found) + return -ENOENT; + } + + return rdev_nan_set_non_evac_channels(rdev, wdev, channels); +} + void cfg80211_stop_pd(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { diff --git a/net/wireless/core.h b/net/wireless/core.h index de23518451ca..58445c09026a 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -578,6 +578,10 @@ int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct cfg80211_nan_local_sched *sched); +int cfg80211_nan_set_non_evac_channels(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_nan_non_evac_channels *channels); + struct cfg80211_internal_bss * cfg80211_bss_update(struct cfg80211_registered_device *rdev, struct cfg80211_internal_bss *tmp, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ef241b6da245..91e26b93a523 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -17845,6 +17845,69 @@ static int nl80211_nan_set_local_sched(struct sk_buff *skb, return cfg80211_nan_set_local_schedule(rdev, wdev, sched); } +static int +nl80211_parse_non_evac_channel(struct cfg80211_registered_device *rdev, + struct nlattr *channel, struct genl_info *info, + struct cfg80211_chan_def *chandef) +{ + struct nlattr **channel_parsed __free(kfree) = + kcalloc(NL80211_ATTR_MAX + 1, sizeof(*channel_parsed), + GFP_KERNEL); + int ret; + + if (!channel_parsed) + return -ENOMEM; + + ret = nla_parse_nested(channel_parsed, NL80211_ATTR_MAX, channel, NULL, + info->extack); + if (ret) + return ret; + + return nl80211_parse_chandef(rdev, info->extack, channel_parsed, + chandef, false); +} + +static int nl80211_nan_set_non_evac_channels(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = info->user_ptr[1]; + int rem, i = 0, n_channels = 0; + struct nlattr *channel; + + if (wdev->iftype != NL80211_IFTYPE_NAN) + return -EOPNOTSUPP; + + if (!wdev_running(wdev)) + return -ENOTCONN; + + /* Count how many channel attributes we got */ + nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL, + info->nlhdr, GENL_HDRLEN, rem) + n_channels++; + + struct cfg80211_nan_non_evac_channels *channels __free(kfree) = + kzalloc(struct_size(channels, chandefs, n_channels), + GFP_KERNEL); + if (!channels) + return -ENOMEM; + + channels->n_channels = n_channels; + + nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL, + info->nlhdr, GENL_HDRLEN, rem) { + int ret; + + ret = nl80211_parse_non_evac_channel(rdev, channel, info, + &channels->chandefs[i]); + if (ret) + return ret; + i++; + } + + return cfg80211_nan_set_non_evac_channels(rdev, wdev, channels); +} + static int nl80211_get_protocol_features(struct sk_buff *skb, struct genl_info *info) { @@ -20724,6 +20787,12 @@ static const struct genl_small_ops nl80211_small_ops[] = { .flags = GENL_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), }, + { + .cmd = NL80211_CMD_NAN_SET_NON_EVAC_CHANNELS, + .doit = nl80211_nan_set_non_evac_channels, + .flags = GENL_ADMIN_PERM, + .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), + }, }; static struct genl_family nl80211_fam __ro_after_init = { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 0d4bbe0e5c5d..1f1311c20e94 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1097,6 +1097,23 @@ rdev_nan_set_peer_sched(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_nan_set_non_evac_channels(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_nan_non_evac_channels *channels) +{ + int ret; + + trace_rdev_nan_set_non_evac_channels(&rdev->wiphy, wdev, channels); + if (rdev->ops->nan_set_non_evac_channels) + ret = rdev->ops->nan_set_non_evac_channels(&rdev->wiphy, wdev, + channels); + else + ret = -EOPNOTSUPP; + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline int rdev_start_pd(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { diff --git a/net/wireless/trace.h b/net/wireless/trace.h index c210ca1e44e2..83007a824010 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2486,6 +2486,31 @@ TRACE_EVENT(rdev_nan_set_peer_sched, ) ); +TRACE_EVENT(rdev_nan_set_non_evac_channels, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_nan_non_evac_channels *channels), + TP_ARGS(wiphy, wdev, channels), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __field(u8, n_channels) + __dynamic_array(u32, freqs, channels->n_channels) + ), + TP_fast_assign( + u32 *freqs = __get_dynamic_array(freqs); + + WIPHY_ASSIGN; + WDEV_ASSIGN; + __entry->n_channels = channels->n_channels; + for (int i = 0; i < channels->n_channels; i++) + freqs[i] = channels->chandefs[i].chan->center_freq; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", n_channels: %u, freqs: %s", + WIPHY_PR_ARG, WDEV_PR_ARG, __entry->n_channels, + __print_array(__get_dynamic_array(freqs), + __entry->n_channels, sizeof(u32))) +); + TRACE_EVENT(rdev_set_mac_acl, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_acl_data *params), -- cgit v1.2.3 From 55ab5eccd1a5ec98416b611c5aa6fdb4c9b51062 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 22 Sep 2026 14:01:51 +0300 Subject: wifi: nl80211: allow a NAN peer schedule with 2 channels in the same slot A NAN peer that has dual radio can schedule a slot to 2 different channels. Until now, for single radio devices, we disallowed such a peer schedule. This was done so because the assumption was that the user space needs to pick one of the channels for us, according to its logic. But for devices that have their ULW logic offloaded, it can be useful if we program it with the full information and let it decide what to do. For example: - peer has slot S scheduled to C1 and C2 - local schedule has S scheduled to C1 - in practice, the device is on C2 at S, for example due to non-NAN activity In that case, this extra information can actually be useful to the device, and bring to more efficient usage of the time. Allow a peer schedule that has a single slot scheduled to two different channels. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260922140131.6c3e3f552bda.I056644aaeea3f96605272651c4108e9ff224caf1@changeid Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 10 ++++++---- net/wireless/nl80211.c | 23 ++--------------------- 2 files changed, 8 insertions(+), 25 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index be9e2842a736..75d4c5d6a7a3 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1406,10 +1406,12 @@ * be provided. * Each peer channel must be compatible with at least one local channel * set by %NL80211_CMD_SET_LOCAL_NAN_SCHED. Different maps must not - * contain compatible channels. - * For single-radio devices (n_radio <= 1), different maps must not - * schedule the same time slot, as the device cannot operate on multiple - * channels simultaneously. + * contain compatible channels. Two maps may schedule the same time slot. + * The device decides at runtime which of the channels to follow. + * For example, the local schedule may place slot S on channel c1 while + * the peer advertises slot S on both c1 and c2. If the device is anyway + * on c2 during slot S (e.g. via the ULW mechanism for non-NAN activity), + * it may use slot S on c2 to communicate with the peer. * When updating an existing peer schedule, the full new schedule must be * provided - partial updates are not supported. The new schedule will * completely replace the previous one. diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 58d336498125..16c63383923b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -17566,8 +17566,7 @@ nl80211_parse_nan_peer_map(struct genl_info *info, struct nlattr *map_attr, map->schedule, n_channels); } -static int nl80211_nan_validate_map_pair(struct wiphy *wiphy, - struct genl_info *info, +static int nl80211_nan_validate_map_pair(struct genl_info *info, const struct cfg80211_nan_peer_map *map1, const struct cfg80211_nan_peer_map *map2, struct cfg80211_nan_channel *nan_channels) @@ -17602,24 +17601,6 @@ static int nl80211_nan_validate_map_pair(struct wiphy *wiphy, } } - /* - * Check for conflicting time slots between maps. - * Only check for single-radio devices (n_radio <= 1) which cannot - * operate on multiple channels simultaneously. - */ - if (wiphy->n_radio > 1) - return 0; - - for (int i = 0; i < ARRAY_SIZE(map1->schedule); i++) { - if (map1->schedule[i] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT && - map2->schedule[i] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT) { - NL_SET_ERR_MSG_FMT(info->extack, - "Maps %u and %u both schedule slot %d", - map1->map_id, map2->map_id, i); - return -EINVAL; - } - } - return 0; } @@ -17736,7 +17717,7 @@ static int nl80211_nan_set_peer_sched(struct sk_buff *skb, /* Validate against previous maps */ for (int j = 0; j < n_maps; j++) { - ret = nl80211_nan_validate_map_pair(&rdev->wiphy, info, + ret = nl80211_nan_validate_map_pair(info, &sched.maps[j], &sched.maps[n_maps], nan_channels); -- cgit v1.2.3 From 743c052c1979560d3ddca312ef084715120e17c8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 30 Sep 2026 12:02:56 +0300 Subject: wifi: cfg80211: add protected TWT assoc flag Protected TWT support is indicated in RSNX, but RSNX may be used from security profile now, and not be present in the association response frame, so the logic in mac80211 to determine it doesn't/won't really work. Add a flag for wpa_supplicant to indicate if protected TWT is supported in the association, it can easily check this based on the RSNX data that was used. Signed-off-by: Johannes Berg Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260930120021.1eb6760356f2.Ie4962eaca97be29c7f08043d615534c7aeb3f370@changeid Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 5 +++++ net/wireless/nl80211.c | 8 ++++++++ 3 files changed, 15 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 2e3dfee85019..3154f12fbd89 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3407,6 +3407,7 @@ struct cfg80211_ml_reconf_req { * @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 + * @ASSOC_REQ_PROTECTED_TWT: Enable protected TWT */ enum cfg80211_assoc_req_flags { ASSOC_REQ_DISABLE_HT = BIT(0), @@ -3419,6 +3420,7 @@ enum cfg80211_assoc_req_flags { ASSOC_REQ_SPP_AMSDU = BIT(7), ASSOC_REQ_DISABLE_UHR = BIT(8), ASSOC_REQ_CIP = BIT(9), + ASSOC_REQ_PROTECTED_TWT = BIT(10), }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 75d4c5d6a7a3..7dae0860a1b8 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3228,6 +3228,9 @@ enum nl80211_commands { * @NL80211_ATTR_CIP_CAPABILITIES: The Control Integrity Protocol for the * station. * + * @NL80211_ATTR_ASSOC_PROTECTED_TWT: Enable protected TWT for the association, + * requires protected TWT support (flag attribute) + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3833,6 +3836,8 @@ enum nl80211_attrs { NL80211_ATTR_ASSOC_CIP, NL80211_ATTR_CIP_CAPABILITIES, + NL80211_ATTR_ASSOC_PROTECTED_TWT, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 572da1d8f3d9..cbd51de57b6e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1100,6 +1100,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_FRAME_NO_STA] = { .type = NLA_FLAG }, [NL80211_ATTR_ASSOC_CIP] = { .type = NLA_FLAG }, [NL80211_ATTR_CIP_CAPABILITIES] = { .type = NLA_U8 }, + [NL80211_ATTR_ASSOC_PROTECTED_TWT] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -13422,6 +13423,13 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) req.flags |= ASSOC_REQ_CIP; } + if (nla_get_flag(info->attrs[NL80211_ATTR_ASSOC_PROTECTED_TWT])) { + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_PROTECTED_TWT)) + return -EINVAL; + req.flags |= ASSOC_REQ_PROTECTED_TWT; + } + req.link_id = nl80211_link_id_or_invalid(info->attrs); if (info->attrs[NL80211_ATTR_MLO_LINKS]) { -- cgit v1.2.3 From ef650cdd4495df8afce89826a1c3bc3f285ab3ae Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Mon, 5 Oct 2026 11:44:48 +0300 Subject: wifi: cfg80211: nan: add Instant Communication support Add NL80211_NAN_CONF_INSTANT_COMM flag to let user space request that the NAN synchronization logic starts Instant Communication (IC) as defined in Chapter 13 of the Wi-Fi Aware Specification v4.0. Add NL80211_NAN_CAPA_INSTANT_COMM for drivers to advertise it. The capability requires configurable synchronization support, and IC requests are rejected if it isn't advertised. When IC is enabled, user space must also configure the discovery beacon interval. The IC schedule is expected to be configured by user space. Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20261005084458.3666739-2-ilan.peer@intel.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 9 ++++++++- include/uapi/linux/nl80211.h | 12 ++++++++++++ net/wireless/core.c | 5 +++++ net/wireless/nl80211.c | 22 ++++++++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3154f12fbd89..05c625d9e076 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4193,9 +4193,12 @@ struct cfg80211_nan_band_config { * that can take a value from 50-6F-9A-01-00-00 to 50-6F-9A-01-FF-FF. * @scan_period: period (in seconds) between NAN scans. * @scan_dwell_time: dwell time (in milliseconds) for NAN scans. - * @discovery_beacon_interval: interval (in TUs) for discovery beacons. + * @discovery_beacon_interval: interval (in TUs) for discovery beacons. Must be + * greater than 0 when @instant_comm is true. * @enable_dw_notification: flag to enable/disable discovery window * notifications. + * @instant_comm: if true, start Instant Communication (IC) as defined in + * Chapter 13 of the Wi-Fi Aware Specification v4.0. * @band_cfgs: array of band specific configurations, indexed by * &enum nl80211_band values. * @extra_nan_attrs: pointer to additional NAN attributes. @@ -4211,6 +4214,7 @@ struct cfg80211_nan_conf { u16 scan_dwell_time; u8 discovery_beacon_interval; bool enable_dw_notification; + bool instant_comm; struct cfg80211_nan_band_config band_cfgs[NUM_NL80211_BANDS]; const u8 *extra_nan_attrs; u16 extra_nan_attrs_len; @@ -6307,10 +6311,13 @@ struct wiphy_radio { * @WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC: Device supports NAN configurable * synchronization. * @WIPHY_NAN_FLAGS_USERSPACE_DE: Device doesn't support DE offload. + * @WIPHY_NAN_FLAGS_INSTANT_COMM: Device can switch to Instant Communication + * (IC) mode. Can only be set along with %WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC. */ enum wiphy_nan_flags { WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC = BIT(0), WIPHY_NAN_FLAGS_USERSPACE_DE = BIT(1), + WIPHY_NAN_FLAGS_INSTANT_COMM = BIT(2), }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 7dae0860a1b8..341f8395d1af 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -7895,6 +7895,11 @@ enum nl80211_nan_band_conf_attributes { * the upcoming discovery window with * %NL80211_CMD_NAN_NEXT_DW_NOTIFICATION. * This is a flag attribute. + * @NL80211_NAN_CONF_INSTANT_COMM: If set, the NAN synchronization logic will + * start Instant Communication (IC) as defined in Chapter 13 of the + * Wi-Fi Aware Specification v4.0. + * %NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL must be set as well. + * This is a flag attribute. * @NUM_NL80211_NAN_CONF_ATTR: Internal. * @NL80211_NAN_CONF_ATTR_MAX: Highest NAN configuration attribute. * @@ -7910,6 +7915,7 @@ enum nl80211_nan_conf_attributes { NL80211_NAN_CONF_SCAN_DWELL_TIME, NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL, NL80211_NAN_CONF_NOTIFY_DW, + NL80211_NAN_CONF_INSTANT_COMM, /* keep last */ NUM_NL80211_NAN_CONF_ATTR, @@ -9126,6 +9132,11 @@ enum nl80211_s1g_short_beacon_attrs { * specification Table 79 (Capabilities field). * @NL80211_NAN_CAPA_PHY: nested attribute containing band-agnostic * capabilities for NAN data path. See &enum nl80211_nan_phy_cap_attr. + * @NL80211_NAN_CAPA_INSTANT_COMM: Flag attribute indicating that the device + * can switch to Instant Communication (IC) mode, as defined in Chapter 13 + * of the Wi-Fi Aware Specification v4.0. Can only be set if + * %NL80211_NAN_CAPA_CONFIGURABLE_SYNC is set. When IC is enabled, the IC + * schedule is expected to be configured by user space. * @__NL80211_NAN_CAPABILITIES_LAST: Internal * @NL80211_NAN_CAPABILITIES_MAX: Highest NAN capability attribute. */ @@ -9139,6 +9150,7 @@ enum nl80211_nan_capabilities { NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME, NL80211_NAN_CAPA_CAPABILITIES, NL80211_NAN_CAPA_PHY, + NL80211_NAN_CAPA_INSTANT_COMM, /* keep last */ __NL80211_NAN_CAPABILITIES_LAST, NL80211_NAN_CAPABILITIES_MAX = __NL80211_NAN_CAPABILITIES_LAST - 1, diff --git a/net/wireless/core.c b/net/wireless/core.c index dc1e0522340a..9176659345f0 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -964,6 +964,11 @@ int wiphy_register(struct wiphy *wiphy) (!wiphy->nan_capa.phy.ht.ht_supported || wiphy->n_radio > 1))) return -EINVAL; + if (WARN_ON((wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_INSTANT_COMM) && + !(wiphy->nan_capa.flags & + WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC))) + return -EINVAL; + if (WARN_ON(wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))) return -EINVAL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index cbd51de57b6e..7ee69ec10b0f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -687,6 +687,7 @@ nl80211_nan_conf_policy[NL80211_NAN_CONF_ATTR_MAX + 1] = { [NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL] = NLA_POLICY_RANGE(NLA_U8, 50, 200), [NL80211_NAN_CONF_NOTIFY_DW] = { .type = NLA_FLAG }, + [NL80211_NAN_CONF_INSTANT_COMM] = { .type = NLA_FLAG }, }; static const struct netlink_range_validation nl80211_punct_bitmap_range = { @@ -3037,6 +3038,10 @@ static int nl80211_put_nan_capa(struct wiphy *wiphy, struct sk_buff *msg) nla_put_flag(msg, NL80211_NAN_CAPA_USERSPACE_DE)) goto fail; + if ((wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_INSTANT_COMM) && + nla_put_flag(msg, NL80211_NAN_CAPA_INSTANT_COMM)) + goto fail; + if (nla_put_u8(msg, NL80211_NAN_CAPA_OP_MODE, wiphy->nan_capa.op_mode) || nla_put_u8(msg, NL80211_NAN_CAPA_NUM_ANTENNAS, @@ -16789,6 +16794,23 @@ static int nl80211_parse_nan_conf(struct wiphy *wiphy, conf->enable_dw_notification = nla_get_flag(attrs[NL80211_NAN_CONF_NOTIFY_DW]); + conf->instant_comm = nla_get_flag(attrs[NL80211_NAN_CONF_INSTANT_COMM]); + if (conf->instant_comm) { + if (!(wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_INSTANT_COMM)) { + NL_SET_ERR_MSG_ATTR(info->extack, + attrs[NL80211_NAN_CONF_INSTANT_COMM], + "Instant Communication is not supported"); + return -EOPNOTSUPP; + } + + if (!conf->discovery_beacon_interval) { + NL_SET_ERR_MSG_ATTR(info->extack, + attrs[NL80211_NAN_CONF_INSTANT_COMM], + "Instant Communication requires a discovery beacon interval"); + return -EINVAL; + } + } + out: if (!conf->band_cfgs[NL80211_BAND_5GHZ].chan && (!conf->bands || conf->bands & BIT(NL80211_BAND_5GHZ))) { -- cgit v1.2.3