Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d4e50c59 authored by Michal Kazior's avatar Michal Kazior Committed by Johannes Berg
Browse files

cfg80211: add channel checking for iface combinations



.connect cannot be handled since the driver scans
and connects on its own. It is up to the driver
then to refuse a connection (with -EBUSY for
example).

Non-fixed channel IBSSes always take a single
channel resource. For example two non-fixed
channel IBSSes always take up 2
num_different_channels, even if they operate on
the same channel at a given point of time.

Signed-off-by: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 2e165b81
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -373,6 +373,14 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
		if (WARN_ON(!c->num_different_channels))
			return -EINVAL;

		/*
		 * Put a sane limit on maximum number of different
		 * channels to simplify channel accounting code.
		 */
		if (WARN_ON(c->num_different_channels >
				CFG80211_MAX_NUM_DIFFERENT_CHANNELS))
			return -EINVAL;

		if (WARN_ON(!c->n_limits))
			return -EINVAL;

+26 −3
Original line number Diff line number Diff line
@@ -428,9 +428,20 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
			  u32 *flags, struct vif_params *params);
void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);

int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
				 struct wireless_dev *wdev,
				  enum nl80211_iftype iftype);
				 enum nl80211_iftype iftype,
				 struct ieee80211_channel *chan,
				 enum cfg80211_chan_mode chanmode);

static inline int
cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
			      struct wireless_dev *wdev,
			      enum nl80211_iftype iftype)
{
	return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL,
					    CHAN_MODE_UNDEFINED);
}

static inline int
cfg80211_can_add_interface(struct cfg80211_registered_device *rdev,
@@ -439,6 +450,16 @@ cfg80211_can_add_interface(struct cfg80211_registered_device *rdev,
	return cfg80211_can_change_interface(rdev, NULL, iftype);
}

static inline int
cfg80211_can_use_chan(struct cfg80211_registered_device *rdev,
		      struct wireless_dev *wdev,
		      struct ieee80211_channel *chan,
		      enum cfg80211_chan_mode chanmode)
{
	return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
					    chan, chanmode);
}

void
cfg80211_get_chan_state(struct cfg80211_registered_device *rdev,
		        struct wireless_dev *wdev,
@@ -461,6 +482,8 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
			       enum nl80211_iftype iftype, int num);

#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10

#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
#define CFG80211_DEV_WARN_ON(cond)	WARN_ON(cond)
#else
+54 −5
Original line number Diff line number Diff line
@@ -938,13 +938,20 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
	return res;
}

int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
				 struct wireless_dev *wdev,
				  enum nl80211_iftype iftype)
				 enum nl80211_iftype iftype,
				 struct ieee80211_channel *chan,
				 enum cfg80211_chan_mode chanmode)
{
	struct wireless_dev *wdev_iter;
	u32 used_iftypes = BIT(iftype);
	int num[NUM_NL80211_IFTYPES];
	struct ieee80211_channel
			*used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS];
	struct ieee80211_channel *ch;
	enum cfg80211_chan_mode chmode;
	int num_different_channels = 0;
	int total = 1;
	int i, j;

@@ -955,9 +962,23 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
		return 0;

	memset(num, 0, sizeof(num));
	memset(used_channels, 0, sizeof(used_channels));

	num[iftype] = 1;

	switch (chanmode) {
	case CHAN_MODE_UNDEFINED:
		break;
	case CHAN_MODE_SHARED:
		WARN_ON(!chan);
		used_channels[0] = chan;
		num_different_channels++;
		break;
	case CHAN_MODE_EXCLUSIVE:
		num_different_channels++;
		break;
	}

	mutex_lock(&rdev->devlist_mtx);
	list_for_each_entry(wdev_iter, &rdev->netdev_list, list) {
		if (wdev_iter == wdev)
@@ -968,6 +989,31 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
		if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype))
			continue;

		cfg80211_get_chan_state(rdev, wdev_iter, &ch, &chmode);

		switch (chmode) {
		case CHAN_MODE_UNDEFINED:
			break;
		case CHAN_MODE_SHARED:
			for (i = 0; i < CFG80211_MAX_NUM_DIFFERENT_CHANNELS; i++)
				if (!used_channels[i] || used_channels[i] == ch)
					break;

			if (i == CFG80211_MAX_NUM_DIFFERENT_CHANNELS) {
				mutex_unlock(&rdev->devlist_mtx);
				return -EBUSY;
			}

			if (used_channels[i] == NULL) {
				used_channels[i] = ch;
				num_different_channels++;
			}
			break;
		case CHAN_MODE_EXCLUSIVE:
			num_different_channels++;
			break;
		}

		num[wdev_iter->iftype]++;
		total++;
		used_iftypes |= BIT(wdev_iter->iftype);
@@ -984,12 +1030,15 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,

		c = &rdev->wiphy.iface_combinations[i];

		if (total > c->max_interfaces)
			continue;
		if (num_different_channels > c->num_different_channels)
			continue;

		limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits,
				 GFP_KERNEL);
		if (!limits)
			return -ENOMEM;
		if (total > c->max_interfaces)
			goto cont;

		for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
			if (rdev->wiphy.software_iftypes & BIT(iftype))