Change channel before IBSS associations
authorChristopher Wiley <wiley@chromium.org>
Thu, 26 Jun 2014 20:13:07 +0000 (13:13 -0700)
committerJouni Malinen <j@w1.fi>
Sat, 28 Jun 2014 07:46:33 +0000 (10:46 +0300)
Fix a bug where changing the mode of the interface to IBSS
fails because the interface is sitting on a channel where IBSS is
disallowed because of a previous association.

Signed-off-by: Christopher Wiley <wiley@chromium.org>
src/drivers/driver_nl80211.c

index 5a1d7d1..c9fe8ef 100644 (file)
@@ -348,6 +348,8 @@ static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx,
                                            void *timeout_ctx);
 static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
                                       enum nl80211_iftype nlmode);
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, int freq);
+
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
                                   const u8 *set_addr, int first);
@@ -414,6 +416,7 @@ static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv);
 static int wpa_driver_nl80211_authenticate_retry(
        struct wpa_driver_nl80211_data *drv);
 
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
 static int i802_set_iface_flags(struct i802_bss *bss, int up);
 
 
@@ -8594,8 +8597,7 @@ static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
 
        wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
 
-       if (wpa_driver_nl80211_set_mode(drv->first_bss,
-                                       NL80211_IFTYPE_ADHOC)) {
+       if (wpa_driver_nl80211_set_mode_ibss(drv->first_bss, params->freq)) {
                wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
                           "IBSS mode");
                return -1;
@@ -9035,8 +9037,10 @@ nla_put_failure:
 }
 
 
-static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
-                                      enum nl80211_iftype nlmode)
+static int wpa_driver_nl80211_set_mode_impl(
+               struct i802_bss *bss,
+               enum nl80211_iftype nlmode,
+               struct hostapd_freq_params *desired_freq_params)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
        int ret = -1;
@@ -9081,6 +9085,19 @@ static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
                        os_sleep(0, 100000);
                        continue;
                }
+
+               /*
+                * Setting the mode will fail for some drivers if the phy is
+                * on a frequency that the mode is disallowed in.
+                */
+               if (desired_freq_params) {
+                       res = i802_set_freq(bss, desired_freq_params);
+                       if (res) {
+                               wpa_printf(MSG_DEBUG,
+                                          "nl80211: Failed to set frequency on interface");
+                       }
+               }
+
                /* Try to set the mode again while the interface is down */
                mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
                if (mode_switch_res == -EBUSY) {
@@ -9170,6 +9187,23 @@ static int dfs_info_handler(struct nl_msg *msg, void *arg)
 }
 
 
+static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+                                      enum nl80211_iftype nlmode)
+{
+       return wpa_driver_nl80211_set_mode_impl(bss, nlmode, NULL);
+}
+
+
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, int freq)
+{
+       struct hostapd_freq_params freq_params;
+       os_memset(&freq_params, 0, sizeof(freq_params));
+       freq_params.freq = freq;
+       return wpa_driver_nl80211_set_mode_impl(bss, NL80211_IFTYPE_ADHOC,
+                                               &freq_params);
+}
+
+
 static int wpa_driver_nl80211_get_capa(void *priv,
                                       struct wpa_driver_capa *capa)
 {