nl80211/cfg80211: enable DFS for IBSS mode
authorSimon Wunderlich <simon.wunderlich@s2003.tu-chemnitz.de>
Mon, 7 Oct 2013 16:41:05 +0000 (18:41 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 28 Oct 2013 14:05:21 +0000 (15:05 +0100)
To use DFS in IBSS mode, userspace is required to react to radar events.
It can inform nl80211 that it is capable of doing so by adding a
NL80211_ATTR_HANDLE_DFS attribute when joining the IBSS.

This attribute is supplied to let the kernelspace know that the
userspace application can and will handle radar events, e.g. by
intiating channel switches to a valid channel. DFS channels may
only be used if this attribute is supplied and the driver supports
it. Driver support will be checked even if a channel without DFS
will be initially joined, as a DFS channel may be chosen later.

Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
[fix attribute name in commit message]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
include/uapi/linux/nl80211.h
net/wireless/chan.c
net/wireless/ibss.c
net/wireless/nl80211.c
net/wireless/util.c

index 5db5fe24eff64aec5cf805c9309e28a79de74d6e..b1acf36e5f453a2128aededbc9e979bc67aea8cb 100644 (file)
@@ -1664,6 +1664,9 @@ struct cfg80211_disassoc_request {
  *     sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is
  *     required to assume that the port is unauthorized until authorized by
  *     user space. Otherwise, port is marked authorized by default.
+ * @userspace_handles_dfs: whether user space controls DFS operation, i.e.
+ *     changes the channel when a radar is detected. This is required
+ *     to operate on DFS channels.
  * @basic_rates: bitmap of basic rates to use when creating the IBSS
  * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
  * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
@@ -1681,6 +1684,7 @@ struct cfg80211_ibss_params {
        bool channel_fixed;
        bool privacy;
        bool control_port;
+       bool userspace_handles_dfs;
        int mcast_rate[IEEE80211_NUM_BANDS];
        struct ieee80211_ht_cap ht_capa;
        struct ieee80211_ht_cap ht_capa_mask;
@@ -3061,6 +3065,7 @@ struct cfg80211_cached_keys;
  * @conn: (private) cfg80211 software SME connection state machine data
  * @connect_keys: (private) keys to set after connection is established
  * @ibss_fixed: (private) IBSS is using fixed BSSID
+ * @ibss_dfs_possible: (private) IBSS may change to a DFS channel
  * @event_list: (private) list for internal event processing
  * @event_lock: (private) lock for event list
  */
@@ -3099,6 +3104,7 @@ struct wireless_dev {
        struct ieee80211_channel *channel;
 
        bool ibss_fixed;
+       bool ibss_dfs_possible;
 
        bool ps;
        int ps_timeout;
index f2aef2a7a570cd687b96e0053166337e1ad92226..f752e9821e717ee68066f4d8467775a8bee2fd78 100644 (file)
@@ -1501,6 +1501,13 @@ enum nl80211_commands {
  * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
  *      supported operating classes.
  *
+ * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
+ *     controls DFS operation in IBSS mode. If the flag is included in
+ *     %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
+ *     channels and reports radar events to userspace. Userspace is required
+ *     to react to radar events, e.g. initiate a channel switch or leave the
+ *     IBSS network.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1815,6 +1822,8 @@ enum nl80211_attrs {
 
        NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
 
+       NL80211_ATTR_HANDLE_DFS,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
index 16f3c3a7b2c17747230f62d1318ed152490eac34..9b8cc877eb191a2a62370c9f2617bed513200d24 100644 (file)
@@ -504,7 +504,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
        case NL80211_IFTYPE_ADHOC:
                if (wdev->current_bss) {
                        *chan = wdev->current_bss->pub.channel;
-                       *chanmode = wdev->ibss_fixed
+                       *chanmode = (wdev->ibss_fixed &&
+                                    !wdev->ibss_dfs_possible)
                                  ? CHAN_MODE_SHARED
                                  : CHAN_MODE_EXCLUSIVE;
                        return;
index 39bff7d367687fbdd4d220d76e34d66e4e586a9c..fa7461b6ba398fc56442ce75fafc6d5b5b0f8665 100644 (file)
@@ -83,6 +83,8 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
                         struct cfg80211_cached_keys *connkeys)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct ieee80211_channel *check_chan;
+       u8 radar_detect_width = 0;
        int err;
 
        ASSERT_WDEV_LOCK(wdev);
@@ -114,14 +116,28 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
        wdev->connect_keys = connkeys;
 
        wdev->ibss_fixed = params->channel_fixed;
+       wdev->ibss_dfs_possible = params->userspace_handles_dfs;
 #ifdef CONFIG_CFG80211_WEXT
        wdev->wext.ibss.chandef = params->chandef;
 #endif
+       check_chan = params->chandef.chan;
+       if (params->userspace_handles_dfs) {
+               /* use channel NULL to check for radar even if the current
+                * channel is not a radar channel - it might decide to change
+                * to DFS channel later.
+                */
+               radar_detect_width = BIT(params->chandef.width);
+               check_chan = NULL;
+       }
+
+       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+                                          check_chan,
+                                          (params->channel_fixed &&
+                                           !radar_detect_width)
+                                          ? CHAN_MODE_SHARED
+                                          : CHAN_MODE_EXCLUSIVE,
+                                          radar_detect_width);
 
-       err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
-                                   params->channel_fixed
-                                   ? CHAN_MODE_SHARED
-                                   : CHAN_MODE_EXCLUSIVE);
        if (err) {
                wdev->connect_keys = NULL;
                return err;
index 460638ac2d73b03a43cd0e6d575946d1658b5141..7502d33a3a700244d566fb229b8ed999d69fa0d7 100644 (file)
@@ -356,6 +356,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
        [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
        [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
+       [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -5768,9 +5769,9 @@ skip_beacons:
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
                return -EINVAL;
 
-       /* DFS channels are only supported for AP/P2P GO ... for now. */
        if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
-           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
+           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO ||
+           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) {
                err = cfg80211_chandef_dfs_required(wdev->wiphy,
                                                    &params.chandef);
                if (err < 0) {
@@ -6602,6 +6603,9 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        ibss.control_port =
                nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
 
+       ibss.userspace_handles_dfs =
+               nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
+
        err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
        if (err)
                kfree(connkeys);
index 3c8be6104ba407a9d0b5d997d8c2fe4d659a63e4..935dea9485da01f7a0fb82cacc6e187058ff1ce0 100644 (file)
@@ -1249,7 +1249,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        enum cfg80211_chan_mode chmode;
        int num_different_channels = 0;
        int total = 1;
-       bool radar_required;
+       bool radar_required = false;
        int i, j;
 
        ASSERT_RTNL();
@@ -1264,14 +1264,20 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_WDS:
-               radar_required = !!(chan &&
-                                   (chan->flags & IEEE80211_CHAN_RADAR));
+               /* if the interface could potentially choose a DFS channel,
+                * then mark DFS as required.
+                */
+               if (!chan) {
+                       if (chanmode != CHAN_MODE_UNDEFINED && radar_detect)
+                               radar_required = true;
+                       break;
+               }
+               radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR);
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_DEVICE:
        case NL80211_IFTYPE_MONITOR:
-               radar_required = false;
                break;
        case NUM_NL80211_IFTYPES:
        case NL80211_IFTYPE_UNSPECIFIED: