MBO: Add Supported Operating Classes element to Association Request
[mech_eap.git] / wpa_supplicant / wpa_supplicant.c
index 9400927..e2a48fb 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -58,7 +58,7 @@
 
 const char *const wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors";
 
 const char *const wpa_supplicant_license =
 "This software may be distributed under the terms of the BSD license.\n"
@@ -545,6 +545,16 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        }
 
        wmm_ac_notify_disassoc(wpa_s);
+
+       wpa_s->sched_scan_plans_num = 0;
+       os_free(wpa_s->sched_scan_plans);
+       wpa_s->sched_scan_plans = NULL;
+
+#ifdef CONFIG_MBO
+       wpa_s->non_pref_chan_num = 0;
+       os_free(wpa_s->non_pref_chan);
+       wpa_s->non_pref_chan = NULL;
+#endif /* CONFIG_MBO */
 }
 
 
@@ -963,6 +973,11 @@ static void wpa_supplicant_reconfig(int sig, void *signal_ctx)
                        wpa_supplicant_terminate_proc(global);
                }
        }
+
+       if (wpa_debug_reopen_file() < 0) {
+               /* Ignore errors since we cannot really do much to fix this */
+               wpa_printf(MSG_DEBUG, "Could not reopen debug log file");
+       }
 }
 
 
@@ -1150,6 +1165,10 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                        return -1;
        }
 
+#ifdef CONFIG_NO_WPA
+       wpa_s->group_cipher = WPA_CIPHER_NONE;
+       wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+#else /* CONFIG_NO_WPA */
        sel = ie.group_cipher & ssid->group_cipher;
        wpa_s->group_cipher = wpa_pick_group_cipher(sel);
        if (wpa_s->group_cipher < 0) {
@@ -1169,6 +1188,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
        }
        wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s",
                wpa_cipher_txt(wpa_s->pairwise_cipher));
+#endif /* CONFIG_NO_WPA */
 
        sel = ie.key_mgmt & ssid->key_mgmt;
 #ifdef CONFIG_SAE
@@ -1279,7 +1299,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                int psk_set = 0;
 
                if (ssid->psk_set) {
-                       wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL);
+                       wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL,
+                                      NULL);
                        psk_set = 1;
                }
 #ifndef CONFIG_NO_PBKDF2
@@ -1290,7 +1311,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                                    4096, psk, PMK_LEN);
                        wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
                                        psk, PMK_LEN);
-                       wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+                       wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL);
                        psk_set = 1;
                        os_memset(psk, 0, sizeof(psk));
                }
@@ -1328,7 +1349,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                                wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
                                                "external passphrase)",
                                                psk, PMK_LEN);
-                               wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+                               wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+                                              NULL);
                                psk_set = 1;
                                os_memset(psk, 0, sizeof(psk));
                        } else
@@ -1341,7 +1363,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                                        ext_password_free(pw);
                                        return -1;
                                }
-                               wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+                               wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+                                              NULL);
                                psk_set = 1;
                                os_memset(psk, 0, sizeof(psk));
                        } else {
@@ -1404,6 +1427,9 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
                if (wpa_s->conf->hs20)
                        *pos |= 0x40; /* Bit 46 - WNM-Notification */
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+               *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_MBO */
                break;
        case 6: /* Bits 48-55 */
                break;
@@ -1586,6 +1612,15 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        struct wpa_connect_work *cwork;
        int rand_style;
 
+       wpa_s->own_disconnect_req = 0;
+
+       /*
+        * If we are starting a new connection, any previously pending EAPOL
+        * RX cannot be valid anymore.
+        */
+       wpabuf_free(wpa_s->pending_eapol_rx);
+       wpa_s->pending_eapol_rx = NULL;
+
        if (ssid->mac_addr == -1)
                rand_style = wpa_s->conf->mac_addr;
        else
@@ -1661,10 +1696,9 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                        return;
                }
                wpa_s->current_bss = bss;
-               wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_STARTED
-                            "ssid=\"%s\" id=%d",
-                            wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
-                            ssid->id);
+               wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d",
+                       wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+                       ssid->id);
 #else /* CONFIG_MESH */
                wpa_msg(wpa_s, MSG_ERROR,
                        "mesh mode support not included in the build");
@@ -1694,6 +1728,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                return;
        }
 
+       wpas_abort_ongoing_scan(wpa_s);
+
        cwork = os_zalloc(sizeof(*cwork));
        if (cwork == NULL)
                return;
@@ -1715,6 +1751,36 @@ static int bss_is_ibss(struct wpa_bss *bss)
 }
 
 
+static int drv_supports_vht(struct wpa_supplicant *wpa_s,
+                           const struct wpa_ssid *ssid)
+{
+       enum hostapd_hw_mode hw_mode;
+       struct hostapd_hw_modes *mode = NULL;
+       u8 channel;
+       int i;
+
+#ifdef CONFIG_HT_OVERRIDES
+       if (ssid->disable_ht)
+               return 0;
+#endif /* CONFIG_HT_OVERRIDES */
+
+       hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel);
+       if (hw_mode == NUM_HOSTAPD_MODES)
+               return 0;
+       for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+               if (wpa_s->hw.modes[i].mode == hw_mode) {
+                       mode = &wpa_s->hw.modes[i];
+                       break;
+               }
+       }
+
+       if (!mode)
+               return 0;
+
+       return mode->vht_capab != 0;
+}
+
+
 void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
                          const struct wpa_ssid *ssid,
                          struct hostapd_freq_params *freq)
@@ -1727,8 +1793,10 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
        struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
        u8 channel;
        int i, chan_idx, ht40 = -1, res, obss_scan = 1;
-       unsigned int j;
+       unsigned int j, k;
        struct hostapd_freq_params vht_freq;
+       int chwidth, seg0, seg1;
+       u32 vht_caps = 0;
 
        freq->freq = ssid->frequency;
 
@@ -1878,12 +1946,12 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
                   "IBSS/mesh: setup freq channel %d, sec_channel_offset %d",
                   freq->channel, freq->sec_channel_offset);
 
-       /* Not sure if mesh is ready for VHT */
-       if (ssid->mode != WPAS_MODE_IBSS)
+       if (!drv_supports_vht(wpa_s, ssid))
                return;
 
        /* For IBSS check VHT_IBSS flag */
-       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
+       if (ssid->mode == WPAS_MODE_IBSS &&
+           !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
                return;
 
        vht_freq = *freq;
@@ -1914,12 +1982,45 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
                        return;
        }
 
+       chwidth = VHT_CHANWIDTH_80MHZ;
+       seg0 = vht80[j] + 6;
+       seg1 = 0;
+
+       if (ssid->max_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) {
+               /* setup center_freq2, bandwidth */
+               for (k = 0; k < ARRAY_SIZE(vht80); k++) {
+                       /* Only accept 80 MHz segments separated by a gap */
+                       if (j == k || abs(vht80[j] - vht80[k]) == 16)
+                               continue;
+                       for (i = vht80[k]; i < vht80[k] + 16; i += 4) {
+                               struct hostapd_channel_data *chan;
+
+                               chan = hw_get_channel_chan(mode, i, NULL);
+                               if (!chan)
+                                       continue;
+
+                               if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+                                                 HOSTAPD_CHAN_NO_IR |
+                                                 HOSTAPD_CHAN_RADAR))
+                                       continue;
+
+                               /* Found a suitable second segment for 80+80 */
+                               chwidth = VHT_CHANWIDTH_80P80MHZ;
+                               vht_caps |=
+                                       VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+                               seg1 = vht80[k] + 6;
+                       }
+
+                       if (chwidth == VHT_CHANWIDTH_80P80MHZ)
+                               break;
+               }
+       }
+
        if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
                                    freq->channel, freq->ht_enabled,
                                    vht_freq.vht_enabled,
                                    freq->sec_channel_offset,
-                                   VHT_CHANWIDTH_80MHZ,
-                                   vht80[j] + 6, 0, 0) != 0)
+                                   chwidth, seg0, seg1, vht_caps) != 0)
                return;
 
        *freq = vht_freq;
@@ -1952,6 +2053,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
        struct ieee80211_vht_capabilities vhtcaps;
        struct ieee80211_vht_capabilities vhtcaps_mask;
 #endif /* CONFIG_VHT_OVERRIDES */
+#ifdef CONFIG_MBO
+       const u8 *mbo = NULL;
+#endif /* CONFIG_MBO */
 
        if (deinit) {
                if (work->started) {
@@ -2156,6 +2260,22 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
        }
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_MBO
+       if (bss) {
+               mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
+               if (mbo) {
+                       int len;
+
+                       len = wpas_mbo_supp_op_class_ie(wpa_s, bss->freq,
+                                                       wpa_ie + wpa_ie_len,
+                                                       sizeof(wpa_ie) -
+                                                       wpa_ie_len);
+                       if (len > 0)
+                               wpa_ie_len += len;
+               }
+       }
+#endif /* CONFIG_MBO */
+
        /*
         * Workaround: Add Extended Capabilities element only if the AP
         * included this element in Beacon/Probe Response frames. Some older
@@ -2204,6 +2324,17 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
        }
 #endif /* CONFIG_FST */
 
+#ifdef CONFIG_MBO
+       if (mbo) {
+               int len;
+
+               len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
+                                 sizeof(wpa_ie) - wpa_ie_len);
+               if (len >= 0)
+                       wpa_ie_len += len;
+       }
+#endif /* CONFIG_MBO */
+
        wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
        use_crypt = 1;
        cipher_pairwise = wpa_s->pairwise_cipher;
@@ -2256,9 +2387,11 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
                }
                params.bssid_hint = bss->bssid;
                params.freq_hint = bss->freq;
+               params.pbss = bss_is_pbss(bss);
        } else {
                params.ssid = ssid->ssid;
                params.ssid_len = ssid->ssid_len;
+               params.pbss = ssid->pbss;
        }
 
        if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set &&
@@ -2520,8 +2653,8 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_MESH
        if (wpa_s->ifmsh) {
-               wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
-                            wpa_s->ifname);
+               wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
+                       wpa_s->ifname);
                wpa_supplicant_leave_mesh(wpa_s);
        }
 #endif /* CONFIG_MESH */
@@ -2688,7 +2821,8 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
                        wpas_notify_network_enabled_changed(wpa_s, other_ssid);
        }
 
-       if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) {
+       if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid &&
+           wpa_s->wpa_state >= WPA_AUTHENTICATING) {
                /* We are already associated with the selected network */
                wpa_printf(MSG_DEBUG, "Already associated with the "
                           "selected network - do nothing");
@@ -4520,6 +4654,11 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
                wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
                wpa_s->max_scan_ssids = capa.max_scan_ssids;
                wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
+               wpa_s->max_sched_scan_plans = capa.max_sched_scan_plans;
+               wpa_s->max_sched_scan_plan_interval =
+                       capa.max_sched_scan_plan_interval;
+               wpa_s->max_sched_scan_plan_iterations =
+                       capa.max_sched_scan_plan_iterations;
                wpa_s->sched_scan_supported = capa.sched_scan_supported;
                wpa_s->max_match_sets = capa.max_match_sets;
                wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
@@ -4659,6 +4798,15 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 
        wpas_rrm_reset(wpa_s);
 
+       wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
+#ifdef CONFIG_HS20
+       hs20_init(wpa_s);
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+       wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan);
+#endif /* CONFIG_MBO */
+
        return 0;
 }
 
@@ -5120,12 +5268,13 @@ int wpa_supplicant_run(struct wpa_global *global)
        struct wpa_supplicant *wpa_s;
 
        if (global->params.daemonize &&
-           wpa_supplicant_daemon(global->params.pid_file))
+           (wpa_supplicant_daemon(global->params.pid_file) ||
+            eloop_sock_requeue()))
                return -1;
 
        if (global->params.wait_for_monitor) {
                for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
-                       if (wpa_s->ctrl_iface)
+                       if (wpa_s->ctrl_iface && !wpa_s->p2p_mgmt)
                                wpa_supplicant_ctrl_iface_wait(
                                        wpa_s->ctrl_iface);
        }
@@ -5223,6 +5372,9 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
        if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND)
                wpas_init_ext_pw(wpa_s);
 
+       if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS)
+               wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
 #ifdef CONFIG_WPS
        wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
@@ -5462,6 +5614,16 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning)
                        wpa_supplicant_req_scan(wpa_s, 0, 0);
                break;
+       case WPA_CTRL_REQ_EXT_CERT_CHECK:
+               if (eap->pending_ext_cert_check != PENDING_CHECK)
+                       return -1;
+               if (os_strcmp(value, "good") == 0)
+                       eap->pending_ext_cert_check = EXT_CERT_CHECK_GOOD;
+               else if (os_strcmp(value, "bad") == 0)
+                       eap->pending_ext_cert_check = EXT_CERT_CHECK_BAD;
+               else
+                       return -1;
+               break;
        default:
                wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
                return -1;
@@ -6049,3 +6211,100 @@ void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
        }
        wpabuf_free(buf);
 }
+
+
+struct wpa_supplicant *
+wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame)
+{
+       switch (frame) {
+#ifdef CONFIG_P2P
+       case VENDOR_ELEM_PROBE_REQ_P2P:
+       case VENDOR_ELEM_PROBE_RESP_P2P:
+       case VENDOR_ELEM_PROBE_RESP_P2P_GO:
+       case VENDOR_ELEM_BEACON_P2P_GO:
+       case VENDOR_ELEM_P2P_PD_REQ:
+       case VENDOR_ELEM_P2P_PD_RESP:
+       case VENDOR_ELEM_P2P_GO_NEG_REQ:
+       case VENDOR_ELEM_P2P_GO_NEG_RESP:
+       case VENDOR_ELEM_P2P_GO_NEG_CONF:
+       case VENDOR_ELEM_P2P_INV_REQ:
+       case VENDOR_ELEM_P2P_INV_RESP:
+       case VENDOR_ELEM_P2P_ASSOC_REQ:
+       case VENDOR_ELEM_P2P_ASSOC_RESP:
+               return wpa_s->parent;
+#endif /* CONFIG_P2P */
+       default:
+               return wpa_s;
+       }
+}
+
+
+void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s)
+{
+       unsigned int i;
+       char buf[30];
+
+       wpa_printf(MSG_DEBUG, "Update vendor elements");
+
+       for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+               if (wpa_s->vendor_elem[i]) {
+                       int res;
+
+                       res = os_snprintf(buf, sizeof(buf), "frame[%u]", i);
+                       if (!os_snprintf_error(sizeof(buf), res)) {
+                               wpa_hexdump_buf(MSG_DEBUG, buf,
+                                               wpa_s->vendor_elem[i]);
+                       }
+               }
+       }
+
+#ifdef CONFIG_P2P
+       if (wpa_s->parent == wpa_s &&
+           wpa_s->global->p2p &&
+           !wpa_s->global->p2p_disabled)
+               p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem);
+#endif /* CONFIG_P2P */
+}
+
+
+int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame,
+                           const u8 *elem, size_t len)
+{
+       u8 *ie, *end;
+
+       ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]);
+       end = ie + wpabuf_len(wpa_s->vendor_elem[frame]);
+
+       for (; ie + 1 < end; ie += 2 + ie[1]) {
+               if (ie + len > end)
+                       break;
+               if (os_memcmp(ie, elem, len) != 0)
+                       continue;
+
+               if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) {
+                       wpabuf_free(wpa_s->vendor_elem[frame]);
+                       wpa_s->vendor_elem[frame] = NULL;
+               } else {
+                       os_memmove(ie, ie + len, end - (ie + len));
+                       wpa_s->vendor_elem[frame]->used -= len;
+               }
+               wpas_vendor_elem_update(wpa_s);
+               return 0;
+       }
+
+       return -1;
+}
+
+
+struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+                                  u16 num_modes, enum hostapd_hw_mode mode)
+{
+       u16 i;
+
+       for (i = 0; i < num_modes; i++) {
+               if (modes[i].mode == mode)
+                       return &modes[i];
+       }
+
+       return NULL;
+}