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 <dylan.eskew@candelatech.com>
Signed-off-by: Rory Little <roryl@candelatech.com>
Link: https://patch.msgid.link/20260921210441.3137-1-roryl@candelatech.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Rory Little 2026-09-21 14:04:41 -07:00 committed by Johannes Berg
parent e01f1c70e4
commit 12bc27e79a

View File

@ -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);