mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
wifi: cfg80211: add option for vif allowed radios
This allows users to prevent a vif from affecting radios other than the configured ones. This can be useful in cases where e.g. an AP is running on one radio, and triggering a scan on another radio should not disturb it. Changing the allowed radios list for a vif is supported, but only while it is down. While it is possible to achieve the same by always explicitly specifying a frequency list for scan requests and ensuring that the wrong channel/band is never accidentally set on an unrelated interface, this change makes multi-radio wiphy setups a lot easier to deal with for CLI users. By itself, this patch only enforces the radio mask for scanning requests and remain-on-channel. Follow-up changes build on this to limit configured frequencies. Signed-off-by: Felix Fietkau <nbd@nbd.name> Link: https://patch.msgid.link/eefcb218780f71a1549875d149f1196486762756.1728462320.git-series.nbd@nbd.name Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
a834cd0c45
commit
3607798ad9
@ -6221,6 +6221,7 @@ enum ieee80211_ap_reg_power {
|
||||
* entered.
|
||||
* @links.cac_time_ms: CAC time in ms
|
||||
* @valid_links: bitmap describing what elements of @links are valid
|
||||
* @radio_mask: Bitmask of radios that this interface is allowed to operate on.
|
||||
*/
|
||||
struct wireless_dev {
|
||||
struct wiphy *wiphy;
|
||||
@ -6333,6 +6334,8 @@ struct wireless_dev {
|
||||
unsigned int cac_time_ms;
|
||||
} links[IEEE80211_MLD_MAX_NUM_LINKS];
|
||||
u16 valid_links;
|
||||
|
||||
u32 radio_mask;
|
||||
};
|
||||
|
||||
static inline const u8 *wdev_address(struct wireless_dev *wdev)
|
||||
@ -6518,6 +6521,17 @@ static inline bool cfg80211_channel_is_psc(struct ieee80211_channel *chan)
|
||||
bool cfg80211_radio_chandef_valid(const struct wiphy_radio *radio,
|
||||
const struct cfg80211_chan_def *chandef);
|
||||
|
||||
/**
|
||||
* cfg80211_wdev_channel_allowed - Check if the wdev may use the channel
|
||||
*
|
||||
* @wdev: the wireless device
|
||||
* @chan: channel to check
|
||||
*
|
||||
* Return: whether or not the wdev may use the channel
|
||||
*/
|
||||
bool cfg80211_wdev_channel_allowed(struct wireless_dev *wdev,
|
||||
struct ieee80211_channel *chan);
|
||||
|
||||
/**
|
||||
* ieee80211_get_response_rate - get basic rate for a given rate
|
||||
*
|
||||
|
@ -2868,6 +2868,9 @@ enum nl80211_commands {
|
||||
* nested item, it contains attributes defined in
|
||||
* &enum nl80211_if_combination_attrs.
|
||||
*
|
||||
* @NL80211_ATTR_VIF_RADIO_MASK: Bitmask of allowed radios (u32).
|
||||
* A value of 0 means all radios.
|
||||
*
|
||||
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
@ -3416,6 +3419,8 @@ enum nl80211_attrs {
|
||||
NL80211_ATTR_WIPHY_RADIOS,
|
||||
NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
|
||||
|
||||
NL80211_ATTR_VIF_RADIO_MASK,
|
||||
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
|
@ -1430,6 +1430,8 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
|
||||
/* allow mac80211 to determine the timeout */
|
||||
wdev->ps_timeout = -1;
|
||||
|
||||
wdev->radio_mask = BIT(wdev->wiphy->n_radio) - 1;
|
||||
|
||||
if ((wdev->iftype == NL80211_IFTYPE_STATION ||
|
||||
wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
|
||||
wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
|
||||
|
@ -829,6 +829,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
||||
[NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
|
||||
[NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
|
||||
[NL80211_ATTR_ASSOC_SPP_AMSDU] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_VIF_RADIO_MASK] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
@ -3996,7 +3997,8 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
|
||||
nla_put_u32(msg, NL80211_ATTR_GENERATION,
|
||||
rdev->devlist_generation ^
|
||||
(cfg80211_rdev_list_generation << 2)) ||
|
||||
nla_put_u8(msg, NL80211_ATTR_4ADDR, wdev->use_4addr))
|
||||
nla_put_u8(msg, NL80211_ATTR_4ADDR, wdev->use_4addr) ||
|
||||
nla_put_u32(msg, NL80211_ATTR_VIF_RADIO_MASK, wdev->radio_mask))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (rdev->ops->get_channel && !wdev->valid_links) {
|
||||
@ -4312,6 +4314,29 @@ static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int nl80211_parse_vif_radio_mask(struct genl_info *info,
|
||||
u32 *radio_mask)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct nlattr *attr = info->attrs[NL80211_ATTR_VIF_RADIO_MASK];
|
||||
u32 mask, allowed;
|
||||
|
||||
if (!attr) {
|
||||
*radio_mask = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
allowed = BIT(rdev->wiphy.n_radio) - 1;
|
||||
mask = nla_get_u32(attr);
|
||||
if (mask & ~allowed)
|
||||
return -EINVAL;
|
||||
if (!mask)
|
||||
mask = allowed;
|
||||
*radio_mask = mask;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
@ -4319,6 +4344,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
int err;
|
||||
enum nl80211_iftype otype, ntype;
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
u32 radio_mask = 0;
|
||||
bool change = false;
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
@ -4332,8 +4359,6 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_MESH_ID]) {
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
if (ntype != NL80211_IFTYPE_MESH_POINT)
|
||||
return -EINVAL;
|
||||
if (otype != NL80211_IFTYPE_MESH_POINT)
|
||||
@ -4364,6 +4389,12 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
if (err > 0)
|
||||
change = true;
|
||||
|
||||
err = nl80211_parse_vif_radio_mask(info, &radio_mask);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (err && netif_running(dev))
|
||||
return -EBUSY;
|
||||
|
||||
if (change)
|
||||
err = cfg80211_change_iface(rdev, dev, ntype, ¶ms);
|
||||
else
|
||||
@ -4372,11 +4403,11 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!err && params.use_4addr != -1)
|
||||
dev->ieee80211_ptr->use_4addr = params.use_4addr;
|
||||
|
||||
if (change && !err) {
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
if (radio_mask)
|
||||
wdev->radio_mask = radio_mask;
|
||||
|
||||
if (change && !err)
|
||||
nl80211_notify_iface(rdev, wdev, NL80211_CMD_SET_INTERFACE);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -4387,6 +4418,7 @@ static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
struct vif_params params;
|
||||
struct wireless_dev *wdev;
|
||||
struct sk_buff *msg;
|
||||
u32 radio_mask;
|
||||
int err;
|
||||
enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
|
||||
|
||||
@ -4424,6 +4456,10 @@ static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = nl80211_parse_vif_radio_mask(info, &radio_mask);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
@ -4465,6 +4501,9 @@ static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
break;
|
||||
}
|
||||
|
||||
if (radio_mask)
|
||||
wdev->radio_mask = radio_mask;
|
||||
|
||||
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
||||
rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) {
|
||||
nlmsg_free(msg);
|
||||
@ -9156,6 +9195,9 @@ static bool cfg80211_off_channel_oper_allowed(struct wireless_dev *wdev,
|
||||
|
||||
lockdep_assert_wiphy(wdev->wiphy);
|
||||
|
||||
if (!cfg80211_wdev_channel_allowed(wdev, chan))
|
||||
return false;
|
||||
|
||||
if (!cfg80211_beaconing_iface_active(wdev))
|
||||
return true;
|
||||
|
||||
@ -9368,7 +9410,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
||||
}
|
||||
|
||||
/* ignore disabled channels */
|
||||
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
||||
if (chan->flags & IEEE80211_CHAN_DISABLED ||
|
||||
!cfg80211_wdev_channel_allowed(wdev, chan))
|
||||
continue;
|
||||
|
||||
request->channels[i] = chan;
|
||||
@ -9388,7 +9431,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
||||
|
||||
chan = &wiphy->bands[band]->channels[j];
|
||||
|
||||
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
||||
if (chan->flags & IEEE80211_CHAN_DISABLED ||
|
||||
!cfg80211_wdev_channel_allowed(wdev, chan))
|
||||
continue;
|
||||
|
||||
request->channels[i] = chan;
|
||||
|
@ -956,7 +956,8 @@ static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev)
|
||||
struct ieee80211_channel *chan =
|
||||
ieee80211_get_channel(&rdev->wiphy, ap->center_freq);
|
||||
|
||||
if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
|
||||
if (!chan || chan->flags & IEEE80211_CHAN_DISABLED ||
|
||||
!cfg80211_wdev_channel_allowed(rdev_req->wdev, chan))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < rdev_req->n_channels; i++) {
|
||||
@ -3515,9 +3516,12 @@ int cfg80211_wext_siwscan(struct net_device *dev,
|
||||
continue;
|
||||
|
||||
for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
|
||||
struct ieee80211_channel *chan;
|
||||
|
||||
/* ignore disabled channels */
|
||||
if (wiphy->bands[band]->channels[j].flags &
|
||||
IEEE80211_CHAN_DISABLED)
|
||||
chan = &wiphy->bands[band]->channels[j];
|
||||
if (chan->flags & IEEE80211_CHAN_DISABLED ||
|
||||
!cfg80211_wdev_channel_allowed(creq->wdev, chan))
|
||||
continue;
|
||||
|
||||
/* If we have a wireless request structure and the
|
||||
|
@ -2923,3 +2923,32 @@ bool cfg80211_radio_chandef_valid(const struct wiphy_radio *radio,
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_radio_chandef_valid);
|
||||
|
||||
bool cfg80211_wdev_channel_allowed(struct wireless_dev *wdev,
|
||||
struct ieee80211_channel *chan)
|
||||
{
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
const struct wiphy_radio *radio;
|
||||
struct cfg80211_chan_def chandef;
|
||||
u32 radio_mask;
|
||||
int i;
|
||||
|
||||
radio_mask = wdev->radio_mask;
|
||||
if (!wiphy->n_radio || radio_mask == BIT(wiphy->n_radio) - 1)
|
||||
return true;
|
||||
|
||||
cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
|
||||
for (i = 0; i < wiphy->n_radio; i++) {
|
||||
if (!(radio_mask & BIT(i)))
|
||||
continue;
|
||||
|
||||
radio = &wiphy->radio[i];
|
||||
if (!cfg80211_radio_chandef_valid(radio, &chandef))
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_wdev_channel_allowed);
|
||||
|
Loading…
Reference in New Issue
Block a user