Cumulative patch from commit 2e988392436227c51002b573ee27a8cee37f70e9

2e98839 P2P: Disable DNS server from dnsmasq
c07f261 P2P NFC: Add script for connection handover with nfcpy
12288d8 WPS NFC: Protect nfcpy pretty print calls against exceptions
c209dd1 WPS NFC: nfcpy script to use new connection handover design
6202500 WPS NFC: Logging level configuration to wps-nfc.py and wps-ap-nfc.py
1f1b5b3 WPS NFC: Clean up nfcpy script no-wait operations
79ede5a WPS NFC: Validate ctrl_iface response before decoding it
ab1db08 WPS NFC: Use argparse in the nfcpy scripts
6f8fa6e WPS NFC: Update wps-nfc.py and wps-ap-nfc.py to use new nfcpy API
b56f6c8 P2P NFC: Add support for freq option in NFC ctrl_iface commands
91a6501 WPS NFC: Use BSSID and AP Channel from handover select
91226e0 WPS: Add testing option to corrupt public key hash
7312776 WPS NFC: add more debug prints for connection handover report
5cd4f66 WPS NFC: Use AP Channel information from credential container
d2f1837 WPS NFC: Add BSSID and AP channel info to Configuration Token
75dbf98 WPS-STRICT: Update valid Device Password ID and Config Error range
5cd4740 P2P NFC: WPA state machine config with driver-based BSS selection
8e9f53c P2P NFC: Static handover with NFC Tag on client
dd87677 P2P NFC: Enable own NFC Tag on GO Registrar
abe44e3 P2P NFC: Add GO info into handover message when in client role
23318be P2P NFC: Optimize join-a-group operation based on NFC information
86e3208 P2P NFC: Copy DH parameters to a separate group interface
d4b4d7f WPS NFC: Update DH keys for ER operations
ac08752 WPS NFC: Use pubkey mismatch config error from Enrollee
59b45d1 P2P NFC: Add processing of P2P client while NFC handover case
74df9ec P2P NFC: Do not try to join peer if both devices are already GO
201b0f5 P2P: Add test option to disable IP address assignment request
25ef852 P2P: Add support for IP address assignment in 4-way handshake
fdd48ff P2P NFC: Optimize GO Negotiation retries
c4f87a7 P2P NFC: Add NFC tag enabling for static handover
dd37a93 P2P NFC: Report handover select from tag for static handover
db6ae69 P2P NFC: Report connection handover as trigger for P2P
9358878 P2P NFC: Build connection handover messages
c00ab85 P2P NFC: Define WPS_NFC config method
0deab08 P2P NFC: Allow separate WPS/P2P IES to be parsed
fca9958 P2P NFC: Pass OOB Dev Password through P2P parser
ab9e344 P2P NFC: Pass OOB Device Password ID to P2P
5154689 P2P NFC: Add WPS attribute building for P2P NFC
01afd8d P2P NFC: Add NDEF helpers for P2P connection handover messages
9e323a2 P2P NFC: Add OOB GO Negotiation Channel attribute
14d8645 WPS NFC: Allow BSSID and channel to be included in handover select
50d1f89 NFC: Update WPS ER to use the new connection handover design
d950793 WPS NFC: Add support for wpa_supplicant AP/GO mode to use handover
fa4c298 WPS NFC: Process new style handover select
068cdb1 WPS NFC: New style connection handover select from AP/Registrar
3189ca0 WPS NFC: Add AP mode connection handover report
41f9ffb WPS NFC: Build new style carrier record for connection handover request
3f1639d WPS NFC: Split DH key generation to a separate function
9754917 WPS NFC: Update NFC connection handover design
34b6795 WPS NFC: Use abbreviated handshake if both PK hashes delivered OOB
57630e6 WPS: Preparations for allowing SSID filtering for provisioning step
5f45455 WPS NFC: Validate peer public key hash on Enrollee
ff40cd6 WPS NFC: Send M2D with config error 20 on pkhash mismatch
e435417 WPS: Remove Version attribute from NFC messages
72403ec WPS: Add builder functions for AP Channel and RF Bands attributes
ea43ad9 P2P: Make group operating channel available
9f7cd9a P2P: Split add-group-info into a helper function
253f2e3 P2P: Apply unsafe frequency rules to available channels
1682c62 Add a header file defining QCA OUI and vendor extensions

Change-Id: Ia7604d018e1ffb25e06bdc01ce258fc4a0569245
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 8aabfc0..ca347e7 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -211,6 +211,7 @@
 	if (p2p->go_neg_peer) {
 		p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
 		p2p->go_neg_peer->wps_method = WPS_NOT_READY;
+		p2p->go_neg_peer->oob_pw_id = 0;
 	}
 	p2p->go_neg_peer = NULL;
 
@@ -1292,15 +1293,16 @@
 		int go_intent, const u8 *own_interface_addr,
 		unsigned int force_freq, int persistent_group,
 		const u8 *force_ssid, size_t force_ssid_len,
-		int pd_before_go_neg, unsigned int pref_freq)
+		int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id)
 {
 	struct p2p_device *dev;
 
 	p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR
 		"  GO Intent=%d  Intended Interface Address=" MACSTR
-		" wps_method=%d persistent_group=%d pd_before_go_neg=%d",
+		" wps_method=%d persistent_group=%d pd_before_go_neg=%d "
+		"oob_pw_id=%u",
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-		wps_method, persistent_group, pd_before_go_neg);
+		wps_method, persistent_group, pd_before_go_neg, oob_pw_id);
 
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
@@ -1384,6 +1386,7 @@
 	}
 
 	dev->wps_method = wps_method;
+	dev->oob_pw_id = oob_pw_id;
 	dev->status = P2P_SC_SUCCESS;
 
 	if (p2p->p2p_scan_running) {
@@ -1403,15 +1406,15 @@
 		  int go_intent, const u8 *own_interface_addr,
 		  unsigned int force_freq, int persistent_group,
 		  const u8 *force_ssid, size_t force_ssid_len,
-		  unsigned int pref_freq)
+		  unsigned int pref_freq, u16 oob_pw_id)
 {
 	struct p2p_device *dev;
 
 	p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR
 		"  GO Intent=%d  Intended Interface Address=" MACSTR
-		" wps_method=%d  persistent_group=%d",
+		" wps_method=%d  persistent_group=%d oob_pw_id=%u",
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-		wps_method, persistent_group);
+		wps_method, persistent_group, oob_pw_id);
 
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL) {
@@ -1442,6 +1445,7 @@
 	os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
 
 	dev->wps_method = wps_method;
+	dev->oob_pw_id = oob_pw_id;
 	dev->status = P2P_SC_SUCCESS;
 
 	return 0;
@@ -1593,6 +1597,7 @@
 	p2p->ssid_set = 0;
 	peer->go_neg_req_sent = 0;
 	peer->wps_method = WPS_NOT_READY;
+	peer->oob_pw_id = 0;
 
 	p2p_set_state(p2p, P2P_PROVISIONING);
 	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
@@ -1757,7 +1762,8 @@
 	if (p2p->invite_peer == NULL)
 		return;
 	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
-	p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr);
+	p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
+			p2p->invite_dev_pw_id);
 }
 
 
@@ -2459,6 +2465,7 @@
 		p2p->go_neg_peer = NULL;
 
 	dev->wps_method = WPS_NOT_READY;
+	dev->oob_pw_id = 0;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
 
@@ -3122,7 +3129,12 @@
 		p2p_connect_send(p2p, p2p->go_neg_peer);
 		return;
 	}
-
+	if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) {
+		p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)");
+		p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+		p2p_set_timeout(p2p, 0, 30000);
+		return;
+	}
 	p2p_set_state(p2p, P2P_CONNECT_LISTEN);
 	p2p_listen_in_find(p2p, 0);
 }
@@ -3266,7 +3278,7 @@
 	if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) {
 		p2p_set_state(p2p, P2P_INVITE);
 		p2p_invite_send(p2p, p2p->invite_peer,
-				p2p->invite_go_dev_addr);
+				p2p->invite_go_dev_addr, p2p->invite_dev_pw_id);
 	} else {
 		if (p2p->invite_peer) {
 			p2p_dbg(p2p, "Invitation Request retry limit reached");
@@ -3381,6 +3393,8 @@
 		return "Keypad";
 	case WPS_PBC:
 		return "PBC";
+	case WPS_NFC:
+		return "NFC";
 	}
 
 	return "??";
@@ -4362,3 +4376,219 @@
 	va_end(ap);
 	p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf);
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p,
+					      int client_freq,
+					      const u8 *go_dev_addr,
+					      const u8 *ssid, size_t ssid_len)
+{
+	struct wpabuf *buf;
+	u8 op_class, channel;
+	enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP;
+
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL)
+		return NULL;
+
+	op_class = p2p->cfg->reg_class;
+	channel = p2p->cfg->channel;
+
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+	p2p_buf_add_device_info(buf, p2p, NULL);
+
+	if (p2p->num_groups > 0) {
+		role = P2P_GO_IN_A_GROUP;
+		p2p_freq_to_channel(p2p_group_get_freq(p2p->groups[0]),
+				    &op_class, &channel);
+	} else if (client_freq > 0) {
+		role = P2P_CLIENT_IN_A_GROUP;
+		p2p_freq_to_channel(client_freq, &op_class, &channel);
+	}
+
+	p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class,
+				       channel, role);
+
+	if (p2p->num_groups > 0) {
+		/* Limit number of clients to avoid very long message */
+		p2p_buf_add_group_info(p2p->groups[0], buf, 5);
+		p2p_group_buf_add_id(p2p->groups[0], buf);
+	} else if (client_freq > 0 &&
+		   go_dev_addr && !is_zero_ether_addr(go_dev_addr) &&
+		   ssid && ssid_len > 0) {
+		/*
+		 * Add the optional P2P Group ID to indicate in which group this
+		 * device is a P2P Client.
+		 */
+		p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len);
+	}
+
+	return buf;
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len)
+{
+	return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
+				      ssid_len);
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len)
+{
+	return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
+				      ssid_len);
+}
+
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+					struct p2p_nfc_params *params)
+{
+	struct p2p_message msg;
+	struct p2p_device *dev;
+	const u8 *p2p_dev_addr;
+	int freq;
+	enum p2p_role_indication role;
+
+	params->next_step = NO_ACTION;
+
+	if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len,
+				   params->p2p_attr, params->p2p_len, &msg)) {
+		p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC");
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (msg.p2p_device_addr)
+		p2p_dev_addr = msg.p2p_device_addr;
+	else if (msg.device_id)
+		p2p_dev_addr = msg.device_id;
+	else {
+		p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id");
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (msg.oob_dev_password) {
+		os_memcpy(params->oob_dev_pw, msg.oob_dev_password,
+			  msg.oob_dev_password_len);
+		params->oob_dev_pw_len = msg.oob_dev_password_len;
+	}
+
+	dev = p2p_create_device(p2p, p2p_dev_addr);
+	if (dev == NULL) {
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	params->peer = &dev->info;
+
+	os_get_reltime(&dev->last_seen);
+	dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+	p2p_copy_wps_info(p2p, dev, 0, &msg);
+
+	if (!msg.oob_go_neg_channel) {
+		p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included");
+		return -1;
+	}
+
+	if (msg.oob_go_neg_channel[3] == 0 &&
+	    msg.oob_go_neg_channel[4] == 0)
+		freq = 0;
+	else
+		freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3],
+					   msg.oob_go_neg_channel[4]);
+	if (freq < 0) {
+		p2p_dbg(p2p, "Unknown peer OOB GO Neg channel");
+		return -1;
+	}
+	role = msg.oob_go_neg_channel[5];
+
+	if (role == P2P_GO_IN_A_GROUP) {
+		p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq);
+		params->go_freq = freq;
+	} else if (role == P2P_CLIENT_IN_A_GROUP) {
+		p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz",
+			freq);
+		params->go_freq = freq;
+	} else
+		p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq);
+	dev->oob_go_neg_freq = freq;
+
+	if (!params->sel && role != P2P_GO_IN_A_GROUP) {
+		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+		if (freq < 0) {
+			p2p_dbg(p2p, "Own listen channel not known");
+			return -1;
+		}
+		p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq);
+		dev->oob_go_neg_freq = freq;
+	}
+
+	if (msg.group_id) {
+		os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN);
+		params->go_ssid_len = msg.group_id_len - ETH_ALEN;
+		os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN,
+			  params->go_ssid_len);
+	}
+
+	p2p_parse_free(&msg);
+
+	if (dev->flags & P2P_DEV_USER_REJECTED) {
+		p2p_dbg(p2p, "Do not report rejected device");
+		return 0;
+	}
+
+	if (!(dev->flags & P2P_DEV_REPORTED)) {
+		p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info,
+				    !(dev->flags & P2P_DEV_REPORTED_ONCE));
+		dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+	}
+
+	if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0)
+		params->next_step = BOTH_GO;
+	else if (role == P2P_GO_IN_A_GROUP)
+		params->next_step = JOIN_GROUP;
+	else if (role == P2P_CLIENT_IN_A_GROUP) {
+		dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
+		params->next_step = PEER_CLIENT;
+	} else if (p2p->num_groups > 0)
+		params->next_step = AUTH_JOIN;
+	else if (params->sel)
+		params->next_step = INIT_GO_NEG;
+	else
+		params->next_step = RESP_GO_NEG;
+
+	return 0;
+}
+
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+				      int go_intent,
+				      const u8 *own_interface_addr)
+{
+
+	p2p->authorized_oob_dev_pw_id = dev_pw_id;
+	if (dev_pw_id == 0) {
+		p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover");
+		return;
+	}
+
+	p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover",
+		dev_pw_id);
+
+	p2p->go_intent = go_intent;
+	os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 2ce6ea6..08e7176 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -9,6 +9,8 @@
 #ifndef P2P_H
 #define P2P_H
 
+#include "wps/wps_defs.h"
+
 /**
  * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
  */
@@ -50,7 +52,7 @@
 };
 
 enum p2p_wps_method {
-	WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC
+	WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC
 };
 
 /**
@@ -706,6 +708,8 @@
 	 *	persistent group (instead of invitation to join an active
 	 *	group)
 	 * @channels: Available operating channels for the group
+	 * @dev_pw_id: Device Password ID for NFC static handover or -1 if not
+	 *	used
 	 * Returns: Status code (P2P_SC_*)
 	 *
 	 * This optional callback can be used to implement persistent reconnect
@@ -727,7 +731,8 @@
 				 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);
 
 	/**
 	 * invitation_received - Callback on Invitation Request RX
@@ -972,7 +977,7 @@
 		int go_intent, const u8 *own_interface_addr,
 		unsigned int force_freq, int persistent_group,
 		const u8 *force_ssid, size_t force_ssid_len,
-		int pd_before_go_neg, unsigned int pref_freq);
+		int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id);
 
 /**
  * p2p_authorize - Authorize P2P group formation (GO negotiation)
@@ -1000,7 +1005,7 @@
 		  int go_intent, const u8 *own_interface_addr,
 		  unsigned int force_freq, int persistent_group,
 		  const u8 *force_ssid, size_t force_ssid_len,
-		  unsigned int pref_freq);
+		  unsigned int pref_freq, u16 oob_pw_id);
 
 /**
  * p2p_reject - Reject peer device (explicitly block connection attempts)
@@ -1102,12 +1107,14 @@
  * @persistent_group: Whether this is to reinvoke a persistent group
  * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
  *	force_freq == 0)
+ * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover
+ *	case or -1 if not used
  * Returns: 0 on success, -1 on failure
  */
 int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
 	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
 	       unsigned int force_freq, const u8 *go_dev_addr,
-	       int persistent_group, unsigned int pref_freq);
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id);
 
 /**
  * p2p_presence_req - Request GO presence
@@ -1372,6 +1379,11 @@
 	size_t ssid_len;
 
 	/**
+	 * freq - Operating channel of the group
+	 */
+	int freq;
+
+	/**
 	 * cb_ctx - Context to use with callback functions
 	 */
 	void *cb_ctx;
@@ -1892,4 +1904,41 @@
  */
 const char * p2p_get_state_txt(struct p2p_data *p2p);
 
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len);
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len);
+
+struct p2p_nfc_params {
+	int sel;
+	const u8 *wsc_attr;
+	size_t wsc_len;
+	const u8 *p2p_attr;
+	size_t p2p_len;
+
+	enum {
+		NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG,
+		BOTH_GO, PEER_CLIENT
+	} next_step;
+	struct p2p_peer_info *peer;
+	u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 +
+		      WPS_OOB_DEVICE_PASSWORD_LEN];
+	size_t oob_dev_pw_len;
+	int go_freq;
+	u8 go_dev_addr[ETH_ALEN];
+	u8 go_ssid[32];
+	size_t go_ssid_len;
+};
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+					struct p2p_nfc_params *params);
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+				      int go_intent,
+				      const u8 *own_interface_addr);
+
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 42c0232..664fade 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -328,6 +328,23 @@
 }
 
 
+void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+				    u8 oper_class, u8 channel,
+				    enum p2p_role_indication role)
+{
+	/* OOB Group Owner Negotiation Channel */
+	wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL);
+	wpabuf_put_le16(buf, 6);
+	wpabuf_put_data(buf, country, 3);
+	wpabuf_put_u8(buf, oper_class); /* Operating Class */
+	wpabuf_put_u8(buf, channel); /* Channel Number */
+	wpabuf_put_u8(buf, (u8) role); /* Role indication */
+	wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating "
+		   "Class %u Channel %u Role %d",
+		   oper_class, channel, role);
+}
+
+
 static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
 			      const char *val)
 {
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index a887a5e..76436f5 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -103,6 +103,8 @@
 		return DEV_PW_USER_SPECIFIED;
 	case WPS_PBC:
 		return DEV_PW_PUSHBUTTON;
+	case WPS_NFC:
+		return DEV_PW_NFC_CONNECTION_HANDOVER;
 	default:
 		return DEV_PW_DEFAULT;
 	}
@@ -118,6 +120,8 @@
 		return "Keypad";
 	case WPS_PBC:
 		return "PBC";
+	case WPS_NFC:
+		return "NFC";
 	default:
 		return "??";
 	}
@@ -131,6 +135,7 @@
 	u8 *len;
 	u8 group_capab;
 	size_t extra = 0;
+	u16 pw_id;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_go_neg)
@@ -172,8 +177,10 @@
 	p2p_buf_update_ie_hdr(buf, len);
 
 	/* WPS IE with Device Password ID attribute */
-	if (p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method),
-			     0) < 0) {
+	pw_id = p2p_wps_method_pw_id(peer->wps_method);
+	if (peer->oob_pw_id)
+		pw_id = peer->oob_pw_id;
+	if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
 		p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request");
 		wpabuf_free(buf);
 		return NULL;
@@ -210,6 +217,8 @@
 	}
 
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (dev->oob_go_neg_freq > 0)
+		freq = dev->oob_go_neg_freq;
 	if (freq <= 0) {
 		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
 			MACSTR " to send GO Negotiation Request",
@@ -250,6 +259,7 @@
 	u8 *len;
 	u8 group_capab;
 	size_t extra = 0;
+	u16 pw_id;
 
 	p2p_dbg(p2p, "Building GO Negotiation Response");
 
@@ -312,9 +322,10 @@
 	p2p_buf_update_ie_hdr(buf, len);
 
 	/* WPS IE with Device Password ID attribute */
-	if (p2p_build_wps_ie(p2p, buf,
-			     p2p_wps_method_pw_id(peer ? peer->wps_method :
-						  WPS_NOT_READY), 0) < 0) {
+	pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY);
+	if (peer && peer->oob_pw_id)
+		pw_id = peer->oob_pw_id;
+	if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
 		p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response");
 		wpabuf_free(buf);
 		return NULL;
@@ -605,7 +616,11 @@
 	if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
 		p2p_dbg(p2p, "User has rejected this peer");
 		status = P2P_SC_FAIL_REJECTED_BY_USER;
-	} else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+	} else if (dev == NULL ||
+		   (dev->wps_method == WPS_NOT_READY &&
+		    (p2p->authorized_oob_dev_pw_id == 0 ||
+		     p2p->authorized_oob_dev_pw_id !=
+		     msg.dev_password_id))) {
 		p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
 			MAC2STR(sa));
 		status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
@@ -692,6 +707,28 @@
 			}
 			break;
 		default:
+			if (msg.dev_password_id &&
+			    msg.dev_password_id == dev->oob_pw_id) {
+				p2p_dbg(p2p, "Peer using NFC");
+				if (dev->wps_method != WPS_NFC) {
+					p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+						p2p_wps_method_str(
+							dev->wps_method));
+					status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+					goto fail;
+				}
+				break;
+			}
+#ifdef CONFIG_WPS_NFC
+			if (p2p->authorized_oob_dev_pw_id &&
+			    msg.dev_password_id ==
+			    p2p->authorized_oob_dev_pw_id) {
+				p2p_dbg(p2p, "Using static handover with our device password from NFC Tag");
+				dev->wps_method = WPS_NFC;
+				dev->oob_pw_id = p2p->authorized_oob_dev_pw_id;
+				break;
+			}
+#endif /* CONFIG_WPS_NFC */
 			p2p_dbg(p2p, "Unsupported Device Password ID %d",
 				msg.dev_password_id);
 			status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
@@ -1017,6 +1054,17 @@
 		}
 		break;
 	default:
+		if (msg.dev_password_id &&
+		    msg.dev_password_id == dev->oob_pw_id) {
+			p2p_dbg(p2p, "Peer using NFC");
+			if (dev->wps_method != WPS_NFC) {
+				p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+					p2p_wps_method_str(dev->wps_method));
+				status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+				goto fail;
+			}
+			break;
+		}
 		p2p_dbg(p2p, "Unsupported Device Password ID %d",
 			msg.dev_password_id);
 		status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 92b5583..395ca08 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -154,6 +154,7 @@
 		group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
 	if (group->num_members >= group->cfg->max_clients)
 		group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
+	group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION;
 	p2p_buf_add_capability(ie, dev_capab, group_capab);
 }
 
@@ -397,10 +398,38 @@
 #endif /* CONFIG_WIFI_DISPLAY */
 
 
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+			    int max_clients)
+{
+	u8 *group_info;
+	int count = 0;
+	struct p2p_group_member *m;
+
+	p2p_dbg(group->p2p, "* P2P Group Info");
+	group_info = wpabuf_put(buf, 0);
+	wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO);
+	wpabuf_put_le16(buf, 0); /* Length to be filled */
+	for (m = group->members; m; m = m->next) {
+		p2p_client_info(buf, m);
+		count++;
+		if (max_clients >= 0 && count >= max_clients)
+			break;
+	}
+	WPA_PUT_LE16(group_info + 1,
+		     (u8 *) wpabuf_put(buf, 0) - group_info - 3);
+}
+
+
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf)
+{
+	p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid,
+			     group->cfg->ssid_len);
+}
+
+
 static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
 {
 	struct wpabuf *p2p_subelems, *ie;
-	struct p2p_group_member *m;
 
 	p2p_subelems = wpabuf_alloc(500);
 	if (p2p_subelems == NULL)
@@ -413,17 +442,8 @@
 	p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL);
 
 	/* P2P Group Info: Only when at least one P2P Client is connected */
-	if (group->members) {
-		u8 *group_info;
-		group_info = wpabuf_put(p2p_subelems, 0);
-		wpabuf_put_u8(p2p_subelems, P2P_ATTR_GROUP_INFO);
-		wpabuf_put_le16(p2p_subelems, 0); /* Length to be filled */
-		for (m = group->members; m; m = m->next)
-			p2p_client_info(p2p_subelems, m);
-		WPA_PUT_LE16(group_info + 1,
-			     (u8 *) wpabuf_put(p2p_subelems, 0) - group_info -
-			     3);
-	}
+	if (group->members)
+		p2p_buf_add_group_info(group, p2p_subelems, -1);
 
 	ie = p2p_group_encaps_probe_resp(p2p_subelems);
 	wpabuf_free(p2p_subelems);
@@ -987,3 +1007,9 @@
 	group->beacon_update = 1;
 	p2p_group_update_ies(group);
 }
+
+
+int p2p_group_get_freq(struct p2p_group *group)
+{
+	return group->cfg->freq;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 3e105eb..6ebaa84 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -12,6 +12,8 @@
 #include "utils/list.h"
 #include "p2p.h"
 
+enum p2p_role_indication;
+
 enum p2p_go_state {
 	UNKNOWN_GO,
 	LOCAL_GO,
@@ -25,7 +27,9 @@
 	struct dl_list list;
 	struct os_reltime last_seen;
 	int listen_freq;
+	int oob_go_neg_freq;
 	enum p2p_wps_method wps_method;
+	u16 oob_pw_id;
 
 	struct p2p_peer_info info;
 
@@ -239,6 +243,7 @@
 
 	const u8 *invite_go_dev_addr;
 	u8 invite_go_dev_addr_buf[ETH_ALEN];
+	int invite_dev_pw_id;
 
 	/**
 	 * sd_peer - Pointer to Service Discovery peer
@@ -462,6 +467,8 @@
 	struct wpabuf *wfd_assoc_bssid;
 	struct wpabuf *wfd_coupled_sink_info;
 #endif /* CONFIG_WIFI_DISPLAY */
+
+	u16 authorized_oob_dev_pw_id;
 };
 
 /**
@@ -503,6 +510,8 @@
 
 	const u8 *minor_reason_code;
 
+	const u8 *oob_go_neg_channel;
+
 	/* P2P Device Info */
 	const u8 *p2p_device_info;
 	size_t p2p_device_info_len;
@@ -514,6 +523,7 @@
 
 	/* WPS IE */
 	u16 dev_password_id;
+	int dev_password_id_present;
 	u16 wps_config_methods;
 	const u8 *wps_pri_dev_type;
 	const u8 *wps_sec_dev_type_list;
@@ -528,6 +538,8 @@
 	size_t model_number_len;
 	const u8 *serial_number;
 	size_t serial_number_len;
+	const u8 *oob_dev_password;
+	size_t oob_dev_password_len;
 
 	/* DS Parameter Set IE */
 	const u8 *ds_params;
@@ -578,6 +590,8 @@
 int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
 int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg);
 int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg);
+int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
+			   size_t p2p_len, struct p2p_message *msg);
 void p2p_parse_free(struct p2p_message *msg);
 int p2p_attr_text(struct wpabuf *data, char *buf, char *end);
 int p2p_group_info_parse(const u8 *gi, size_t gi_len,
@@ -602,6 +616,10 @@
 void p2p_group_update_ies(struct p2p_group *group);
 void p2p_group_force_beacon_update_ies(struct p2p_group *group);
 struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g);
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+			    int max_clients);
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf);
+int p2p_group_get_freq(struct p2p_group *group);
 
 
 void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
@@ -633,6 +651,9 @@
 void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
 				   u16 interval);
 void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
+void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+				    u8 oper_class, u8 channel,
+				    enum p2p_role_indication role);
 int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
 		     int all_attr);
 
@@ -680,7 +701,7 @@
 void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
 				 const u8 *data, size_t len);
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
-		    const u8 *go_dev_addr);
+		    const u8 *go_dev_addr, int dev_pw_id);
 void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
 void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
 
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 2734386..98cfb33 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -16,7 +16,8 @@
 
 static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
 						struct p2p_device *peer,
-						const u8 *go_dev_addr)
+						const u8 *go_dev_addr,
+						int dev_pw_id)
 {
 	struct wpabuf *buf;
 	u8 *len;
@@ -85,6 +86,11 @@
 		wpabuf_put_buf(buf, wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (dev_pw_id >= 0) {
+		/* WSC IE in Invitation Request for NFC static handover */
+		p2p_build_wps_ie(p2p, buf, dev_pw_id, 0);
+	}
+
 	return buf;
 }
 
@@ -228,7 +234,8 @@
 		status = p2p->cfg->invitation_process(
 			p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id,
 			msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN,
-			&go, group_bssid, &op_freq, persistent, &intersection);
+			&go, group_bssid, &op_freq, persistent, &intersection,
+			msg.dev_password_id_present ? msg.dev_password_id : -1);
 	}
 
 	if (op_freq) {
@@ -450,12 +457,14 @@
 
 
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
-		    const u8 *go_dev_addr)
+		    const u8 *go_dev_addr, int dev_pw_id)
 {
 	struct wpabuf *req;
 	int freq;
 
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (freq <= 0)
+		freq = dev->oob_go_neg_freq;
 	if (freq <= 0) {
 		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
 			MACSTR " to send Invitation Request",
@@ -463,7 +472,7 @@
 		return -1;
 	}
 
-	req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
+	req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id);
 	if (req == NULL)
 		return -1;
 	if (p2p->state != P2P_IDLE)
@@ -528,7 +537,7 @@
 int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
 	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
 	       unsigned int force_freq, const u8 *go_dev_addr,
-	       int persistent_group, unsigned int pref_freq)
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id)
 {
 	struct p2p_device *dev;
 
@@ -546,9 +555,15 @@
 		p2p->invite_go_dev_addr = NULL;
 	wpa_hexdump_ascii(MSG_DEBUG, "Invitation for SSID",
 			  ssid, ssid_len);
+	if (dev_pw_id >= 0) {
+		p2p_dbg(p2p, "Invitation to use Device Password ID %d",
+			dev_pw_id);
+	}
+	p2p->invite_dev_pw_id = dev_pw_id;
 
 	dev = p2p_get_device(p2p, peer);
-	if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
+	if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 &&
+			    dev->oob_go_neg_freq <= 0)) {
 		p2p_dbg(p2p, "Cannot invite unknown P2P Device " MACSTR,
 			MAC2STR(peer));
 		return -1;
@@ -586,5 +601,5 @@
 	os_memcpy(p2p->inv_ssid, ssid, ssid_len);
 	p2p->inv_ssid_len = ssid_len;
 	p2p->inv_persistent = persistent_group;
-	return p2p_invite_send(p2p, dev, go_dev_addr);
+	return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id);
 }
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index 097a31d..d6144a0 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -268,6 +268,19 @@
 		wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u",
 			   *msg->minor_reason_code);
 		break;
+	case P2P_ATTR_OOB_GO_NEG_CHANNEL:
+		if (len < 6) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short OOB GO Neg "
+				   "Channel attribute (length %d)", len);
+			return -1;
+		}
+		msg->oob_go_neg_channel = data;
+		wpa_printf(MSG_DEBUG, "P2P: * OOB GO Neg Channel: "
+			   "Country %c%c(0x%02x) Operating Class %d "
+			   "Channel Number %d Role %d",
+			   data[0], data[1], data[2], data[3], data[4],
+			   data[5]);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
 			   "(length %d)", id, len);
@@ -340,6 +353,7 @@
 		msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id);
 		wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d",
 			   msg->dev_password_id);
+		msg->dev_password_id_present = 1;
 	}
 	if (attr.primary_dev_type) {
 		char devtype[WPS_DEV_TYPE_BUFSIZE];
@@ -367,6 +381,9 @@
 	msg->serial_number = attr.serial_number;
 	msg->serial_number_len = attr.serial_number_len;
 
+	msg->oob_dev_password = attr.oob_dev_password;
+	msg->oob_dev_password_len = attr.oob_dev_password_len;
+
 	return 0;
 }
 
@@ -450,6 +467,33 @@
 }
 
 
+int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
+			   size_t p2p_len, struct p2p_message *msg)
+{
+	os_memset(msg, 0, sizeof(*msg));
+
+	msg->wps_attributes = wpabuf_alloc_copy(wsc, wsc_len);
+	if (msg->wps_attributes &&
+	    p2p_parse_wps_ie(msg->wps_attributes, msg)) {
+		p2p_parse_free(msg);
+		return -1;
+	}
+
+	msg->p2p_attributes = wpabuf_alloc_copy(p2p, p2p_len);
+	if (msg->p2p_attributes &&
+	    p2p_parse_p2p_ie(msg->p2p_attributes, msg)) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data");
+		if (msg->p2p_attributes)
+			wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data",
+					msg->p2p_attributes);
+		p2p_parse_free(msg);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 /**
  * p2p_parse_free - Free temporary data from P2P parsing
  * @msg: Parsed attributes