]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - wpa_supplicant/ctrl_iface.c
P2P NFC: Report connection handover as trigger for P2P
[thirdparty/hostap.git] / wpa_supplicant / ctrl_iface.c
index 8e3931ffee079649d96aa6b86e17ff81ab525f56..3bfcc909ac4a700b166ec262a123517d9a3c23f3 100644 (file)
@@ -54,7 +54,7 @@ static int pno_start(struct wpa_supplicant *wpa_s)
        struct wpa_ssid *ssid;
        struct wpa_driver_scan_params params;
 
-       if (wpa_s->pno)
+       if (wpa_s->pno || wpa_s->pno_sched_pending)
                return 0;
 
        if ((wpa_s->wpa_state > WPA_SCANNING) &&
@@ -64,8 +64,14 @@ static int pno_start(struct wpa_supplicant *wpa_s)
        }
 
        if (wpa_s->wpa_state == WPA_SCANNING) {
-               wpa_supplicant_cancel_sched_scan(wpa_s);
                wpa_supplicant_cancel_scan(wpa_s);
+               if (wpa_s->sched_scanning) {
+                       wpa_printf(MSG_DEBUG, "Schedule PNO on completion of "
+                                  "ongoing sched scan");
+                       wpa_supplicant_cancel_sched_scan(wpa_s);
+                       wpa_s->pno_sched_pending = 1;
+                       return 0;
+               }
        }
 
        os_memset(&params, 0, sizeof(params));
@@ -128,11 +134,13 @@ static int pno_stop(struct wpa_supplicant *wpa_s)
 {
        int ret = 0;
 
-       if (wpa_s->pno) {
+       if (wpa_s->pno || wpa_s->sched_scanning) {
                wpa_s->pno = 0;
                ret = wpa_supplicant_stop_sched_scan(wpa_s);
        }
 
+       wpa_s->pno_sched_pending = 0;
+
        if (wpa_s->wpa_state == WPA_SCANNING)
                wpa_supplicant_req_scan(wpa_s, 0, 0);
 
@@ -805,7 +813,7 @@ static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s,
        else if (hwaddr_aton(cmd, bssid))
                return -1;
 
-       return wpas_wps_start_nfc(wpa_s, _bssid);
+       return wpas_wps_start_nfc(wpa_s, _bssid, NULL, 0, 0, NULL, NULL, 0);
 }
 
 
@@ -900,12 +908,12 @@ static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
 
 static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
                                              char *reply, size_t max_len,
-                                             int cr)
+                                             int ndef)
 {
        struct wpabuf *buf;
        int res;
 
-       buf = wpas_wps_nfc_handover_req(wpa_s, cr);
+       buf = wpas_wps_nfc_handover_req(wpa_s, ndef);
        if (buf == NULL)
                return -1;
 
@@ -920,23 +928,59 @@ static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
 }
 
 
+static int wpas_ctrl_nfc_get_handover_req_p2p(struct wpa_supplicant *wpa_s,
+                                             char *reply, size_t max_len,
+                                             int ndef)
+{
+       struct wpabuf *buf;
+       int res;
+
+       buf = wpas_p2p_nfc_handover_req(wpa_s, ndef);
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Could not generate NFC handover request");
+               return -1;
+       }
+
+       res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+                                        wpabuf_len(buf));
+       reply[res++] = '\n';
+       reply[res] = '\0';
+
+       wpabuf_free(buf);
+
+       return res;
+}
+
+
 static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s,
                                          char *cmd, char *reply,
                                          size_t max_len)
 {
        char *pos;
+       int ndef;
 
        pos = os_strchr(cmd, ' ');
        if (pos == NULL)
                return -1;
        *pos++ = '\0';
 
-       if (os_strcmp(cmd, "NDEF") != 0)
+       if (os_strcmp(cmd, "WPS") == 0)
+               ndef = 0;
+       else if (os_strcmp(cmd, "NDEF") == 0)
+               ndef = 1;
+       else
                return -1;
 
        if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+               if (!ndef)
+                       return -1;
                return wpas_ctrl_nfc_get_handover_req_wps(
-                       wpa_s, reply, max_len, os_strcmp(pos, "WPS-CR") == 0);
+                       wpa_s, reply, max_len, ndef);
+       }
+
+       if (os_strcmp(pos, "P2P-CR") == 0) {
+               return wpas_ctrl_nfc_get_handover_req_p2p(
+                       wpa_s, reply, max_len, ndef);
        }
 
        return -1;
@@ -965,6 +1009,28 @@ static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s,
 }
 
 
+static int wpas_ctrl_nfc_get_handover_sel_p2p(struct wpa_supplicant *wpa_s,
+                                             char *reply, size_t max_len,
+                                             int ndef, int tag)
+{
+       struct wpabuf *buf;
+       int res;
+
+       buf = wpas_p2p_nfc_handover_sel(wpa_s, ndef, tag);
+       if (buf == NULL)
+               return -1;
+
+       res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+                                        wpabuf_len(buf));
+       reply[res++] = '\n';
+       reply[res] = '\0';
+
+       wpabuf_free(buf);
+
+       return res;
+}
+
+
 static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
                                          char *cmd, char *reply,
                                          size_t max_len)
@@ -988,11 +1054,23 @@ static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
        if (pos2)
                *pos2++ = '\0';
        if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+               if (!ndef)
+                       return -1;
                return wpas_ctrl_nfc_get_handover_sel_wps(
                        wpa_s, reply, max_len, ndef,
                        os_strcmp(pos, "WPS-CR") == 0, pos2);
        }
 
+       if (os_strcmp(pos, "P2P-CR") == 0) {
+               return wpas_ctrl_nfc_get_handover_sel_p2p(
+                       wpa_s, reply, max_len, ndef, 0);
+       }
+
+       if (os_strcmp(pos, "P2P-CR-TAG") == 0) {
+               return wpas_ctrl_nfc_get_handover_sel_p2p(
+                       wpa_s, reply, max_len, ndef, 1);
+       }
+
        return -1;
 }
 
@@ -1110,6 +1188,17 @@ static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s,
 
        if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) {
                ret = wpas_wps_nfc_report_handover(wpa_s, req, sel);
+       } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0)
+       {
+               ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel);
+               if (ret < 0)
+                       ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel);
+       } else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0)
+       {
+               ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel);
+       } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0)
+       {
+               ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel);
        } else {
                wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
                           "reported: role=%s type=%s", role, type);
@@ -3652,6 +3741,7 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
        unsigned int timeout = atoi(cmd);
        enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
        u8 dev_id[ETH_ALEN], *_dev_id = NULL;
+       u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL;
        char *pos;
        unsigned int search_delay;
 
@@ -3668,6 +3758,14 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
                _dev_id = dev_id;
        }
 
+       pos = os_strstr(cmd, "dev_type=");
+       if (pos) {
+               pos += 9;
+               if (wps_dev_type_str2bin(pos, dev_type) < 0)
+                       return -1;
+               _dev_type = dev_type;
+       }
+
        pos = os_strstr(cmd, "delay=");
        if (pos) {
                pos += 6;
@@ -3675,8 +3773,8 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
        } else
                search_delay = wpas_p2p_search_delay(wpa_s);
 
-       return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id,
-                            search_delay);
+       return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
+                            _dev_id, search_delay);
 }
 
 
@@ -4664,7 +4762,68 @@ static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd)
 #endif /* CONFIG_P2P */
 
 
+static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val)
+{
+       struct wpa_freq_range_list ranges;
+       int *freqs = NULL;
+       struct hostapd_hw_modes *mode;
+       u16 i;
+
+       if (wpa_s->hw.modes == NULL)
+               return NULL;
+
+       os_memset(&ranges, 0, sizeof(ranges));
+       if (freq_range_list_parse(&ranges, val) < 0)
+               return NULL;
+
+       for (i = 0; i < wpa_s->hw.num_modes; i++) {
+               int j;
+
+               mode = &wpa_s->hw.modes[i];
+               for (j = 0; j < mode->num_channels; j++) {
+                       unsigned int freq;
+
+                       if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED)
+                               continue;
+
+                       freq = mode->channels[j].freq;
+                       if (!freq_range_list_includes(&ranges, freq))
+                               continue;
+
+                       int_array_add_unique(&freqs, freq);
+               }
+       }
+
+       os_free(ranges.range);
+       return freqs;
+}
+
+
 #ifdef CONFIG_INTERWORKING
+
+static int ctrl_interworking_select(struct wpa_supplicant *wpa_s, char *param)
+{
+       int auto_sel = 0;
+       int *freqs = NULL;
+
+       if (param) {
+               char *pos;
+
+               auto_sel = os_strstr(param, "auto") != NULL;
+
+               pos = os_strstr(param, "freq=");
+               if (pos) {
+                       freqs = freq_range_to_channel_list(wpa_s, pos + 5);
+                       if (freqs == NULL)
+                               return -1;
+               }
+
+       }
+
+       return interworking_select(wpa_s, auto_sel, freqs);
+}
+
+
 static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst)
 {
        u8 bssid[ETH_ALEN];
@@ -4788,9 +4947,8 @@ static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
        int used;
        char *pos;
        size_t resp_len, start, requested_len;
-
-       if (!wpa_s->last_gas_resp)
-               return -1;
+       struct wpabuf *resp;
+       int ret;
 
        used = hwaddr_aton2(cmd, addr);
        if (used < 0)
@@ -4801,11 +4959,18 @@ static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
                pos++;
        dialog_token = atoi(pos);
 
-       if (os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) != 0 ||
-           dialog_token != wpa_s->last_gas_dialog_token)
+       if (wpa_s->last_gas_resp &&
+           os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 &&
+           dialog_token == wpa_s->last_gas_dialog_token)
+               resp = wpa_s->last_gas_resp;
+       else if (wpa_s->prev_gas_resp &&
+                os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 &&
+                dialog_token == wpa_s->prev_gas_dialog_token)
+               resp = wpa_s->prev_gas_resp;
+       else
                return -1;
 
-       resp_len = wpabuf_len(wpa_s->last_gas_resp);
+       resp_len = wpabuf_len(resp);
        start = 0;
        requested_len = resp_len;
 
@@ -4826,9 +4991,24 @@ static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
        if (requested_len * 2 + 1 > buflen)
                return os_snprintf(buf, buflen, "FAIL-Too long response");
 
-       return wpa_snprintf_hex(buf, buflen,
-                               wpabuf_head_u8(wpa_s->last_gas_resp) + start,
-                               requested_len);
+       ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(resp) + start,
+                              requested_len);
+
+       if (start + requested_len == resp_len) {
+               /*
+                * Free memory by dropping the response after it has been
+                * fetched.
+                */
+               if (resp == wpa_s->prev_gas_resp) {
+                       wpabuf_free(wpa_s->prev_gas_resp);
+                       wpa_s->prev_gas_resp = NULL;
+               } else {
+                       wpabuf_free(wpa_s->last_gas_resp);
+                       wpa_s->last_gas_resp = NULL;
+               }
+       }
+
+       return ret;
 }
 #endif /* CONFIG_INTERWORKING */
 
@@ -5167,6 +5347,10 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
        wpas_p2p_stop_find(wpa_s);
        p2p_ctrl_flush(wpa_s);
        wpas_p2p_group_remove(wpa_s, "*");
+       wpas_p2p_service_flush(wpa_s);
+       wpa_s->global->p2p_disabled = 0;
+       wpa_s->global->p2p_per_sta_psk = 0;
+       wpa_s->conf->num_sec_device_types = 0;
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS_TESTING
@@ -5211,6 +5395,10 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
        wpa_s->extra_blacklist_count = 0;
        wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
        wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all");
+       wpa_config_flush_blobs(wpa_s->conf);
+       wpa_s->conf->auto_interworking = 0;
+       wpa_s->conf->okc = 0;
+       wpa_s->conf->pmf = 0;
 
        wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200);
        wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70);
@@ -5381,6 +5569,9 @@ void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
 {
        struct wpa_radio_work *work, *tmp;
 
+       if (!wpa_s || !wpa_s->radio)
+               return;
+
        dl_list_for_each_safe(work, tmp, &wpa_s->radio->work,
                              struct wpa_radio_work, list) {
                struct wpa_external_work *ework;
@@ -5410,37 +5601,12 @@ static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx)
 
 static int set_scan_freqs(struct wpa_supplicant *wpa_s, char *val)
 {
-       struct wpa_freq_range_list ranges;
        int *freqs = NULL;
-       struct hostapd_hw_modes *mode;
-       u16 i;
-
-       if (wpa_s->hw.modes == NULL)
-               return -1;
 
-       os_memset(&ranges, 0, sizeof(ranges));
-       if (freq_range_list_parse(&ranges, val) < 0)
+       freqs = freq_range_to_channel_list(wpa_s, val);
+       if (freqs == NULL)
                return -1;
 
-       for (i = 0; i < wpa_s->hw.num_modes; i++) {
-               int j;
-
-               mode = &wpa_s->hw.modes[i];
-               for (j = 0; j < mode->num_channels; j++) {
-                       unsigned int freq;
-
-                       if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED)
-                               continue;
-
-                       freq = mode->channels[j].freq;
-                       if (!freq_range_list_includes(&ranges, freq))
-                               continue;
-
-                       int_array_add_unique(&freqs, freq);
-               }
-       }
-
-       os_free(ranges.range);
        os_free(wpa_s->manual_scan_freqs);
        wpa_s->manual_scan_freqs = freqs;
 
@@ -5844,9 +6010,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                        reply_len = -1;
        } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
                interworking_stop_fetch_anqp(wpa_s);
-       } else if (os_strncmp(buf, "INTERWORKING_SELECT", 19) == 0) {
-               if (interworking_select(wpa_s, os_strstr(buf + 19, "auto") !=
-                                       NULL) < 0)
+       } else if (os_strcmp(buf, "INTERWORKING_SELECT") == 0) {
+               if (ctrl_interworking_select(wpa_s, NULL) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "INTERWORKING_SELECT ", 20) == 0) {
+               if (ctrl_interworking_select(wpa_s, buf + 20) < 0)
                        reply_len = -1;
        } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) {
                if (ctrl_interworking_connect(wpa_s, buf + 21) < 0)
@@ -6272,6 +6440,8 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
 {
 #ifdef CONFIG_P2P
        static const char * cmd[] = {
+               "LIST_NETWORKS",
+               "SAVE_CONFIG",
                "P2P_FIND",
                "P2P_STOP_FIND",
                "P2P_LISTEN",
@@ -6286,6 +6456,12 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
                NULL
        };
        static const char * prefix[] = {
+#ifdef ANDROID
+               "DRIVER ",
+#endif /* ANDROID */
+               "GET_NETWORK ",
+               "REMOVE_NETWORK ",
+               "SET ",
                "P2P_FIND ",
                "P2P_CONNECT ",
                "P2P_LISTEN ",