Skip to content

Commit

Permalink
cfg80211: add channel checking for iface combinations
Browse files Browse the repository at this point in the history
.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: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Michal Kazior authored and Johannes Berg committed Jun 29, 2012
1 parent 2e165b8 commit d4e50c5
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 8 deletions.
8 changes: 8 additions & 0 deletions net/wireless/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
29 changes: 26 additions & 3 deletions net/wireless/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
struct wireless_dev *wdev,
enum nl80211_iftype iftype);
int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
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,
Expand All @@ -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,
Expand All @@ -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
Expand Down
59 changes: 54 additions & 5 deletions net/wireless/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
struct wireless_dev *wdev,
enum nl80211_iftype iftype)
int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
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;

Expand All @@ -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)
Expand All @@ -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);
Expand All @@ -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))
Expand Down

0 comments on commit d4e50c5

Please sign in to comment.