diff options
author | Michal Kazior <michal.kazior@tieto.com> | 2012-06-29 06:47:07 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2012-06-29 07:39:19 -0400 |
commit | d4e50c5917e110451ced8f8de594cea858791f37 (patch) | |
tree | 078ac36d0642114f90b57e50ef9b585e2f8a5be9 /net | |
parent | 2e165b818456ecc1024dd0387eeac64745526377 (diff) |
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: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/wireless/core.c | 8 | ||||
-rw-r--r-- | net/wireless/core.h | 29 | ||||
-rw-r--r-- | net/wireless/util.c | 59 |
3 files changed, 88 insertions, 8 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index 580551e9caba..b26695ad3e97 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -373,6 +373,14 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) | |||
373 | if (WARN_ON(!c->num_different_channels)) | 373 | if (WARN_ON(!c->num_different_channels)) |
374 | return -EINVAL; | 374 | return -EINVAL; |
375 | 375 | ||
376 | /* | ||
377 | * Put a sane limit on maximum number of different | ||
378 | * channels to simplify channel accounting code. | ||
379 | */ | ||
380 | if (WARN_ON(c->num_different_channels > | ||
381 | CFG80211_MAX_NUM_DIFFERENT_CHANNELS)) | ||
382 | return -EINVAL; | ||
383 | |||
376 | if (WARN_ON(!c->n_limits)) | 384 | if (WARN_ON(!c->n_limits)) |
377 | return -EINVAL; | 385 | return -EINVAL; |
378 | 386 | ||
diff --git a/net/wireless/core.h b/net/wireless/core.h index d5efe1b0a8f7..81fef3ddb5a8 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -428,9 +428,20 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, | |||
428 | u32 *flags, struct vif_params *params); | 428 | u32 *flags, struct vif_params *params); |
429 | void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); | 429 | void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); |
430 | 430 | ||
431 | int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | 431 | int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, |
432 | struct wireless_dev *wdev, | 432 | struct wireless_dev *wdev, |
433 | enum nl80211_iftype iftype); | 433 | enum nl80211_iftype iftype, |
434 | struct ieee80211_channel *chan, | ||
435 | enum cfg80211_chan_mode chanmode); | ||
436 | |||
437 | static inline int | ||
438 | cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | ||
439 | struct wireless_dev *wdev, | ||
440 | enum nl80211_iftype iftype) | ||
441 | { | ||
442 | return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL, | ||
443 | CHAN_MODE_UNDEFINED); | ||
444 | } | ||
434 | 445 | ||
435 | static inline int | 446 | static inline int |
436 | cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, | 447 | cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, |
@@ -439,6 +450,16 @@ cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, | |||
439 | return cfg80211_can_change_interface(rdev, NULL, iftype); | 450 | return cfg80211_can_change_interface(rdev, NULL, iftype); |
440 | } | 451 | } |
441 | 452 | ||
453 | static inline int | ||
454 | cfg80211_can_use_chan(struct cfg80211_registered_device *rdev, | ||
455 | struct wireless_dev *wdev, | ||
456 | struct ieee80211_channel *chan, | ||
457 | enum cfg80211_chan_mode chanmode) | ||
458 | { | ||
459 | return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, | ||
460 | chan, chanmode); | ||
461 | } | ||
462 | |||
442 | void | 463 | void |
443 | cfg80211_get_chan_state(struct cfg80211_registered_device *rdev, | 464 | cfg80211_get_chan_state(struct cfg80211_registered_device *rdev, |
444 | struct wireless_dev *wdev, | 465 | struct wireless_dev *wdev, |
@@ -461,6 +482,8 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, | |||
461 | void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, | 482 | void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, |
462 | enum nl80211_iftype iftype, int num); | 483 | enum nl80211_iftype iftype, int num); |
463 | 484 | ||
485 | #define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10 | ||
486 | |||
464 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS | 487 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS |
465 | #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) | 488 | #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) |
466 | #else | 489 | #else |
diff --git a/net/wireless/util.c b/net/wireless/util.c index 9b92ec57d07b..4713cea9a2fa 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -938,13 +938,20 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, | |||
938 | return res; | 938 | return res; |
939 | } | 939 | } |
940 | 940 | ||
941 | int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | 941 | int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, |
942 | struct wireless_dev *wdev, | 942 | struct wireless_dev *wdev, |
943 | enum nl80211_iftype iftype) | 943 | enum nl80211_iftype iftype, |
944 | struct ieee80211_channel *chan, | ||
945 | enum cfg80211_chan_mode chanmode) | ||
944 | { | 946 | { |
945 | struct wireless_dev *wdev_iter; | 947 | struct wireless_dev *wdev_iter; |
946 | u32 used_iftypes = BIT(iftype); | 948 | u32 used_iftypes = BIT(iftype); |
947 | int num[NUM_NL80211_IFTYPES]; | 949 | int num[NUM_NL80211_IFTYPES]; |
950 | struct ieee80211_channel | ||
951 | *used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS]; | ||
952 | struct ieee80211_channel *ch; | ||
953 | enum cfg80211_chan_mode chmode; | ||
954 | int num_different_channels = 0; | ||
948 | int total = 1; | 955 | int total = 1; |
949 | int i, j; | 956 | int i, j; |
950 | 957 | ||
@@ -955,9 +962,23 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | |||
955 | return 0; | 962 | return 0; |
956 | 963 | ||
957 | memset(num, 0, sizeof(num)); | 964 | memset(num, 0, sizeof(num)); |
965 | memset(used_channels, 0, sizeof(used_channels)); | ||
958 | 966 | ||
959 | num[iftype] = 1; | 967 | num[iftype] = 1; |
960 | 968 | ||
969 | switch (chanmode) { | ||
970 | case CHAN_MODE_UNDEFINED: | ||
971 | break; | ||
972 | case CHAN_MODE_SHARED: | ||
973 | WARN_ON(!chan); | ||
974 | used_channels[0] = chan; | ||
975 | num_different_channels++; | ||
976 | break; | ||
977 | case CHAN_MODE_EXCLUSIVE: | ||
978 | num_different_channels++; | ||
979 | break; | ||
980 | } | ||
981 | |||
961 | mutex_lock(&rdev->devlist_mtx); | 982 | mutex_lock(&rdev->devlist_mtx); |
962 | list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { | 983 | list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { |
963 | if (wdev_iter == wdev) | 984 | if (wdev_iter == wdev) |
@@ -968,6 +989,31 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | |||
968 | if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) | 989 | if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) |
969 | continue; | 990 | continue; |
970 | 991 | ||
992 | cfg80211_get_chan_state(rdev, wdev_iter, &ch, &chmode); | ||
993 | |||
994 | switch (chmode) { | ||
995 | case CHAN_MODE_UNDEFINED: | ||
996 | break; | ||
997 | case CHAN_MODE_SHARED: | ||
998 | for (i = 0; i < CFG80211_MAX_NUM_DIFFERENT_CHANNELS; i++) | ||
999 | if (!used_channels[i] || used_channels[i] == ch) | ||
1000 | break; | ||
1001 | |||
1002 | if (i == CFG80211_MAX_NUM_DIFFERENT_CHANNELS) { | ||
1003 | mutex_unlock(&rdev->devlist_mtx); | ||
1004 | return -EBUSY; | ||
1005 | } | ||
1006 | |||
1007 | if (used_channels[i] == NULL) { | ||
1008 | used_channels[i] = ch; | ||
1009 | num_different_channels++; | ||
1010 | } | ||
1011 | break; | ||
1012 | case CHAN_MODE_EXCLUSIVE: | ||
1013 | num_different_channels++; | ||
1014 | break; | ||
1015 | } | ||
1016 | |||
971 | num[wdev_iter->iftype]++; | 1017 | num[wdev_iter->iftype]++; |
972 | total++; | 1018 | total++; |
973 | used_iftypes |= BIT(wdev_iter->iftype); | 1019 | used_iftypes |= BIT(wdev_iter->iftype); |
@@ -984,12 +1030,15 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | |||
984 | 1030 | ||
985 | c = &rdev->wiphy.iface_combinations[i]; | 1031 | c = &rdev->wiphy.iface_combinations[i]; |
986 | 1032 | ||
1033 | if (total > c->max_interfaces) | ||
1034 | continue; | ||
1035 | if (num_different_channels > c->num_different_channels) | ||
1036 | continue; | ||
1037 | |||
987 | limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, | 1038 | limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, |
988 | GFP_KERNEL); | 1039 | GFP_KERNEL); |
989 | if (!limits) | 1040 | if (!limits) |
990 | return -ENOMEM; | 1041 | return -ENOMEM; |
991 | if (total > c->max_interfaces) | ||
992 | goto cont; | ||
993 | 1042 | ||
994 | for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { | 1043 | for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { |
995 | if (rdev->wiphy.software_iftypes & BIT(iftype)) | 1044 | if (rdev->wiphy.software_iftypes & BIT(iftype)) |