]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - wpa_supplicant/p2p_supplicant.c
wpa_supplicant: Add a configuration file for the P2P_DEVICE parameters
[thirdparty/hostap.git] / wpa_supplicant / p2p_supplicant.c
index 4375d9bbc46ae45e16cef50c9776ba2636fb44e2..09f98a342644b9c0af8a6e2450c0bc80271a15a8 100644 (file)
@@ -20,6 +20,7 @@
 #include "ap/ap_config.h"
 #include "ap/sta_info.h"
 #include "ap/ap_drv_ops.h"
+#include "ap/wps_hostapd.h"
 #include "ap/p2p_hostapd.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
@@ -103,13 +104,15 @@ static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx);
 static struct wpa_supplicant *
 wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
                         int go);
-static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s);
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
+                              const u8 *ssid, size_t ssid_len);
 static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
                                   const u8 *ssid, size_t ssid_len);
 static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
 static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
                         const u8 *dev_addr, enum p2p_wps_method wps_method,
-                        int auto_join, const u8 *ssid, size_t ssid_len);
+                        int auto_join, int freq,
+                        const u8 *ssid, size_t ssid_len);
 static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
@@ -120,6 +123,7 @@ static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx);
 static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
                                        int group_added);
 static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
+static void wpas_stop_listen(void *ctx);
 
 
 /*
@@ -249,7 +253,12 @@ static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
        int ret;
 
        if (deinit) {
-               wpa_scan_free_params(params);
+               if (!work->started) {
+                       wpa_scan_free_params(params);
+                       return;
+               }
+
+               wpa_s->p2p_scan_work = NULL;
                return;
        }
 
@@ -352,7 +361,7 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
                break;
        }
 
-       radio_remove_unstarted_work(wpa_s, "p2p-scan");
+       radio_remove_works(wpa_s, "p2p-scan", 0);
        if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb,
                           params) < 0)
                goto fail;
@@ -580,6 +589,10 @@ static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s,
                bssid = wpa_s->bssid;
 
        bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len);
+       if (bss == NULL && wpa_s->go_params &&
+           !is_zero_ether_addr(wpa_s->go_params->peer_device_addr))
+               bss = wpa_bss_get_p2p_dev_addr(
+                       wpa_s, wpa_s->go_params->peer_device_addr);
        if (bss == NULL) {
                u8 iface_addr[ETH_ALEN];
                if (p2p_get_interface_addr(wpa_s->global->p2p, bssid,
@@ -709,12 +722,10 @@ static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s,
                changed = 1;
        }
 
-#ifndef CONFIG_NO_CONFIG_WRITE
        if (changed && wpa_s->conf->update_config &&
            wpa_config_write(wpa_s->confname, wpa_s->conf)) {
                wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
        }
-#endif /* CONFIG_NO_CONFIG_WRITE */
 
        return s->id;
 }
@@ -782,11 +793,9 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
                          addr, ETH_ALEN);
        }
 
-#ifndef CONFIG_NO_CONFIG_WRITE
        if (wpa_s->parent->conf->update_config &&
            wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
                wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
 }
 
 
@@ -994,6 +1003,12 @@ static void wpas_send_action_cb(struct wpa_radio_work *work, int deinit)
        struct send_action_work *awork = work->ctx;
 
        if (deinit) {
+               if (work->started) {
+                       eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+                                            wpa_s, NULL);
+                       wpa_s->p2p_send_action_work = NULL;
+                       offchannel_send_action_done(wpa_s);
+               }
                os_free(awork);
                return;
        }
@@ -1101,8 +1116,10 @@ static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
 static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
                                    struct p2p_go_neg_results *res)
 {
-       wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR,
-                  MAC2STR(res->peer_interface_addr));
+       wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR
+                  " dev_addr " MACSTR " wps_method %d",
+                  MAC2STR(res->peer_interface_addr),
+                  MAC2STR(res->peer_device_addr), res->wps_method);
        wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start WPS Enrollee for SSID",
                          res->ssid, res->ssid_len);
        wpa_supplicant_ap_deinit(wpa_s);
@@ -1111,14 +1128,15 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
                wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1);
 #ifdef CONFIG_WPS_NFC
        } else if (res->wps_method == WPS_NFC) {
-               wpas_wps_start_nfc(wpa_s, res->peer_interface_addr,
+               wpas_wps_start_nfc(wpa_s, res->peer_device_addr,
+                                  res->peer_interface_addr,
                                   wpa_s->parent->p2p_oob_dev_pw,
                                   wpa_s->parent->p2p_oob_dev_pw_id, 1,
                                   wpa_s->parent->p2p_oob_dev_pw_id ==
                                   DEV_PW_NFC_CONNECTION_HANDOVER ?
                                   wpa_s->parent->p2p_peer_oob_pubkey_hash :
                                   NULL,
-                                  NULL, 0);
+                                  NULL, 0, 0);
 #endif /* CONFIG_WPS_NFC */
        } else {
                u16 dev_pw_id = DEV_PW_DEFAULT;
@@ -1370,6 +1388,11 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
        d->dtim_period = s->dtim_period;
        d->disassoc_low_ack = s->disassoc_low_ack;
        d->disable_scan_offload = s->disable_scan_offload;
+
+       if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey) {
+               d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey);
+               d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
+       }
 }
 
 
@@ -1669,9 +1692,9 @@ static void wpas_dev_found(void *ctx, const u8 *addr,
                       info->dev_capab, info->group_capab,
                       wfd_dev_info_hex ? " wfd_dev_info=0x" : "",
                       wfd_dev_info_hex ? wfd_dev_info_hex : "");
-#endif /* CONFIG_NO_STDOUT_DEBUG */
 
        os_free(wfd_dev_info_hex);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
 
        wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device);
 }
@@ -1731,6 +1754,10 @@ static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit)
        struct wpas_p2p_listen_work *lwork = work->ctx;
 
        if (deinit) {
+               if (work->started) {
+                       wpa_s->p2p_listen_work = NULL;
+                       wpas_stop_listen(wpa_s);
+               }
                wpas_p2p_listen_work_free(lwork);
                return;
        }
@@ -2816,7 +2843,7 @@ static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
                wpa_s->pending_pd_before_join = 0;
                wpa_printf(MSG_DEBUG, "P2P: Starting pending "
                           "join-existing-group operation");
-               wpas_p2p_join_start(wpa_s);
+               wpas_p2p_join_start(wpa_s, 0, NULL, 0);
                return;
        }
 
@@ -2860,7 +2887,7 @@ static void wpas_prov_disc_fail(void *ctx, const u8 *peer,
                wpa_printf(MSG_DEBUG, "P2P: Starting pending "
                           "join-existing-group operation (no ACK for PD "
                           "Req attempts)");
-               wpas_p2p_join_start(wpa_s);
+               wpas_p2p_join_start(wpa_s, 0, NULL, 0);
                return;
        }
 
@@ -2886,7 +2913,8 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
                                  const u8 *go_dev_addr, const u8 *ssid,
                                  size_t ssid_len, int *go, u8 *group_bssid,
                                  int *force_freq, int persistent_group,
-                                 const struct p2p_channels *channels)
+                                 const struct p2p_channels *channels,
+                                 int dev_pw_id)
 {
        struct wpa_supplicant *wpa_s = ctx;
        struct wpa_ssid *s;
@@ -2905,6 +2933,21 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
                                   "authorized invitation");
                        goto accept_inv;
                }
+
+#ifdef CONFIG_WPS_NFC
+               if (dev_pw_id >= 0 && wpa_s->parent->p2p_nfc_tag_enabled &&
+                   dev_pw_id == wpa_s->parent->p2p_oob_dev_pw_id) {
+                       wpa_printf(MSG_DEBUG, "P2P: Accept invitation based on local enabled NFC Tag");
+                       wpa_s->parent->p2p_wps_method = WPS_NFC;
+                       wpa_s->parent->pending_join_wps_method = WPS_NFC;
+                       os_memcpy(wpa_s->parent->pending_join_dev_addr,
+                                 go_dev_addr, ETH_ALEN);
+                       os_memcpy(wpa_s->parent->pending_join_iface_addr,
+                                 bssid, ETH_ALEN);
+                       goto accept_inv;
+               }
+#endif /* CONFIG_WPS_NFC */
+
                /*
                 * Do not accept the invitation automatically; notify user and
                 * request approval.
@@ -3024,12 +3067,12 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
                if (s) {
                        int go = s->mode == WPAS_MODE_P2P_GO;
                        wpas_p2p_group_add_persistent(
-                               wpa_s, s, go, go ? op_freq : 0, 0, 0, NULL,
+                               wpa_s, s, go, 0, go ? op_freq : 0, 0, 0, NULL,
                                go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
                } else if (bssid) {
                        wpa_s->user_initiated_pd = 0;
                        wpas_p2p_join(wpa_s, bssid, go_dev_addr,
-                                     wpa_s->p2p_wps_method, 0,
+                                     wpa_s->p2p_wps_method, 0, op_freq,
                                      ssid, ssid_len);
                }
                return;
@@ -3105,11 +3148,9 @@ static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s,
                   ssid->p2p_client_list + (i + 1) * ETH_ALEN,
                   (ssid->num_p2p_clients - i - 1) * ETH_ALEN);
        ssid->num_p2p_clients--;
-#ifndef CONFIG_NO_CONFIG_WRITE
        if (wpa_s->parent->conf->update_config &&
            wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
                wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
 }
 
 
@@ -3137,7 +3178,6 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
 {
        struct wpa_supplicant *wpa_s = ctx;
        struct wpa_ssid *ssid;
-       int freq;
 
        if (bssid) {
                wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
@@ -3193,17 +3233,10 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
                "starting persistent group");
        os_sleep(0, 50000);
 
-       freq = wpa_s->p2p_persistent_go_freq;
-       if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
-           freq_included(channels, neg_freq)) {
-               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use frequence %d MHz from invitation for GO mode",
-                       neg_freq);
-               freq = neg_freq;
-       }
-
        wpas_p2p_group_add_persistent(wpa_s, ssid,
                                      ssid->mode == WPAS_MODE_P2P_GO,
-                                     freq,
+                                     wpa_s->p2p_persistent_go_freq,
+                                     neg_freq,
                                      wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht,
                                      channels,
                                      ssid->mode == WPAS_MODE_P2P_GO ?
@@ -3438,7 +3471,7 @@ static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
                                                 struct hostapd_hw_modes *mode,
                                                 u8 channel, u8 bw)
 {
-       int flag;
+       int flag = 0;
        enum chan_allowed res, res2;
 
        res2 = res = has_channel(wpa_s->global, mode, channel, &flag);
@@ -3647,7 +3680,20 @@ int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s)
        iface.ifname = wpa_s->pending_interface_name;
        iface.driver = wpa_s->driver->name;
        iface.driver_param = wpa_s->conf->driver_param;
-       iface.confname = wpa_s->confname;
+
+       /*
+        * If a P2P Device configuration file was given, use it as the interface
+        * configuration file (instead of using parent's configuration file.
+        */
+       if (wpa_s->conf_p2p_dev) {
+               iface.confname = wpa_s->conf_p2p_dev;
+               iface.ctrl_interface = NULL;
+       } else {
+               iface.confname = wpa_s->confname;
+               iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+       }
+       iface.conf_p2p_dev = NULL;
+
        p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface);
        if (!p2pdev_wpa_s) {
                wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface");
@@ -4278,7 +4324,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
 
 start:
        /* Start join operation immediately */
-       wpas_p2p_join_start(wpa_s);
+       wpas_p2p_join_start(wpa_s, 0, NULL, 0);
 }
 
 
@@ -4379,11 +4425,12 @@ static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
 
 static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
                         const u8 *dev_addr, enum p2p_wps_method wps_method,
-                        int auto_join, const u8 *ssid, size_t ssid_len)
+                        int auto_join, int op_freq,
+                        const u8 *ssid, size_t ssid_len)
 {
        wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
-                  MACSTR " dev " MACSTR ")%s",
-                  MAC2STR(iface_addr), MAC2STR(dev_addr),
+                  MACSTR " dev " MACSTR " op_freq=%d)%s",
+                  MAC2STR(iface_addr), MAC2STR(dev_addr), op_freq,
                   auto_join ? " (auto_join)" : "");
        if (ssid && ssid_len) {
                wpa_printf(MSG_DEBUG, "P2P: Group SSID specified: %s",
@@ -4400,12 +4447,13 @@ static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
        wpas_p2p_stop_find(wpa_s);
 
        wpa_s->p2p_join_scan_count = 0;
-       wpas_p2p_join_scan_req(wpa_s, 0, ssid, ssid_len);
+       wpas_p2p_join_scan_req(wpa_s, op_freq, ssid, ssid_len);
        return 0;
 }
 
 
-static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
+                              const u8 *ssid, size_t ssid_len)
 {
        struct wpa_supplicant *group;
        struct p2p_go_neg_results res;
@@ -4433,17 +4481,25 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
        group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg;
 
        os_memset(&res, 0, sizeof(res));
+       os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN);
        os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
                  ETH_ALEN);
        res.wps_method = wpa_s->pending_join_wps_method;
-       bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->pending_join_iface_addr);
-       if (bss) {
-               res.freq = bss->freq;
-               res.ssid_len = bss->ssid_len;
-               os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
-               wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency "
-                          "from BSS table: %d MHz (SSID %s)", bss->freq,
-                          wpa_ssid_txt(bss->ssid, bss->ssid_len));
+       if (freq && ssid && ssid_len) {
+               res.freq = freq;
+               res.ssid_len = ssid_len;
+               os_memcpy(res.ssid, ssid, ssid_len);
+       } else {
+               bss = wpa_bss_get_bssid_latest(wpa_s,
+                                              wpa_s->pending_join_iface_addr);
+               if (bss) {
+                       res.freq = bss->freq;
+                       res.ssid_len = bss->ssid_len;
+                       os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+                       wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)",
+                                  bss->freq,
+                                  wpa_ssid_txt(bss->ssid, bss->ssid_len));
+               }
        }
 
        if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
@@ -4655,7 +4711,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                }
                wpa_s->user_initiated_pd = 1;
                if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
-                                 auto_join, NULL, 0) < 0)
+                                 auto_join, freq, NULL, 0) < 0)
                        return -1;
                return ret;
        }
@@ -4722,6 +4778,10 @@ void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
                p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
                              wpa_s->pending_listen_duration);
                wpa_s->pending_listen_freq = 0;
+       } else {
+               wpa_printf(MSG_DEBUG, "P2P: Ignore remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d freq=%u duration=%u)",
+                          wpa_s->off_channel_freq, wpa_s->pending_listen_freq,
+                          freq, duration);
        }
 }
 
@@ -5133,12 +5193,12 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
 
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
                                  struct wpa_ssid *ssid, int addr_allocated,
-                                 int freq, int ht40, int vht,
-                                 const struct p2p_channels *channels,
+                                 int force_freq, int neg_freq, int ht40,
+                                 int vht, const struct p2p_channels *channels,
                                  int connection_timeout)
 {
        struct p2p_go_neg_results params;
-       int go = 0;
+       int go = 0, freq;
 
        if (ssid->disabled != 2 || ssid->ssid == NULL)
                return -1;
@@ -5164,9 +5224,15 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
        if (ssid->mode != WPAS_MODE_P2P_GO)
                return -1;
 
-       freq = wpas_p2p_select_go_freq(wpa_s, freq);
-       if (freq < 0)
-               return -1;
+       if (force_freq > 0) {
+               freq = wpas_p2p_select_go_freq(wpa_s, force_freq);
+               if (freq < 0)
+                       return -1;
+       } else {
+               freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
+               if (freq < 0 || (freq > 0 && !freq_included(channels, freq)))
+                       freq = 0;
+       }
 
        if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, channels))
                return -1;
@@ -5697,7 +5763,7 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 
        return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
                          ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr,
-                         1, pref_freq);
+                         1, pref_freq, -1);
 }
 
 
@@ -5771,7 +5837,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
 
        return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
                          ssid->ssid, ssid->ssid_len, force_freq,
-                         go_dev_addr, persistent, pref_freq);
+                         go_dev_addr, persistent, pref_freq, -1);
 }
 
 
@@ -6757,11 +6823,9 @@ void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
        }
        dl_list_add(&persistent->psk_list, &p->list);
 
-#ifndef CONFIG_NO_CONFIG_WRITE
        if (wpa_s->parent->conf->update_config &&
            wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
                wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
 }
 
 
@@ -6772,14 +6836,10 @@ static void wpas_p2p_remove_psk(struct wpa_supplicant *wpa_s,
        int res;
 
        res = wpas_p2p_remove_psk_entry(wpa_s, s, addr, iface_addr);
-       if (res > 0) {
-#ifndef CONFIG_NO_CONFIG_WRITE
-               if (wpa_s->conf->update_config &&
-                   wpa_config_write(wpa_s->confname, wpa_s->conf))
-                       wpa_dbg(wpa_s, MSG_DEBUG,
-                               "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
-       }
+       if (res > 0 && wpa_s->conf->update_config &&
+           wpa_config_write(wpa_s->confname, wpa_s->conf))
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "P2P: Failed to update configuration");
 }
 
 
@@ -6975,11 +7035,7 @@ static struct wpabuf * wpas_p2p_nfc_handover(int ndef, struct wpabuf *wsc,
                                             struct wpabuf *p2p)
 {
        struct wpabuf *ret;
-
-       if (wsc == NULL) {
-               wpabuf_free(p2p);
-               return NULL;
-       }
+       size_t wsc_len;
 
        if (p2p == NULL) {
                wpabuf_free(wsc);
@@ -6987,15 +7043,17 @@ static struct wpabuf * wpas_p2p_nfc_handover(int ndef, struct wpabuf *wsc,
                return NULL;
        }
 
-       ret = wpabuf_alloc(2 + wpabuf_len(wsc) + 2 + wpabuf_len(p2p));
+       wsc_len = wsc ? wpabuf_len(wsc) : 0;
+       ret = wpabuf_alloc(2 + wsc_len + 2 + wpabuf_len(p2p));
        if (ret == NULL) {
                wpabuf_free(wsc);
                wpabuf_free(p2p);
                return NULL;
        }
 
-       wpabuf_put_be16(ret, wpabuf_len(wsc));
-       wpabuf_put_buf(ret, wsc);
+       wpabuf_put_be16(ret, wsc_len);
+       if (wsc)
+               wpabuf_put_buf(ret, wsc);
        wpabuf_put_be16(ret, wpabuf_len(p2p));
        wpabuf_put_buf(ret, p2p);
 
@@ -7019,10 +7077,38 @@ static struct wpabuf * wpas_p2p_nfc_handover(int ndef, struct wpabuf *wsc,
 }
 
 
+static int wpas_p2p_cli_freq(struct wpa_supplicant *wpa_s,
+                            struct wpa_ssid **ssid, u8 *go_dev_addr)
+{
+       struct wpa_supplicant *iface;
+
+       if (go_dev_addr)
+               os_memset(go_dev_addr, 0, ETH_ALEN);
+       if (ssid)
+               *ssid = NULL;
+       for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+               if (iface->wpa_state < WPA_ASSOCIATING ||
+                   iface->current_ssid == NULL || iface->assoc_freq == 0 ||
+                   !iface->current_ssid->p2p_group ||
+                   iface->current_ssid->mode != WPAS_MODE_INFRA)
+                       continue;
+               if (ssid)
+                       *ssid = iface->current_ssid;
+               if (go_dev_addr)
+                       os_memcpy(go_dev_addr, iface->go_dev_addr, ETH_ALEN);
+               return iface->assoc_freq;
+       }
+       return 0;
+}
+
+
 struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
                                          int ndef)
 {
        struct wpabuf *wsc, *p2p;
+       struct wpa_ssid *ssid;
+       u8 go_dev_addr[ETH_ALEN];
+       int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
 
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) {
                wpa_printf(MSG_DEBUG, "P2P: P2P disabled - cannot build handover request");
@@ -7036,9 +7122,14 @@ struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
                return NULL;
        }
 
-       wsc = wps_build_nfc_handover_req_p2p(wpa_s->parent->wps,
-                                            wpa_s->conf->wps_nfc_dh_pubkey);
-       p2p = p2p_build_nfc_handover_req(wpa_s->global->p2p);
+       if (cli_freq == 0) {
+               wsc = wps_build_nfc_handover_req_p2p(
+                       wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey);
+       } else
+               wsc = NULL;
+       p2p = p2p_build_nfc_handover_req(wpa_s->global->p2p, cli_freq,
+                                        go_dev_addr, ssid ? ssid->ssid : NULL,
+                                        ssid ? ssid->ssid_len : 0);
 
        return wpas_p2p_nfc_handover(ndef, wsc, p2p);
 }
@@ -7048,6 +7139,9 @@ struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
                                          int ndef, int tag)
 {
        struct wpabuf *wsc, *p2p;
+       struct wpa_ssid *ssid;
+       u8 go_dev_addr[ETH_ALEN];
+       int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
 
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return NULL;
@@ -7057,14 +7151,18 @@ struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
                           &wpa_s->conf->wps_nfc_dh_privkey) < 0)
                return NULL;
 
-       wsc = wps_build_nfc_handover_sel_p2p(wpa_s->parent->wps,
-                                            tag ?
-                                            wpa_s->conf->wps_nfc_dev_pw_id :
-                                            DEV_PW_NFC_CONNECTION_HANDOVER,
-                                            wpa_s->conf->wps_nfc_dh_pubkey,
-                                            tag ? wpa_s->conf->wps_nfc_dev_pw :
-                                            NULL);
-       p2p = p2p_build_nfc_handover_sel(wpa_s->global->p2p);
+       if (cli_freq == 0) {
+               wsc = wps_build_nfc_handover_sel_p2p(
+                       wpa_s->parent->wps,
+                       tag ? wpa_s->conf->wps_nfc_dev_pw_id :
+                       DEV_PW_NFC_CONNECTION_HANDOVER,
+                       wpa_s->conf->wps_nfc_dh_pubkey,
+                       tag ? wpa_s->conf->wps_nfc_dev_pw : NULL);
+       } else
+               wsc = NULL;
+       p2p = p2p_build_nfc_handover_sel(wpa_s->global->p2p, cli_freq,
+                                        go_dev_addr, ssid ? ssid->ssid : NULL,
+                                        ssid ? ssid->ssid_len : 0);
 
        return wpas_p2p_nfc_handover(ndef, wsc, p2p);
 }
@@ -7074,20 +7172,36 @@ static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s,
                                   struct p2p_nfc_params *params)
 {
        wpa_printf(MSG_DEBUG, "P2P: Initiate join-group based on NFC "
-                  "connection handover");
+                  "connection handover (freq=%d)",
+                  params->go_freq);
+
+       if (params->go_freq && params->go_ssid_len) {
+               wpa_s->p2p_wps_method = WPS_NFC;
+               wpa_s->pending_join_wps_method = WPS_NFC;
+               os_memset(wpa_s->pending_join_iface_addr, 0, ETH_ALEN);
+               os_memcpy(wpa_s->pending_join_dev_addr, params->go_dev_addr,
+                         ETH_ALEN);
+               return wpas_p2p_join_start(wpa_s, params->go_freq,
+                                          params->go_ssid,
+                                          params->go_ssid_len);
+       }
+
        return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
                                WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
-                               0, -1, 0, 1, 1);
+                               params->go_freq, -1, 0, 1, 1);
 }
 
 
 static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s,
-                                 struct p2p_nfc_params *params)
+                                 struct p2p_nfc_params *params, int tag)
 {
+       int res, persistent;
+       struct wpa_ssid *ssid;
+
        wpa_printf(MSG_DEBUG, "P2P: Authorize join-group based on NFC "
                   "connection handover");
        for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
-               struct wpa_ssid *ssid = wpa_s->current_ssid;
+               ssid = wpa_s->current_ssid;
                if (ssid == NULL)
                        continue;
                if (ssid->mode != WPAS_MODE_P2P_GO)
@@ -7107,27 +7221,56 @@ static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s,
                wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
                return -1;
        }
-       return wpas_ap_wps_add_nfc_pw(
+       res = wpas_ap_wps_add_nfc_pw(
                wpa_s, wpa_s->parent->p2p_oob_dev_pw_id,
                wpa_s->parent->p2p_oob_dev_pw,
                wpa_s->parent->p2p_peer_oob_pk_hash_known ?
                wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL);
+       if (res)
+               return res;
+
+       if (!tag) {
+               wpa_printf(MSG_DEBUG, "P2P: Negotiated handover - wait for peer to join without invitation");
+               return 0;
+       }
+
+       if (!params->peer ||
+           !(params->peer->dev_capab & P2P_DEV_CAPAB_INVITATION_PROCEDURE))
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "P2P: Static handover - invite peer " MACSTR
+                  " to join", MAC2STR(params->peer->p2p_device_addr));
+
+       wpa_s->global->p2p_invite_group = wpa_s;
+       persistent = ssid->p2p_persistent_group &&
+               wpas_p2p_get_persistent(wpa_s->parent,
+                                       params->peer->p2p_device_addr,
+                                       ssid->ssid, ssid->ssid_len);
+       wpa_s->parent->pending_invite_ssid_id = -1;
+
+       return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr,
+                         P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr,
+                         ssid->ssid, ssid->ssid_len, ssid->frequency,
+                         wpa_s->global->p2p_dev_addr, persistent, 0,
+                         wpa_s->parent->p2p_oob_dev_pw_id);
 }
 
 
 static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s,
-                                   struct p2p_nfc_params *params)
+                                   struct p2p_nfc_params *params,
+                                   int forced_freq)
 {
        wpa_printf(MSG_DEBUG, "P2P: Initiate GO Negotiation based on NFC "
                   "connection handover");
        return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
                                WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
-                               0, -1, 0, 1, 1);
+                               forced_freq, -1, 0, 1, 1);
 }
 
 
 static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s,
-                                   struct p2p_nfc_params *params)
+                                   struct p2p_nfc_params *params,
+                                   int forced_freq)
 {
        int res;
 
@@ -7135,7 +7278,7 @@ static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s,
                   "connection handover");
        res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
                               WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
-                              0, -1, 0, 1, 1);
+                              forced_freq, -1, 0, 1, 1);
        if (res)
                return res;
 
@@ -7151,7 +7294,7 @@ static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s,
 
 static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s,
                                            const struct wpabuf *data,
-                                           int sel, int tag)
+                                           int sel, int tag, int forced_freq)
 {
        const u8 *pos, *end;
        u16 len, id;
@@ -7212,6 +7355,40 @@ static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s,
        if (res)
                return res;
 
+       if (params.next_step == NO_ACTION)
+               return 0;
+
+       if (params.next_step == BOTH_GO) {
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_BOTH_GO "peer=" MACSTR,
+                       MAC2STR(params.peer->p2p_device_addr));
+               return 0;
+       }
+
+       if (params.next_step == PEER_CLIENT) {
+               if (!is_zero_ether_addr(params.go_dev_addr)) {
+                       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
+                               "peer=" MACSTR " freq=%d go_dev_addr=" MACSTR
+                               " ssid=\"%s\"",
+                               MAC2STR(params.peer->p2p_device_addr),
+                               params.go_freq,
+                               MAC2STR(params.go_dev_addr),
+                               wpa_ssid_txt(params.go_ssid,
+                                            params.go_ssid_len));
+               } else {
+                       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
+                               "peer=" MACSTR " freq=%d",
+                               MAC2STR(params.peer->p2p_device_addr),
+                               params.go_freq);
+               }
+               return 0;
+       }
+
+       if (wpas_p2p_cli_freq(wpa_s, NULL, NULL)) {
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_WHILE_CLIENT "peer="
+                       MACSTR, MAC2STR(params.peer->p2p_device_addr));
+               return 0;
+       }
+
        wpabuf_free(wpa_s->p2p_oob_dev_pw);
        wpa_s->p2p_oob_dev_pw = NULL;
 
@@ -7261,16 +7438,19 @@ static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s,
 
        switch (params.next_step) {
        case NO_ACTION:
+       case BOTH_GO:
+       case PEER_CLIENT:
+               /* already covered above */
                return 0;
        case JOIN_GROUP:
                return wpas_p2p_nfc_join_group(wpa_s, &params);
        case AUTH_JOIN:
-               return wpas_p2p_nfc_auth_join(wpa_s, &params);
+               return wpas_p2p_nfc_auth_join(wpa_s, &params, tag);
        case INIT_GO_NEG:
-               return wpas_p2p_nfc_init_go_neg(wpa_s, &params);
+               return wpas_p2p_nfc_init_go_neg(wpa_s, &params, forced_freq);
        case RESP_GO_NEG:
                /* TODO: use own OOB Dev Pw */
-               return wpas_p2p_nfc_resp_go_neg(wpa_s, &params);
+               return wpas_p2p_nfc_resp_go_neg(wpa_s, &params, forced_freq);
        }
 
        return -1;
@@ -7278,18 +7458,18 @@ static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s,
 
 
 int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
-                            const struct wpabuf *data)
+                            const struct wpabuf *data, int forced_freq)
 {
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return -1;
 
-       return wpas_p2p_nfc_connection_handover(wpa_s, data, 1, 1);
+       return wpas_p2p_nfc_connection_handover(wpa_s, data, 1, 1, forced_freq);
 }
 
 
 int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
                                 const struct wpabuf *req,
-                                const struct wpabuf *sel)
+                                const struct wpabuf *sel, int forced_freq)
 {
        struct wpabuf *tmp;
        int ret;
@@ -7303,13 +7483,16 @@ int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
                          wpabuf_head(req), wpabuf_len(req));
        wpa_hexdump_ascii(MSG_DEBUG, "NFC: Sel",
                          wpabuf_head(sel), wpabuf_len(sel));
+       if (forced_freq)
+               wpa_printf(MSG_DEBUG, "NFC: Forced freq %d", forced_freq);
        tmp = ndef_parse_p2p(init ? sel : req);
        if (tmp == NULL) {
                wpa_printf(MSG_DEBUG, "P2P: Could not parse NDEF");
                return -1;
        }
 
-       ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0);
+       ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0,
+                                              forced_freq);
        wpabuf_free(tmp);
 
        return ret;
@@ -7320,11 +7503,19 @@ int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled)
 {
        const u8 *if_addr;
        int go_intent = wpa_s->conf->p2p_go_intent;
+       struct wpa_supplicant *iface;
 
        if (wpa_s->global->p2p == NULL)
                return -1;
 
        if (!enabled) {
+               wpa_printf(MSG_DEBUG, "P2P: Disable use of own NFC Tag");
+               for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+               {
+                       if (!iface->ap_iface)
+                               continue;
+                       hostapd_wps_nfc_token_disable(iface->ap_iface->bss[0]);
+               }
                p2p_set_authorized_oob_dev_pw_id(wpa_s->global->p2p, 0,
                                                 0, NULL);
                if (wpa_s->p2p_nfc_tag_enabled)
@@ -7345,6 +7536,8 @@ int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled)
                return -1;
        }
 
+       wpa_printf(MSG_DEBUG, "P2P: Enable use of own NFC Tag");
+
        wpa_s->p2p_oob_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
        wpabuf_free(wpa_s->p2p_oob_dev_pw);
        wpa_s->p2p_oob_dev_pw = wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
@@ -7372,6 +7565,27 @@ int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled)
 
        wpa_s->p2p_nfc_tag_enabled = enabled;
 
+       for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+               struct hostapd_data *hapd;
+               if (iface->ap_iface == NULL)
+                       continue;
+               hapd = iface->ap_iface->bss[0];
+               wpabuf_free(hapd->conf->wps_nfc_dh_pubkey);
+               hapd->conf->wps_nfc_dh_pubkey =
+                       wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+               wpabuf_free(hapd->conf->wps_nfc_dh_privkey);
+               hapd->conf->wps_nfc_dh_privkey =
+                       wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+               wpabuf_free(hapd->conf->wps_nfc_dev_pw);
+               hapd->conf->wps_nfc_dev_pw =
+                       wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
+               hapd->conf->wps_nfc_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+
+               if (hostapd_wps_nfc_token_enable(iface->ap_iface->bss[0]) < 0) {
+                       wpa_dbg(iface, MSG_DEBUG,
+                               "P2P: Failed to enable NFC Tag for GO");
+               }
+       }
        p2p_set_authorized_oob_dev_pw_id(
                wpa_s->global->p2p, wpa_s->conf->wps_nfc_dev_pw_id, go_intent,
                if_addr);