Cumulative patch from commit 905828fea4b95a6d48ce86e1b5272c25a618b3d8

905828f hostapd: Fix vht_capab 'Maximum A-MPDU Length Exponent' handling
89de64c ACS: Fix VHT80 segment picking
1f37483 DFS: Print error in case CAC fails
354c903 AP/GO interface teardown optimization
8bc4372 Use P2P_IE_VENDOR_TYPE more consistently
8714caa WPS: Parse Registrar Configuration Methods
6b9f7af nl80211: Extend the new vendor command for testing nl80211
3a94adb P2P: Do not start scan for P2P Device interfaces at driver init
aa10983 P2P: Do not initialize bgscan on P2P interfaces
819f096 nl80211: Fix RTM event handling for dynamic interfaces
54ac5aa config: Add bgscan option when saving global configuration
268043d bgscan: Do not initialize bgscan if disabled by user
adef894 nl80211: Add vendor command support
d0595b2 nl80211: Fix tearing down WDS STA interfaces

Change-Id: I6d49f445692b71a4cd324f517eba651518ee14bb
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/acs.c b/src/ap/acs.c
index f58b091..60b7580 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -367,6 +367,19 @@
 }
 
 
+static int acs_usable_vht80_chan(struct hostapd_channel_data *chan)
+{
+	const int allowed[] = { 36, 52, 100, 116, 132, 149 };
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(allowed); i++)
+		if (chan->chan == allowed[i])
+			return 1;
+
+	return 0;
+}
+
+
 static int acs_survey_is_sufficient(struct freq_survey *survey)
 {
 	if (!(survey->filled & SURVEY_HAS_NF)) {
@@ -541,6 +554,15 @@
 			continue;
 		}
 
+		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+		    iface->conf->ieee80211ac &&
+		    iface->conf->vht_oper_chwidth == 1 &&
+		    !acs_usable_vht80_chan(chan)) {
+			wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80",
+				   chan->chan);
+			continue;
+		}
+
 		factor = 0;
 		if (acs_usable_chan(chan))
 			factor = chan->interference_factor;
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index b8b260a..5ba48c9 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -767,8 +767,10 @@
 				    vht_enabled, sec_channel_offset,
 				    vht_oper_chwidth, center_segment0,
 				    center_segment1,
-				    iface->current_mode->vht_capab))
+				    iface->current_mode->vht_capab)) {
+		wpa_printf(MSG_ERROR, "Can't set freq params");
 		return -1;
+	}
 
 	res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
 	if (!res)
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index a959694..a7129f1 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -16,6 +16,7 @@
 #include "wpa_auth.h"
 #include "sta_info.h"
 #include "ap_mlme.h"
+#include "hostapd.h"
 
 
 #ifndef CONFIG_NO_HOSTAPD_LOGGER
@@ -80,7 +81,8 @@
 		       HOSTAPD_LEVEL_DEBUG,
 		       "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)",
 		       MAC2STR(sta->addr), reason_code);
-	mlme_deletekeys_request(hapd, sta);
+	if (!hapd->iface->driver_ap_teardown)
+		mlme_deletekeys_request(hapd, sta);
 }
 
 
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 8c0cbab..c27cf3b 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -250,9 +250,8 @@
 
 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
 	*pos++ = 4 + 3 + 1;
-	WPA_PUT_BE24(pos, OUI_WFA);
-	pos += 3;
-	*pos++ = P2P_OUI_TYPE;
+	WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
+	pos += 4;
 
 	*pos++ = P2P_ATTR_MINOR_REASON_CODE;
 	WPA_PUT_LE16(pos, 1);
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 2b8b90f..92eda21 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -608,19 +608,25 @@
 	hostapd_set_state(iface, HAPD_IFACE_DFS);
 	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
-		"freq=%d chan=%d sec_chan=%d",
+		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d",
 		iface->freq,
-		iface->conf->channel, iface->conf->secondary_channel);
-	if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
-				  iface->freq,
-				  iface->conf->channel,
-				  iface->conf->ieee80211n,
-				  iface->conf->ieee80211ac,
-				  iface->conf->secondary_channel,
-				  iface->conf->vht_oper_chwidth,
-				  iface->conf->vht_oper_centr_freq_seg0_idx,
-				  iface->conf->vht_oper_centr_freq_seg1_idx)) {
-		wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed");
+		iface->conf->channel, iface->conf->secondary_channel,
+		iface->conf->vht_oper_chwidth,
+		iface->conf->vht_oper_centr_freq_seg0_idx,
+		iface->conf->vht_oper_centr_freq_seg1_idx);
+
+	res = hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+				    iface->freq,
+				    iface->conf->channel,
+				    iface->conf->ieee80211n,
+				    iface->conf->ieee80211ac,
+				    iface->conf->secondary_channel,
+				    iface->conf->vht_oper_chwidth,
+				    iface->conf->vht_oper_centr_freq_seg0_idx,
+				    iface->conf->vht_oper_centr_freq_seg1_idx);
+
+	if (res) {
+		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
 		return -1;
 	}
 
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index ad1c2d0..6ba6f98 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -350,7 +350,7 @@
 
 static void hostapd_clear_wep(struct hostapd_data *hapd)
 {
-	if (hapd->drv_priv) {
+	if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) {
 		hostapd_set_privacy(hapd, 0);
 		hostapd_broadcast_wep_clear(hapd);
 	}
@@ -401,11 +401,15 @@
 	if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
 		return 0;
 
-	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Flushing old station entries");
-	if (hostapd_flush(hapd)) {
-		wpa_msg(hapd->msg_ctx, MSG_WARNING, "Could not connect to "
-			"kernel driver");
-		ret = -1;
+	if (!hapd->iface->driver_ap_teardown) {
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+			"Flushing old station entries");
+
+		if (hostapd_flush(hapd)) {
+			wpa_msg(hapd->msg_ctx, MSG_WARNING,
+				"Could not connect to kernel driver");
+			ret = -1;
+		}
 	}
 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations");
 	os_memset(addr, 0xff, ETH_ALEN);
@@ -1009,6 +1013,15 @@
 	struct hostapd_data *hapd = iface->bss[0];
 	size_t i;
 
+	/*
+	 * It is possible that setup_interface() is called after the interface
+	 * was disabled etc., in which case driver_ap_teardown is possibly set
+	 * to 1. Clear it here so any other key/station deletion, which is not
+	 * part of a teardown flow, would also call the relevant driver
+	 * callbacks.
+	 */
+	iface->driver_ap_teardown = 0;
+
 	if (!iface->phy[0]) {
 		const char *phy = hostapd_drv_get_radio_name(hapd);
 		if (phy) {
@@ -1627,7 +1640,11 @@
 	driver = hapd_iface->bss[0]->driver;
 	drv_priv = hapd_iface->bss[0]->drv_priv;
 
-	/* whatever hostapd_interface_deinit does */
+	hapd_iface->driver_ap_teardown =
+		!!(hapd_iface->drv_flags &
+		   WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
+	/* same as hostapd_interface_deinit without deinitializing ctrl-iface */
 	for (j = 0; j < hapd_iface->num_bss; j++) {
 		struct hostapd_data *hapd = hapd_iface->bss[j];
 		hostapd_free_stas(hapd);
@@ -1943,6 +1960,10 @@
 			return -1;
 		if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
 			wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+			hapd_iface->driver_ap_teardown =
+				!!(hapd_iface->drv_flags &
+				   WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
 			hostapd_interface_deinit_free(hapd_iface);
 			k = i;
 			while (k < (interfaces->count - 1)) {
@@ -1955,8 +1976,12 @@
 		}
 
 		for (j = 0; j < hapd_iface->conf->num_bss; j++) {
-			if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf))
+			if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) {
+				hapd_iface->driver_ap_teardown =
+					!(hapd_iface->drv_flags &
+					  WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
 				return hostapd_remove_bss(hapd_iface, j);
+			}
 		}
 	}
 	return -1;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 489ab16..be7df51 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -273,6 +273,12 @@
 	unsigned int wait_channel_update:1;
 	unsigned int cac_started:1;
 
+	/*
+	 * When set, indicates that the driver will handle the AP
+	 * teardown: delete global keys, station keys, and stations.
+	 */
+	unsigned int driver_ap_teardown:1;
+
 	int num_ap; /* number of entries in ap_list */
 	struct ap_info *ap_list; /* AP info list head */
 	struct ap_info *ap_hash[STA_HASH_SIZE];
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index af1bc9b..28e92fd 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -764,7 +764,7 @@
 	VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
 	VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
 	VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
-	VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT);
+	VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX);
 	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
 	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
 	VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
diff --git a/src/ap/p2p_hostapd.c b/src/ap/p2p_hostapd.c
index 795d313..9be640c 100644
--- a/src/ap/p2p_hostapd.c
+++ b/src/ap/p2p_hostapd.c
@@ -96,9 +96,8 @@
 	u8 bitmap;
 	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
 	*eid++ = 4 + 3 + 1;
-	WPA_PUT_BE24(eid, OUI_WFA);
-	eid += 3;
-	*eid++ = P2P_OUI_TYPE;
+	WPA_PUT_BE32(eid, P2P_IE_VENDOR_TYPE);
+	eid += 4;
 
 	*eid++ = P2P_ATTR_MANAGEABILITY;
 	WPA_PUT_LE16(eid, 1);
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index f7af088..0b90e3b 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -156,7 +156,8 @@
 	if (sta->flags & WLAN_STA_WDS)
 		hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
 
-	if (!(sta->flags & WLAN_STA_PREAUTH))
+	if (!hapd->iface->driver_ap_teardown &&
+	    !(sta->flags & WLAN_STA_PREAUTH))
 		hostapd_drv_sta_remove(hapd, sta->addr);
 
 	ap_sta_hash_del(hapd, sta);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index cf4664f..dcee679 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -758,7 +758,14 @@
 #define VHT_CAP_MU_BEAMFORMEE_CAPABLE               ((u32) BIT(20))
 #define VHT_CAP_VHT_TXOP_PS                         ((u32) BIT(21))
 #define VHT_CAP_HTC_VHT                             ((u32) BIT(22))
-#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT          ((u32) BIT(23) | \
+
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1        ((u32) BIT(23))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2        ((u32) BIT(24))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3        ((u32) BIT(23) | BIT(24))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4        ((u32) BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5        ((u32) BIT(23) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6        ((u32) BIT(24) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX      ((u32) BIT(23) | \
 							   BIT(24) | BIT(25))
 #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB   ((u32) BIT(27))
 #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB     ((u32) BIT(26) | BIT(27))
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index d2aad24..6b6c0ef 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -914,7 +914,8 @@
 #define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE	0x00000400
 /* This interface is P2P capable (P2P GO or P2P Client) */
 #define WPA_DRIVER_FLAGS_P2P_CAPABLE	0x00000800
-/* unused: 0x00001000 */
+/* Driver supports station and key removal when stopping an AP */
+#define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT		0x00001000
 /*
  * Driver uses the initial interface for P2P management interface and non-P2P
  * purposes (e.g., connect to infra AP), but this interface cannot be used for
@@ -2520,6 +2521,30 @@
 #endif /* ANDROID */
 
 	/**
+	 * vendor_cmd - Execute vendor specific command
+	 * @priv: Private driver interface data
+	 * @vendor_id: Vendor id
+	 * @subcmd: Vendor command id
+	 * @data: Vendor command parameters (%NULL if no parameters)
+	 * @data_len: Data length
+	 * @buf: Return buffer (%NULL to ignore reply)
+	 * Returns: 0 on success, negative (<0) on failure
+	 *
+	 * This function handles vendor specific commands that are passed to
+	 * the driver/device. The command is identified by vendor id and
+	 * command id. Parameters can be passed as argument to the command
+	 * in the data buffer. Reply (if any) will be filled in the supplied
+	 * return buffer.
+	 *
+	 * The exact driver behavior is driver interface and vendor specific. As
+	 * an example, this will be converted to a vendor specific cfg80211
+	 * command in case of the nl80211 driver interface.
+	 */
+	int (*vendor_cmd)(void *priv, unsigned int vendor_id,
+			  unsigned int subcmd, const u8 *data, size_t data_len,
+			  struct wpabuf *buf);
+
+	/**
 	 * set_rekey_info - Set rekey information
 	 * @priv: Private driver interface data
 	 * @kek: Current KEK
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index c509170..87c9661 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -1239,6 +1239,16 @@
 			drv->if_disabled = 1;
 			wpa_supplicant_event(drv->ctx,
 					     EVENT_INTERFACE_DISABLED, NULL);
+
+			/*
+			 * Try to get drv again, since it may be removed as
+			 * part of the EVENT_INTERFACE_DISABLED handling for
+			 * dynamic interfaces
+			 */
+			drv = nl80211_find_drv(global, ifi->ifi_index,
+					       buf, len);
+			if (!drv)
+				return;
 		}
 	}
 
@@ -3808,6 +3818,15 @@
 	drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
 	drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
 
+	/*
+	 * As all cfg80211 drivers must support cases where the AP interface is
+	 * removed without the knowledge of wpa_supplicant/hostapd, e.g., in
+	 * case that the user space daemon has crashed, they must be able to
+	 * cleanup all stations and key entries in the AP tear down flow. Thus,
+	 * this flag can/should always be set for cfg80211 drivers.
+	 */
+	drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT;
+
 	if (!info.device_ap_sme) {
 		drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
 
@@ -9431,8 +9450,8 @@
 					name);
 
 		i802_set_sta_vlan(priv, addr, bss->ifname, 0);
-		return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
-						    name);
+		nl80211_remove_iface(drv, if_nametoindex(name));
+		return 0;
 	}
 }
 
@@ -11710,6 +11729,106 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+static int cmd_reply_handler(struct nl_msg *msg, void *arg)
+{
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpabuf *buf = arg;
+
+	if (!buf)
+		return NL_SKIP;
+
+	if ((size_t) genlmsg_attrlen(gnlh, 0) > wpabuf_tailroom(buf)) {
+		wpa_printf(MSG_INFO, "nl80211: insufficient buffer space for reply");
+		return NL_SKIP;
+	}
+
+	wpabuf_put_data(buf, genlmsg_attrdata(gnlh, 0),
+			genlmsg_attrlen(gnlh, 0));
+
+	return NL_SKIP;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static int vendor_reply_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct nlattr *nl_vendor_reply, *nl;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpabuf *buf = arg;
+	int rem;
+
+	if (!buf)
+		return NL_SKIP;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	nl_vendor_reply = tb[NL80211_ATTR_VENDOR_DATA];
+
+	if (!nl_vendor_reply)
+		return NL_SKIP;
+
+	if ((size_t) nla_len(nl_vendor_reply) > wpabuf_tailroom(buf)) {
+		wpa_printf(MSG_INFO, "nl80211: Vendor command: insufficient buffer space for reply");
+		return NL_SKIP;
+	}
+
+	nla_for_each_nested(nl, nl_vendor_reply, rem) {
+		wpabuf_put_data(buf, nla_data(nl), nla_len(nl));
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
+			      unsigned int subcmd, const u8 *data,
+			      size_t data_len, struct wpabuf *buf)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (vendor_id == 0xffffffff) {
+		nl80211_cmd(drv, msg, 0, subcmd);
+		if (nlmsg_append(msg, (void *) data, data_len, NLMSG_ALIGNTO) <
+		    0)
+			goto nla_put_failure;
+		ret = send_and_recv_msgs(drv, msg, cmd_reply_handler, buf);
+		if (ret)
+			wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
+				   ret);
+		return ret;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR);
+	if (nl80211_set_iface_id(msg, bss) < 0)
+		goto nla_put_failure;
+	NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, vendor_id);
+	NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd);
+	if (data)
+		NLA_PUT(msg, NL80211_ATTR_VENDOR_DATA, data_len, data);
+
+	ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: vendor command failed err=%d",
+			   ret);
+	return ret;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
 static int nl80211_set_qos_map(void *priv, const u8 *qos_map_set,
 			       u8 qos_map_set_len)
 {
@@ -11829,5 +11948,6 @@
 #ifdef ANDROID
 	.driver_cmd = wpa_driver_nl80211_driver_cmd,
 #endif /* ANDROID */
+	.vendor_cmd = nl80211_vendor_cmd,
 	.set_qos_map = nl80211_set_qos_map,
 };
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 4b90989..c5bf41f 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -1660,20 +1660,15 @@
 	case WLAN_PA_VENDOR_SPECIFIC:
 		data++;
 		len--;
-		if (len < 3)
+		if (len < 4)
 			return;
-		if (WPA_GET_BE24(data) != OUI_WFA)
+		if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
 			return;
 
-		data += 3;
-		len -= 3;
-		if (len < 1)
-			return;
+		data += 4;
+		len -= 4;
 
-		if (*data != P2P_OUI_TYPE)
-			return;
-
-		p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq);
+		p2p_rx_p2p_action(p2p, sa, data, len, freq);
 		break;
 	case WLAN_PA_GAS_INITIAL_REQ:
 		p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq);
@@ -1706,15 +1701,10 @@
 	if (len < 4)
 		return;
 
-	if (WPA_GET_BE24(data) != OUI_WFA)
+	if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
 		return;
-	data += 3;
-	len -= 3;
-
-	if (*data != P2P_OUI_TYPE)
-		return;
-	data++;
-	len--;
+	data += 4;
+	len -= 4;
 
 	/* P2P action frame */
 	p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa));
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 664fade..e9b683d 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -17,8 +17,7 @@
 void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token)
 {
 	wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC);
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, P2P_OUI_TYPE);
+	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 
 	wpabuf_put_u8(buf, subtype); /* OUI Subtype */
 	wpabuf_put_u8(buf, dialog_token);
@@ -31,8 +30,7 @@
 {
 	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
 	wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, P2P_OUI_TYPE);
+	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 
 	wpabuf_put_u8(buf, subtype); /* OUI Subtype */
 	wpabuf_put_u8(buf, dialog_token);
@@ -47,8 +45,7 @@
 	/* P2P IE header */
 	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
 	len = wpabuf_put(buf, 1); /* IE length to be filled */
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, P2P_OUI_TYPE);
+	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 	wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
 	return len;
 }
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index 26b9c2d..9df834c 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -171,8 +171,7 @@
 
 	/* ANQP Query Request Frame */
 	len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, P2P_OUI_TYPE);
+	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 	wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */
 	wpabuf_put_buf(buf, tlvs);
 	gas_anqp_set_element_len(buf, len_pos);
@@ -218,8 +217,7 @@
 	if (tlvs) {
 		/* ANQP Query Response Frame */
 		len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-		wpabuf_put_be24(buf, OUI_WFA);
-		wpabuf_put_u8(buf, P2P_OUI_TYPE);
+		wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 		 /* Service Update Indicator */
 		wpabuf_put_le16(buf, update_indic);
 		wpabuf_put_buf(buf, tlvs);
@@ -250,8 +248,7 @@
 		/* ANQP Query Response Frame */
 		wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */
 		wpabuf_put_le16(buf, 3 + 1 + 2 + total_len);
-		wpabuf_put_be24(buf, OUI_WFA);
-		wpabuf_put_u8(buf, P2P_OUI_TYPE);
+		wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 		/* Service Update Indicator */
 		wpabuf_put_le16(buf, update_indic);
 	}
@@ -393,17 +390,12 @@
 		return;
 	}
 
-	if (WPA_GET_BE24(pos) != OUI_WFA) {
-		p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+		p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+			WPA_GET_BE32(pos));
 		return;
 	}
-	pos += 3;
-
-	if (*pos != P2P_OUI_TYPE) {
-		p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos);
-		return;
-	}
-	pos++;
+	pos += 4;
 
 	if (pos + 2 > end)
 		return;
@@ -571,17 +563,12 @@
 		return;
 	}
 
-	if (WPA_GET_BE24(pos) != OUI_WFA) {
-		p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+		p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+			WPA_GET_BE32(pos));
 		return;
 	}
-	pos += 3;
-
-	if (*pos != P2P_OUI_TYPE) {
-		p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos);
-		return;
-	}
-	pos++;
+	pos += 4;
 
 	if (pos + 2 > end)
 		return;
@@ -795,17 +782,12 @@
 	if (pos + 4 > end)
 		return;
 
-	if (WPA_GET_BE24(pos) != OUI_WFA) {
-		p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+		p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+			WPA_GET_BE32(pos));
 		return;
 	}
-	pos += 3;
-
-	if (*pos != P2P_OUI_TYPE) {
-		p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos);
-		return;
-	}
-	pos++;
+	pos += 4;
 
 	if (pos + 2 > end)
 		return;
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 1ebcfe9..40bc1ad 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -59,6 +59,14 @@
 		}
 		attr->settings_delay_time = pos;
 		break;
+	case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u",
+				   len);
+			return -1;
+		}
+		attr->registrar_configuration_methods = pos;
+		break;
 	default:
 		wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
 			   "Extension subelement %u", id);
diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h
index eeb08d1..82c4739 100644
--- a/src/wps/wps_attr_parse.h
+++ b/src/wps/wps_attr_parse.h
@@ -55,6 +55,7 @@
 	const u8 *network_key_shareable; /* 1 octet (Bool) */
 	const u8 *request_to_enroll; /* 1 octet (Bool) */
 	const u8 *ap_channel; /* 2 octets */
+	const u8 *registrar_configuration_methods; /* 2 octets */
 
 	/* variable length fields */
 	const u8 *manufacturer;
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index 6f8a49f..e125048 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -146,7 +146,8 @@
 	WFA_ELEM_AUTHORIZEDMACS = 0x01,
 	WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
 	WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
-	WFA_ELEM_SETTINGS_DELAY_TIME = 0x04
+	WFA_ELEM_SETTINGS_DELAY_TIME = 0x04,
+	WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05
 };
 
 /* Device Password ID */