]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: mac80211: allow only AP chanctx sharing with NPCA
authorJohannes Berg <johannes.berg@intel.com>
Tue, 28 Apr 2026 09:25:37 +0000 (11:25 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 5 May 2026 12:49:03 +0000 (14:49 +0200)
When two interfaces share a channel context, disable NPCA
unless both are AP interfaces that require NPCA. This way,
two AP interfaces can have identical chandefs set up and
share the channel context, but any non-APs cannot share a
chanctx with NPCA (they'd almost certainly have different
BSS color.)

This doesn't mean the chanctx cannot be shared but rather
that NPCA will be disabled on the shared channel context.

Link: https://patch.msgid.link/20260428112708.3832e15f4e78.I08a7c7f47d796f4d5d8f9a682c1fba37db2e4cf5@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/chan.c

index ba1723e624811e9753f83c1a34f931a3a661f2cb..ae7fd44e17b5410cd50c066ea6436a7c8615f3a8 100644 (file)
@@ -218,6 +218,8 @@ struct ieee80211_low_level_stats {
  *     bandwidth) OFDMA settings need to be changed
  * @IEEE80211_CHANCTX_CHANGE_PUNCTURING: The punctured channel(s) bitmap
  *     was changed.
+ * @IEEE80211_CHANCTX_CHANGE_NPCA: NPCA configuration changed
+ * @IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT: NPCA puncturing changed
  */
 enum ieee80211_chanctx_change {
        IEEE80211_CHANCTX_CHANGE_WIDTH          = BIT(0),
@@ -227,6 +229,8 @@ enum ieee80211_chanctx_change {
        IEEE80211_CHANCTX_CHANGE_MIN_DEF        = BIT(4),
        IEEE80211_CHANCTX_CHANGE_AP             = BIT(5),
        IEEE80211_CHANCTX_CHANGE_PUNCTURING     = BIT(6),
+       IEEE80211_CHANCTX_CHANGE_NPCA           = BIT(7),
+       IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT     = BIT(8),
 };
 
 /**
@@ -234,10 +238,13 @@ enum ieee80211_chanctx_change {
  * @oper: channel definition to use for operation
  * @ap: the channel definition of the AP, if any
  *     (otherwise the chan member is %NULL)
+ * @require_npca: If NPCA is configured, require it to
+ *     remain, this is used by AP interfaces
  */
 struct ieee80211_chan_req {
        struct cfg80211_chan_def oper;
        struct cfg80211_chan_def ap;
+       bool require_npca;
 };
 
 /**
index 413dd73e8815952eee16ce37141a00e09c05eeac..7e9dcb02f9bd9c5240f635b6b7ff31c0a2c4f41b 100644 (file)
@@ -1613,7 +1613,10 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
        unsigned int link_id = params->beacon.link_id;
        struct ieee80211_link_data *link;
        struct ieee80211_bss_conf *link_conf;
-       struct ieee80211_chan_req chanreq = { .oper = params->chandef };
+       struct ieee80211_chan_req chanreq = {
+               .oper = params->chandef,
+               .require_npca = true,
+       };
        u64 tsf;
 
        lockdep_assert_wiphy(local->hw.wiphy);
@@ -4627,7 +4630,10 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                           struct cfg80211_csa_settings *params)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       struct ieee80211_chan_req chanreq = { .oper = params->chandef };
+       struct ieee80211_chan_req chanreq = {
+               .oper = params->chandef,
+               .require_npca = true,
+       };
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_channel_switch ch_switch = {
                .link_id = params->link_id,
@@ -5069,7 +5075,10 @@ static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_link_data *link;
-       struct ieee80211_chan_req chanreq = { .oper = *chandef };
+       struct ieee80211_chan_req chanreq = {
+               .oper = *chandef,
+               .require_npca = true,
+       };
        int ret;
        u64 changed = 0;
 
index 16ee79566e50e38004929b03a872ffbb13d8a69d..5e24988ef5614cd9511edc55c600f28e09bed776 100644 (file)
@@ -285,19 +285,41 @@ ieee80211_chanreq_compatible(const struct ieee80211_chan_req *a,
                             const struct ieee80211_chan_req *b,
                             struct ieee80211_chan_req *tmp)
 {
+       struct ieee80211_chan_req _a = *a, _b = *b;
        const struct cfg80211_chan_def *compat;
 
        if (a->ap.chan && b->ap.chan &&
            !cfg80211_chandef_identical(&a->ap, &b->ap))
                return NULL;
 
-       compat = cfg80211_chandef_compatible(&a->oper, &b->oper);
+       /*
+        * Remove NPCA if it's not required, so that interfaces
+        * sharing a channel context will not use NPCA while the
+        * channel context is shared.
+        * If both sides are AP interfaces requiring NPC, there's
+        * an assumption that userspace will set them up with
+        * identical configurations and the same BSS color
+        * (if the config is not identical, sharing will fail due
+        * to cfg80211_chandef_compatible() failing below.)
+        */
+       if (!_a.require_npca) {
+               _a.oper.npca_chan = NULL;
+               _a.oper.npca_punctured = 0;
+       }
+
+       if (!_b.require_npca) {
+               _b.oper.npca_chan = NULL;
+               _b.oper.npca_punctured = 0;
+       }
+
+       compat = cfg80211_chandef_compatible(&_a.oper, &_b.oper);
        if (!compat)
                return NULL;
 
        /* Note: later code assumes this always fills & returns tmp if compat */
        tmp->oper = *compat;
        tmp->ap = a->ap.chan ? a->ap : b->ap;
+       tmp->require_npca = a->require_npca && b->require_npca;
        return tmp;
 }
 
@@ -754,7 +776,6 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local,
                                      const struct ieee80211_chan_req *chanreq,
                                      struct ieee80211_link_data *rsvd_for)
 {
-       const struct cfg80211_chan_def *chandef = &chanreq->oper;
        struct ieee80211_chan_req ctx_req = {
                .oper = ctx->conf.def,
                .ap = ctx->conf.ap,
@@ -762,7 +783,7 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local,
        u32 changed = 0;
 
        /* 5/10 MHz not handled here */
-       switch (chandef->width) {
+       switch (chanreq->oper.width) {
        case NL80211_CHAN_WIDTH_1:
        case NL80211_CHAN_WIDTH_2:
        case NL80211_CHAN_WIDTH_4:
@@ -807,10 +828,15 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local,
                        changed |= IEEE80211_CHANCTX_CHANGE_WIDTH;
                if (ctx->conf.def.punctured != chanreq->oper.punctured)
                        changed |= IEEE80211_CHANCTX_CHANGE_PUNCTURING;
+               if (ctx->conf.def.npca_chan != chanreq->oper.npca_chan)
+                       changed |= IEEE80211_CHANCTX_CHANGE_NPCA;
+               if (chanreq->oper.npca_chan &&
+                   ctx->conf.def.npca_punctured != chanreq->oper.npca_punctured)
+                       changed |= IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT;
        }
        if (!cfg80211_chandef_identical(&ctx->conf.ap, &chanreq->ap))
                changed |= IEEE80211_CHANCTX_CHANGE_AP;
-       ctx->conf.def = *chandef;
+       ctx->conf.def = chanreq->oper;
        ctx->conf.ap = chanreq->ap;
 
        /* check if min chanctx also changed */