From 12bc27e79a8ddab8ee6d900dae712d7c10231faf Mon Sep 17 00:00:00 2001 From: Rory Little Date: Mon, 21 Sep 2026 14:04:41 -0700 Subject: [PATCH] wifi: cfg80211: validate monitor channel set against radio usage Current logic only looks globally at interface counts to validate that a monitor can have its channel configured. This works to ensure that the wiphy is not pulled off the channel currently being used by another active interface, but fails to allow for the case where the channels in use on the other active interface and the desired channel for the monitor fall on disjoint sets of radios. Instead, search for any interface which would cause conflict, otherwise allowing for the configuration. Suggested-by: Dylan Eskew Signed-off-by: Rory Little Link: https://patch.msgid.link/20260921210441.3137-1-roryl@candelatech.com Signed-off-by: Johannes Berg --- net/wireless/chan.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 1071e823108b..c743b6fb7e30 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -1810,13 +1810,44 @@ bool cfg80211_reg_check_beaconing(struct wiphy *wiphy, } EXPORT_SYMBOL(cfg80211_reg_check_beaconing); +static bool cfg80211_can_set_monitor_channel(struct cfg80211_registered_device *rdev, + struct cfg80211_chan_def *chandef) +{ + struct wireless_dev *wdev; + int radio_idx; + + lockdep_assert_held(&rdev->wiphy.mtx); + + if (rdev->num_running_monitor_ifaces < 1) + return false; + + if (cfg80211_has_monitors_only(rdev)) + return true; + + radio_idx = cfg80211_get_radio_idx_by_chan(&rdev->wiphy, chandef->chan); + /* No defined radio covers this channel. Fall back on global behavior */ + if (radio_idx < 0) + return false; + + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + if (wdev->iftype == NL80211_IFTYPE_MONITOR) + continue; + if (!wdev->netdev) + continue; + if (rdev_get_radio_mask(rdev, wdev->netdev) & BIT(radio_idx)) + return false; + } + + return true; +} + int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_chan_def *chandef) { if (!rdev->ops->set_monitor_channel) return -EOPNOTSUPP; - if (!cfg80211_has_monitors_only(rdev)) + if (!cfg80211_can_set_monitor_channel(rdev, chandef)) return -EBUSY; return rdev_set_monitor_channel(rdev, dev, chandef);