P2P: Support dedicated P2P_DEVICE without separate group interface
[mech_eap.git] / wpa_supplicant / wpa_supplicant.c
index 94b5eff..6b9c957 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"
@@ -397,6 +397,18 @@ void free_hw_features(struct wpa_supplicant *wpa_s)
 }
 
 
+static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_bss_tmp_disallowed *bss, *prev;
+
+       dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed,
+                             struct wpa_bss_tmp_disallowed, list) {
+               dl_list_del(&bss->list);
+               os_free(bss);
+       }
+}
+
+
 static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 {
        int i;
@@ -545,6 +557,18 @@ 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 */
+
+       free_bss_tmp_disallowed(wpa_s);
 }
 
 
@@ -1289,7 +1313,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
@@ -1300,7 +1325,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));
                }
@@ -1338,7 +1363,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
@@ -1351,7 +1377,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 {
@@ -1414,6 +1441,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;
@@ -1598,6 +1628,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 
        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
@@ -1673,10 +1710,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");
@@ -2031,6 +2067,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) {
@@ -2215,25 +2254,21 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
        os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
 #endif /* CONFIG_P2P */
 
-#ifdef CONFIG_HS20
-       if (is_hs20_network(wpa_s, ssid, bss)) {
-               struct wpabuf *hs20;
-               hs20 = wpabuf_alloc(20);
-               if (hs20) {
-                       int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
-                       size_t len;
-
-                       wpas_hs20_add_indication(hs20, pps_mo_id);
-                       len = sizeof(wpa_ie) - wpa_ie_len;
-                       if (wpabuf_len(hs20) <= len) {
-                               os_memcpy(wpa_ie + wpa_ie_len,
-                                         wpabuf_head(hs20), wpabuf_len(hs20));
-                               wpa_ie_len += wpabuf_len(hs20);
-                       }
-                       wpabuf_free(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_HS20 */
+#endif /* CONFIG_MBO */
 
        /*
         * Workaround: Add Extended Capabilities element only if the AP
@@ -2259,6 +2294,27 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
                }
        }
 
+#ifdef CONFIG_HS20
+       if (is_hs20_network(wpa_s, ssid, bss)) {
+               struct wpabuf *hs20;
+
+               hs20 = wpabuf_alloc(20);
+               if (hs20) {
+                       int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+                       size_t len;
+
+                       wpas_hs20_add_indication(hs20, pps_mo_id);
+                       len = sizeof(wpa_ie) - wpa_ie_len;
+                       if (wpabuf_len(hs20) <= len) {
+                               os_memcpy(wpa_ie + wpa_ie_len,
+                                         wpabuf_head(hs20), wpabuf_len(hs20));
+                               wpa_ie_len += wpabuf_len(hs20);
+                       }
+                       wpabuf_free(hs20);
+               }
+       }
+#endif /* CONFIG_HS20 */
+
        if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
                struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
                size_t len;
@@ -2283,6 +2339,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;
@@ -2335,9 +2402,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 &&
@@ -2421,8 +2490,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
 
        params.p2p = ssid->p2p_group;
 
-       if (wpa_s->parent->set_sta_uapsd)
-               params.uapsd = wpa_s->parent->sta_uapsd;
+       if (wpa_s->p2pdev->set_sta_uapsd)
+               params.uapsd = wpa_s->p2pdev->sta_uapsd;
        else
                params.uapsd = -1;
 
@@ -2599,8 +2668,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 */
@@ -3441,8 +3510,11 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent)
        wpa_s->scan_interval = 5;
        wpa_s->new_connection = 1;
        wpa_s->parent = parent ? parent : wpa_s;
+       wpa_s->p2pdev = wpa_s->parent;
        wpa_s->sched_scanning = 0;
 
+       dl_list_init(&wpa_s->bss_tmp_disallowed);
+
        return wpa_s;
 }
 
@@ -4600,6 +4672,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;
@@ -4739,6 +4816,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;
 }
 
@@ -4754,6 +4840,8 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
 
        iface = global->ifaces;
        while (iface) {
+               if (iface->p2pdev == wpa_s)
+                       iface->p2pdev = iface->parent;
                if (iface == wpa_s || iface->parent != wpa_s) {
                        iface = iface->next;
                        continue;
@@ -5200,12 +5288,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);
        }
@@ -5303,6 +5392,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 */
@@ -5542,6 +5634,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;
@@ -6129,3 +6231,175 @@ 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->p2pdev;
+#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;
+}
+
+
+static struct
+wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s,
+                                                const u8 *bssid)
+{
+       struct wpa_bss_tmp_disallowed *bss;
+
+       dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed,
+                        struct wpa_bss_tmp_disallowed, list) {
+               if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0)
+                       return bss;
+       }
+
+       return NULL;
+}
+
+
+void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                         unsigned int sec)
+{
+       struct wpa_bss_tmp_disallowed *bss;
+       struct os_reltime until;
+
+       os_get_reltime(&until);
+       until.sec += sec;
+
+       bss = wpas_get_disallowed_bss(wpa_s, bssid);
+       if (bss) {
+               bss->disallowed_until = until;
+               return;
+       }
+
+       bss = os_malloc(sizeof(*bss));
+       if (!bss) {
+               wpa_printf(MSG_DEBUG,
+                          "Failed to allocate memory for temp disallow BSS");
+               return;
+       }
+
+       bss->disallowed_until = until;
+       os_memcpy(bss->bssid, bssid, ETH_ALEN);
+       dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
+}
+
+
+int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+       struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev;
+       struct os_reltime now, age;
+
+       os_get_reltime(&now);
+
+       dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed,
+                        struct wpa_bss_tmp_disallowed, list) {
+               if (!os_reltime_before(&now, &tmp->disallowed_until)) {
+                       /* This BSS is not disallowed anymore */
+                       dl_list_del(&tmp->list);
+                       os_free(tmp);
+                       continue;
+               }
+               if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) {
+                       bss = tmp;
+                       break;
+               }
+       }
+       if (!bss)
+               return 0;
+
+       os_reltime_sub(&bss->disallowed_until, &now, &age);
+       wpa_printf(MSG_DEBUG,
+                  "BSS " MACSTR " disabled for %ld.%0ld seconds",
+                  MAC2STR(bss->bssid), age.sec, age.usec);
+       return 1;
+}