Merge tag 'LA.UM.9.12.r1-15200-SMxx50.QSSI13.0' into int/13/fp4

"LA.UM.9.12.r1-15200-SMxx50.QSSI13.0"

* tag 'LA.UM.9.12.r1-15200-SMxx50.QSSI13.0': (83 commits)
  DPP: Support SAE AKM in AP configuration
  Support HS2-2022 as a program name
  Use helper functions for checking Passpoint release
  ap_send_frame: Verify stationID parameter is present
  DPP: Check valid parameter combination for DPPReconfigure error case
  sta_set_security: Support imsiPrivacyCertID
  sta_set_security: Fix the error message for imsiPrivacyCert
  Do not add vht_capab for the 2.4 GHz interface in dual band AP
  traffic: Include -b argument for ping only for 255.255.255.255
  Use a new macro to include wifi-hal path for Android builds
  DPP: Start unconfigured AP on channel 6 if PB is used
  DPP: Support DPPBootstrapPOST,HTTP
  DPP: Report POSTResult for SetPeerBootstrap DPPNetRole,BSConfigurator
  DPP: Report ConfResult,FAILED on Configurator if rejecting the request
  DPP: Report push button failures immediately
  DPP: Fix MUDURL handling for hostapd-as-initiator
  DPP: More options for discovering the Controller for a Relay
  DPP: Fix PB determination of DPPProvisioningRole for STA Configurator
  DPP: Map DPPURICurves values as needed
  DPP: Fix REST API use
  ...

Change-Id: I810597906093b085a44957d178064082b0ff80d6
diff --git a/Android.mk b/Android.mk
index 3f30c1f..a2ec223 100644
--- a/Android.mk
+++ b/Android.mk
@@ -65,6 +65,7 @@
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := sigma_dut
+QCOM_WLAN_ROOT ?= hardware/qcom/wlan
 ifeq ($(PRODUCT_VENDOR_MOVE_ENABLED), true)
 LOCAL_VENDOR_MODULE := true
 endif
@@ -73,7 +74,7 @@
 LOCAL_C_INCLUDES += \
 	$(LOCAL_PATH) frameworks/base/cmds/keystore system/security/keystore \
 	$(LOCAL_PATH) frameworks/opt/net/wifi/libwifi_hal/include/ \
-	$(LOCAL_PATH) hardware/qcom/wlan/qcwcn/wifi_hal \
+	$(LOCAL_PATH) $(QCOM_WLAN_ROOT)/qcwcn/wifi_hal \
 	$(LOCAL_PATH) system/core/include/cutils \
 	$(LOCAL_PATH) hardware/libhardware_legacy/include/hardware_legacy \
 	$(LOCAL_PATH) external/libpcap \
diff --git a/DPP/README b/DPP/README
new file mode 100644
index 0000000..cbf7691
--- /dev/null
+++ b/DPP/README
@@ -0,0 +1,10 @@
+DPP bootstrapping services
+
+On the device that provides bootstrapping services, the following
+processes needs to be started:
+
+# stunnel to handle HTTPS(TLS-PSK)
+stunnel stunnel-server.conf
+
+# local-only HTTP server to handle the core DPP REST API
+./dpp-rest-server.py
diff --git a/DPP/dpp-rest-server.py b/DPP/dpp-rest-server.py
new file mode 100755
index 0000000..d4b630a
--- /dev/null
+++ b/DPP/dpp-rest-server.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+#
+# Sigma Control API DUT (DPP REST API server)
+# Copyright (c) 2022, Qualcomm Innovation Center, Inc.
+# All Rights Reserved.
+# Licensed under the Clear BSD license. See README for more details.
+
+from http.server import HTTPServer, BaseHTTPRequestHandler
+import json
+import os
+import wpaspy
+
+wpas_ctrl = '/var/run/wpa_supplicant'
+ifname = None
+
+def wpas_connect():
+    ifaces = []
+    if os.path.isdir(wpas_ctrl):
+        try:
+            ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+        except OSError as error:
+            print("Could not find wpa_supplicant: %s", str(error))
+            return None
+
+    if len(ifaces) < 1:
+        print("No wpa_supplicant control interface found")
+        return None
+
+    for ctrl in ifaces:
+        if ifname and ifname not in ctrl:
+            continue
+        if os.path.basename(ctrl).startswith("p2p-dev-"):
+            # skip P2P management interface
+            continue
+        try:
+            print("Trying to use control interface " + ctrl)
+            wpas = wpaspy.Ctrl(ctrl)
+            return wpas
+        except Exception as e:
+            pass
+    print("Could not connect to wpa_supplicant")
+    return None
+
+class DPP_REST_HTTPRequestHandler(BaseHTTPRequestHandler):
+    def do_POST(self):
+        if self.path != "/dpp/bskey":
+            self.send_response(404)
+            self.end_headers()
+            return
+
+        if 'Content-Length' not in self.headers:
+            self.send_response(411)
+            self.end_headers()
+            return
+
+        content_len = int(self.headers['Content-Length'])
+        body = self.rfile.read(content_len)
+
+        try:
+            data = json.loads(body)
+        except:
+            self.send_response(400)
+            self.end_headers()
+            self.wfile.write("Invalid JSON data".encode("ascii"))
+            return
+
+        if 'dppUri' not in data:
+            print("Missing dppUri")
+            self.send_response(400)
+            self.end_headers()
+            self.wfile.write("Missing dppUri".encode("ascii"))
+            return
+
+        wpas = wpas_connect()
+        if wpas is None:
+            self.send_response(503)
+            self.end_headers()
+            self.wfile.write("DPP Configurator not available".encode("ascii"))
+            return
+
+        res = wpas.request("DPP_QR_CODE " + data['dppUri'])
+
+        if "UNKNOWN COMMAND" in res:
+            self.send_response(503)
+            self.end_headers()
+            self.wfile.write("DPP Configurator not available".encode("ascii"))
+            return
+
+        if "FAIL" in res:
+            self.send_response(400)
+            self.end_headers()
+            self.wfile.write("Invalid DPP URI".encode("ascii"))
+            return
+
+        id = int(res)
+        with open("/tmp/dpp-rest-server.id", "w") as f:
+            f.write(str(id))
+        with open("/tmp/dpp-rest-server.uri", "w") as f:
+            f.write(data['dppUri'])
+
+        self.send_response(200)
+        self.end_headers()
+
+def main():
+    # Bind to localhost only and use an external stunnel proxy to handle
+    # TLS-PSK.
+    httpd = HTTPServer(('localhost', 44444), DPP_REST_HTTPRequestHandler)
+    httpd.serve_forever()
+
+if __name__ == "__main__":
+    main()
diff --git a/DPP/stunnel-server.conf b/DPP/stunnel-server.conf
new file mode 100644
index 0000000..ec6544c
--- /dev/null
+++ b/DPP/stunnel-server.conf
@@ -0,0 +1,9 @@
+debug=debug
+foreground=yes
+pid = /tmp/stunnel-dpp-rest-server.pid
+
+[PSK server]
+accept = 8908
+connect = 44444
+ciphers = PSK
+PSKsecrets = stunnel-server.psk
diff --git a/DPP/stunnel-server.psk b/DPP/stunnel-server.psk
new file mode 100644
index 0000000..af86525
--- /dev/null
+++ b/DPP/stunnel-server.psk
@@ -0,0 +1 @@
+dpp-rest:00112233445566778899aabbccddeeff
diff --git a/ap.c b/ap.c
index 0bfa56b..b2ffa66 100644
--- a/ap.c
+++ b/ap.c
@@ -26,6 +26,7 @@
 #include <net/if.h>
 #include "wpa_ctrl.h"
 #include "wpa_helpers.h"
+#include "nl80211_copy.h"
 #ifdef ANDROID
 #include <hardware_legacy/wifi.h>
 #include <grp.h>
@@ -5412,6 +5413,10 @@
 			"venue_url=10:https://bed-bath-and-beyond.r2m-testbed.wi-fi.org/floorplans/index.html\n"
 			);
 		break;
+	case 3:
+		fprintf(f,
+			"venue_url=1:http://the-great-mall.r2m-test bed.wi-fi.org/floorplans/index.html\n");
+		break;
 	}
 
 	switch (dut->ap_advice_of_charge) {
@@ -7999,6 +8004,7 @@
 	enum driver_type drv;
 	const char *key_mgmt;
 	int conf_counter = 0;
+	bool append_vht = false;
 #ifdef ANDROID
 	struct group *gr;
 #endif /* ANDROID */
@@ -8027,6 +8033,13 @@
 
 	dut->mode = SIGMA_MODE_AP;
 
+	if (dut->ap_dpp_conf_addr &&
+	    strcasecmp(dut->ap_dpp_conf_addr, "mDNS") == 0 &&
+	    dpp_mdns_discover_relay_params(dut) < 0) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Failed to discover DPP Controller for AP Relay using mDNS");
+	}
+
 	if (drv == DRIVER_ATHEROS)
 		return cmd_ath_ap_config_commit(dut, conn, cmd);
 	if (drv == DRIVER_WCN)
@@ -8445,7 +8458,8 @@
 				dut->ap_radius_port);
 		fprintf(f, "auth_server_shared_secret=%s\n",
 			dut->ap_radius_password);
-		if (dut->program == PROGRAM_HS2_R3) {
+		if (dut->program == PROGRAM_HS2_R3 ||
+		    dut->program == PROGRAM_HS2_R4) {
 			fprintf(f, "radius_das_port=3799\n");
 			fprintf(f, "radius_das_client=0.0.0.0 %s\n",
 				dut->ap_radius_password);
@@ -8809,9 +8823,14 @@
 			fprintf(f, "fragment_size=128\n");
 	}
 
-	if (dut->ap_dpp_conf_addr && dut->ap_dpp_conf_pkhash)
-		fprintf(f, "dpp_controller=ipaddr=%s pkhash=%s\n",
-			dut->ap_dpp_conf_addr, dut->ap_dpp_conf_pkhash);
+	if (dut->ap_dpp_conf_addr && dut->ap_dpp_conf_pkhash) {
+		if (strcasecmp(dut->ap_dpp_conf_addr, "mDNS") != 0) {
+			fprintf(f, "dpp_controller=ipaddr=%s pkhash=%s\n",
+				dut->ap_dpp_conf_addr, dut->ap_dpp_conf_pkhash);
+			fprintf(f, "dpp_configurator_connectivity=1\n");
+		}
+		fprintf(f, "dpp_relay_port=8908\n");
+	}
 
 	if (dut->ap_he_rtsthrshld == VALUE_ENABLED)
 		fprintf(f, "he_rts_threshold=512\n");
@@ -8873,13 +8892,32 @@
 					get_6g_ch_op_class(dut->ap_channel));
 		}
 
+		if (dut->use_5g) {
+			/* Do not try to enable VHT on the 2.4 GHz band when
+			 * configuring a dual band AP that does have VHT enabled
+			 * on the 5 GHz radio. */
+			if (dut->ap_is_dual) {
+				int chan;
+
+				if (conf_counter)
+					chan = dut->ap_tag_channel[0];
+				else
+					chan = dut->ap_channel;
+				append_vht = chan >= 36 && chan <= 171;
+			} else {
+				append_vht = true;
+			}
+		}
+
 		find_ap_ampdu_exp_and_max_mpdu_len(dut);
 
-		if (dut->ap_sgi80 || dut->ap_txBF ||
-		    dut->ap_ldpc != VALUE_NOT_SET ||
-		    dut->ap_tx_stbc == VALUE_ENABLED || dut->ap_mu_txBF ||
-		    dut->ap_ampdu_exp || dut->ap_max_mpdu_len ||
-		    dut->ap_chwidth == AP_160 || dut->ap_chwidth == AP_80_80) {
+		if (append_vht &&
+		    (dut->ap_sgi80 || dut->ap_txBF ||
+		     dut->ap_ldpc != VALUE_NOT_SET ||
+		     dut->ap_tx_stbc == VALUE_ENABLED || dut->ap_mu_txBF ||
+		     dut->ap_ampdu_exp || dut->ap_max_mpdu_len ||
+		     dut->ap_chwidth == AP_160 ||
+		     dut->ap_chwidth == AP_80_80)) {
 			fprintf(f, "vht_capab=%s%s%s%s%s%s",
 				dut->ap_sgi80 ? "[SHORT-GI-80]" : "",
 				dut->ap_txBF ?
@@ -9885,8 +9923,7 @@
 
 	dut->ap_ocvc = dut->user_config_ap_ocvc;
 
-	if (dut->program == PROGRAM_HS2 || dut->program == PROGRAM_HS2_R2 ||
-	    dut->program == PROGRAM_HS2_R3 ||
+	if (is_passpoint(dut->program) ||
 	    dut->program == PROGRAM_IOTLP) {
 		int i;
 
@@ -9943,7 +9980,7 @@
 		dut->ap_add_sha256 = 0;
 	}
 
-	if (dut->program == PROGRAM_HS2_R2 || dut->program == PROGRAM_HS2_R3 ||
+	if (is_passpoint_r2_or_newer(dut->program) ||
 	    dut->program == PROGRAM_IOTLP) {
 		int i;
 		const char hessid[] = "50:6f:9a:00:11:22";
@@ -10193,7 +10230,11 @@
 	dut->ap_dpp_conf_addr = NULL;
 	free(dut->ap_dpp_conf_pkhash);
 	dut->ap_dpp_conf_pkhash = NULL;
+	dut->dpp_local_bootstrap = -1;
 	dut->ap_start_disabled = 0;
+	dpp_mdns_stop(dut);
+	unlink("/tmp/dpp-rest-server.uri");
+	unlink("/tmp/dpp-rest-server.id");
 
 	if (is_60g_sigma_dut(dut)) {
 		dut->ap_mode = AP_11ad;
@@ -10459,10 +10500,10 @@
 #endif /* __linux__ */
 
 enum send_frame_type {
-		DISASSOC, DEAUTH, SAQUERY
+		DISASSOC, DEAUTH, SAQUERY, CHANNEL_SWITCH
 };
 enum send_frame_protection {
-	CORRECT_KEY, INCORRECT_KEY, UNPROTECTED
+	PROTECTION_NOT_SET, CORRECT_KEY, INCORRECT_KEY, UNPROTECTED
 };
 
 
@@ -10533,6 +10574,8 @@
 	case SAQUERY:
 		*pos++ = 0xd0;
 		break;
+	default:
+		return -1;
 	}
 
 	if (protected == INCORRECT_KEY)
@@ -10609,6 +10652,8 @@
 			*pos++ = 0x12;
 			*pos++ = 0x34;
 			break;
+		default:
+			return -1;
 		}
 	}
 
@@ -11222,8 +11267,10 @@
 	/* const char *ifname = get_param(cmd, "INTERFACE"); */
 	const char *val;
 	enum send_frame_type frame;
-	enum send_frame_protection protected;
+	enum send_frame_protection protected = PROTECTION_NOT_SET;
 	char buf[100];
+	unsigned int freq;
+	unsigned int chan;
 
 	val = get_param(cmd, "Program");
 	if (val) {
@@ -11254,6 +11301,8 @@
 		frame = DEAUTH;
 	else if (strcasecmp(val, "saquery") == 0)
 		frame = SAQUERY;
+	else if (strcasecmp(val, "ChannelSwitchAnncment") == 0)
+		frame = CHANNEL_SWITCH;
 	else {
 		send_resp(dut, conn, SIGMA_ERROR, "errorCode,Unsupported "
 			  "PMFFrameType");
@@ -11263,29 +11312,32 @@
 	val = get_param(cmd, "PMFProtected");
 	if (val == NULL)
 		val = get_param(cmd, "Protected");
-	if (val == NULL)
-		return -1;
-	if (strcasecmp(val, "Correct-key") == 0 ||
-	    strcasecmp(val, "CorrectKey") == 0)
-		protected = CORRECT_KEY;
-	else if (strcasecmp(val, "IncorrectKey") == 0)
-		protected = INCORRECT_KEY;
-	else if (strcasecmp(val, "Unprotected") == 0)
-		protected = UNPROTECTED;
-	else {
-		send_resp(dut, conn, SIGMA_ERROR, "errorCode,Unsupported "
-			  "PMFProtected");
-		return 0;
+	if (val) {
+		if (strcasecmp(val, "Correct-key") == 0 ||
+		    strcasecmp(val, "CorrectKey") == 0)
+			protected = CORRECT_KEY;
+		else if (strcasecmp(val, "IncorrectKey") == 0)
+			protected = INCORRECT_KEY;
+		else if (strcasecmp(val, "Unprotected") == 0)
+			protected = UNPROTECTED;
+		else {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Unsupported PMFProtected");
+			return STATUS_SENT_ERROR;
+		}
 	}
 
 	val = get_param(cmd, "stationID");
-	if (val == NULL)
-		return -1;
 
-	if (protected == INCORRECT_KEY ||
-	    (protected == UNPROTECTED && frame == SAQUERY))
+	if (val && (protected == INCORRECT_KEY ||
+		    (protected == UNPROTECTED && frame == SAQUERY)))
 		return ap_inject_frame(dut, conn, frame, protected, val);
 
+	if (!val && frame != CHANNEL_SWITCH) {
+		sigma_dut_print(dut, DUT_MSG_ERROR, "stationID not specified");
+		return INVALID_SEND_STATUS;
+	}
+
 	switch (frame) {
 	case DISASSOC:
 		snprintf(buf, sizeof(buf), "disassoc %s test=%d",
@@ -11298,6 +11350,19 @@
 	case SAQUERY:
 		snprintf(buf, sizeof(buf), "sa_query %s", val);
 		break;
+	case CHANNEL_SWITCH:
+		val = get_param(cmd, "channel");
+		if (!val)
+			return -1;
+		chan = atoi(val);
+		freq = channel_to_freq(dut, chan);
+		if (!freq) {
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"Invalid channel: %d", chan);
+			return INVALID_SEND_STATUS;
+		}
+		snprintf(buf, sizeof(buf), "chan_switch 5 %d", freq);
+		break;
 	}
 
 	if (run_hostapd_cli(dut, buf) != 0)
@@ -13586,22 +13651,34 @@
 	val = get_param(cmd, "GI");
 	if (val) {
 		int fix_rate_sgi;
+		u8 he_gi_val;
+		u8 auto_rate_gi;
 
 		if (strcmp(val, "0.8") == 0) {
-			run_iwpriv(dut, ifname, "enable_short_gi 9");
 			fix_rate_sgi = 1;
+			auto_rate_gi = 9;
+			he_gi_val = NL80211_RATE_INFO_HE_GI_0_8;
 		} else if (strcmp(val, "1.6") == 0) {
-			run_iwpriv(dut, ifname, "enable_short_gi 10");
 			fix_rate_sgi = 2;
+			auto_rate_gi = 10;
+			he_gi_val = NL80211_RATE_INFO_HE_GI_1_6;
 		} else if (strcmp(val, "3.2") == 0) {
-			run_iwpriv(dut, ifname, "enable_short_gi 11");
 			fix_rate_sgi = 3;
+			auto_rate_gi = 11;
+			he_gi_val = NL80211_RATE_INFO_HE_GI_3_2;
 		} else {
 			send_resp(dut, conn, SIGMA_ERROR,
 				  "errorCode,GI value not supported");
 			return STATUS_SENT_ERROR;
 		}
-		run_iwpriv(dut, ifname, "enable_short_gi %d", fix_rate_sgi);
+		if (wcn_set_he_gi(dut, ifname, he_gi_val)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"wcn_set_he_gi failed, using iwpriv");
+			run_iwpriv(dut, ifname, "enable_short_gi %d",
+				   auto_rate_gi);
+			run_iwpriv(dut, ifname, "enable_short_gi %d",
+				   fix_rate_sgi);
+		}
 	}
 
 	val = get_param(cmd, "LTF");
@@ -14085,6 +14162,10 @@
 		dut->ap_dpp_conf_pkhash = strdup(val);
 	}
 
+	if (dut->ap_dpp_conf_addr &&
+	    strcasecmp(dut->ap_dpp_conf_addr, "mDNS") == 0)
+		dpp_mdns_discover_relay_params(dut);
+
 	return 1;
 }
 
diff --git a/dpp.c b/dpp.c
index e4bf459..3ae2bbf 100644
--- a/dpp.c
+++ b/dpp.c
@@ -19,6 +19,218 @@
 #endif /* ANDROID */
 
 
+static const char * dpp_mdns_role_txt(enum dpp_mdns_role role)
+{
+	switch (role) {
+	case DPP_MDNS_NOT_RUNNING:
+		break;
+	case DPP_MDNS_RELAY:
+		return "relay";
+	case DPP_MDNS_CONTROLLER:
+		return "controller";
+	case DPP_MDNS_BOOTSTRAPPING:
+		return "bootstrapping";
+	}
+	return "unknown";
+}
+
+
+static int dpp_mdns_discover(struct sigma_dut *dut, enum dpp_mdns_role role,
+			     char *addr, size_t addr_size, unsigned int *port,
+			     unsigned char *hash)
+{
+	char cmd[200], buf[10000], *pos, *pos2, *pos3;
+	char *ifname = NULL, *ipaddr = NULL, *bskeyhash = NULL;
+	size_t len;
+	FILE *f;
+
+	if (port)
+		*port = 0;
+
+	snprintf(cmd, sizeof(cmd), "avahi-browse _%s._sub._dpp._tcp -r -t -p",
+		 dpp_mdns_role_txt(role));
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "Run: %s", cmd);
+	f = popen(cmd, "r");
+	if (!f) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"Could not run avahi-browse: %s",
+				strerror(errno));
+		return -1;
+	}
+	len = fread(buf, 1, sizeof(buf) - 1, f);
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "avahi-browse returned %zu octets",
+			len);
+	pclose(f);
+	if (!len)
+		return -1;
+	buf[len] = '\0';
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "mDNS results:\n%s", buf);
+
+	pos = buf;
+	while (pos) {
+		pos2 = strchr(pos, '\n');
+		if (pos2)
+			*pos2 = '\0';
+		if (pos[0] != '=')
+			goto next;
+
+		pos = strchr(pos, ';');
+		if (!pos)
+			goto next;
+		pos++;
+		/* ifname */
+		pos3 = strchr(pos, ';');
+		if (!pos3)
+			goto next;
+		*pos3 = '\0';
+		ifname = pos;
+
+		pos = pos3 + 1;
+		/* IP version */
+		if (strncmp(pos, "IPv4;", 5) != 0)
+			goto next;
+
+		pos = strchr(pos, ';');
+		if (!pos)
+			goto next;
+		pos++;
+		/* name */
+
+		pos = strchr(pos, ';');
+		if (!pos)
+			goto next;
+		pos++;
+		/* service */
+
+		pos = strchr(pos, ';');
+		if (!pos)
+			goto next;
+		pos++;
+		/* local? */
+
+		pos = strchr(pos, ';');
+		if (!pos)
+			goto next;
+		pos++;
+		/* fqdn */
+
+		pos = strchr(pos, ';');
+		if (!pos)
+			goto next;
+		pos++;
+		/* IP address */
+
+		pos3 = strchr(pos, ';');
+		if (!pos3)
+			goto next;
+		*pos3 = '\0';
+		ipaddr = pos;
+		pos = pos3 + 1;
+
+		if (port)
+			*port = atoi(pos);
+
+		pos = strstr(pos, "\"bskeyhash=");
+		if (pos) {
+			pos += 11;
+			pos3 = strchr(pos, '"');
+			if (pos3)
+				*pos3 = '\0';
+			bskeyhash = pos;
+		}
+
+		/* Could try to pick the most appropriate candidate if multiple
+		 * entries are discovered */
+		break;
+
+	next:
+		if (!pos2)
+			break;
+		pos = pos2 + 1;
+	}
+
+	if (!ipaddr)
+		return -1;
+	sigma_dut_print(dut, DUT_MSG_INFO, "Discovered (mDNS) service at %s@%s",
+			ipaddr, ifname);
+	if (bskeyhash && hash) {
+		unsigned char *bin;
+		size_t bin_len;
+
+		sigma_dut_print(dut, DUT_MSG_DEBUG, "bskeyhash: %s", bskeyhash);
+
+		bin = base64_decode(bskeyhash, strlen(bskeyhash), &bin_len);
+		if (!bin || bin_len != 32) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Invalid bskeyhash value in mDNS records");
+			free(bin);
+			return -1;
+		}
+
+		memcpy(hash, bin, bin_len);
+		free(bin);
+	}
+	strlcpy(addr, ipaddr, addr_size);
+
+	return 0;
+}
+
+
+int dpp_mdns_discover_relay_params(struct sigma_dut *dut)
+{
+	char tcp_addr[30];
+	unsigned char hash[32];
+
+	if (dpp_mdns_discover(dut, DPP_MDNS_CONTROLLER,
+			      tcp_addr, sizeof(tcp_addr), NULL, hash) < 0) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Could not discover Controller IP address using mDNS");
+		return -1;
+	}
+
+	free(dut->ap_dpp_conf_addr);
+	dut->ap_dpp_conf_addr = strdup(tcp_addr);
+
+	free(dut->ap_dpp_conf_pkhash);
+	dut->ap_dpp_conf_pkhash = malloc(2 * 32 + 1);
+	if (dut->ap_dpp_conf_pkhash) {
+		int i;
+		char *pos = dut->ap_dpp_conf_pkhash;
+		char *end = pos + 2 * 32 + 1;
+
+		for (i = 0; i < 32; i++)
+			pos += snprintf(pos, end - pos, "%02x", hash[i]);
+		*pos = '\0';
+	}
+
+	if (dut->ap_dpp_conf_addr && dut->ap_dpp_conf_pkhash) {
+		const char *ifname = dut->hostapd_ifname;
+
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Controller discovered using mDNS: %s (pkhash %s)",
+				dut->ap_dpp_conf_addr,
+				dut->ap_dpp_conf_pkhash);
+		if (dut->hostapd_running && ifname) {
+			char cmd[500];
+
+			snprintf(cmd, sizeof(cmd),
+				 "DPP_RELAY_ADD_CONTROLLER %s %s",
+				 dut->ap_dpp_conf_addr,
+				 dut->ap_dpp_conf_pkhash);
+			if (wpa_command(ifname, cmd) < 0)
+				sigma_dut_print(dut, DUT_MSG_INFO,
+						"Failed to add Controller connection into hostapd");
+			else
+				sigma_dut_print(dut, DUT_MSG_INFO,
+						"Added Controller connection into hostapd");
+		}
+		return 0;
+	}
+
+	return -1;
+}
+
+
 static int sigma_dut_is_ap(struct sigma_dut *dut)
 {
 	return dut->device_type == AP_unknown ||
@@ -27,16 +239,24 @@
 }
 
 
-static int dpp_hostapd_run(struct sigma_dut *dut)
+static int dpp_hostapd_run(struct sigma_dut *dut, bool chirp_chan)
 {
 	if (dut->hostapd_running)
 		return 0;
 
+	if (dut->ap_dpp_conf_addr &&
+	    strcasecmp(dut->ap_dpp_conf_addr, "mDNS") == 0 &&
+	    dpp_mdns_discover_relay_params(dut) < 0) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"Failed to discover Controller for AP Relay using mDNS - cannot start hostapd");
+		return -1;
+	}
+
 	sigma_dut_print(dut, DUT_MSG_INFO,
 			"Starting hostapd in unconfigured state for DPP");
 	snprintf(dut->ap_ssid, sizeof(dut->ap_ssid), "unconfigured");
 	if (!dut->ap_oper_chn)
-		dut->ap_channel = 11;
+		dut->ap_channel = chirp_chan ? 6 : 11;
 	dut->ap_is_dual = 0;
 	dut->ap_mode = dut->ap_channel <= 14 ? AP_11ng : AP_11na;
 	dut->ap_key_mgmt = AP_OPEN;
@@ -66,23 +286,27 @@
 }
 
 
-static const char * dpp_get_curve(struct sigma_cmd *cmd, const char *arg)
+static const char * dpp_map_curve(const char *val)
 {
-	const char *val = get_param(cmd, arg);
-
 	if (!val)
-		val = "P-256";
-	else if (strcasecmp(val, "BP-256R1") == 0)
-		val = "BP-256";
-	else if (strcasecmp(val, "BP-384R1") == 0)
-		val = "BP-384";
-	else if (strcasecmp(val, "BP-512R1") == 0)
-		val = "BP-512";
+		return "P-256";
+	if (strcasecmp(val, "BP-256R1") == 0)
+		return "BP-256";
+	if (strcasecmp(val, "BP-384R1") == 0)
+		return "BP-384";
+	if (strcasecmp(val, "BP-512R1") == 0)
+		return "BP-512";
 
 	return val;
 }
 
 
+static const char * dpp_get_curve(struct sigma_cmd *cmd, const char *arg)
+{
+	return dpp_map_curve(get_param(cmd, arg));
+}
+
+
 static enum sigma_cmd_result
 dpp_get_local_bootstrap(struct sigma_dut *dut, struct sigma_conn *conn,
 			struct sigma_cmd *cmd, int send_result, int *success)
@@ -91,12 +315,22 @@
 	const char *bs = get_param(cmd, "DPPBS");
 	const char *chan_list = get_param(cmd, "DPPChannelList");
 	const char *tcp = get_param(cmd, "DPPOverTCP");
+	const char *val;
 	char *pos, mac[50], buf[200], resp[1000], hex[2000];
 	const char *ifname = get_station_ifname(dut);
 	int res;
 	const char *type;
 	int include_mac;
+	char host[100];
+	bool uri_host;
+	char ip[30];
+	char uri_curves_buf[200];
+	const char *uri_curves = NULL;
 
+	host[0] = '\0';
+	ip[0] = '\0';
+	val = get_param(cmd, "DPPURIHost");
+	uri_host = val && strcasecmp(val, "Yes") == 0;
 	include_mac = !tcp || strcasecmp(tcp, "yes") != 0;
 
 	if (success)
@@ -111,6 +345,32 @@
 		return STATUS_SENT_ERROR;
 	}
 
+	val = get_param(cmd, "DPPURICurves");
+	if (val) {
+		char *saveptr, *r, *tmp, *end;
+
+		pos = uri_curves_buf;
+		end = pos + sizeof(uri_curves_buf);
+		*pos = '\0';
+		tmp = strdup(val);
+		if (!tmp)
+			return ERROR_SEND_STATUS;
+		r = strtok_r(tmp, ":", &saveptr);
+		while (r) {
+			int len;
+
+			len = snprintf(pos, end - pos, "%s%s",
+				       pos > uri_curves_buf ? ":" : "",
+				       dpp_map_curve(r));
+			if (len < 0)
+				break;
+			pos += len;
+			r = strtok_r(NULL, ":", &saveptr);
+		}
+		free(tmp);
+		uri_curves = uri_curves_buf;
+	}
+
 	if (sigma_dut_is_ap(dut)) {
 		u8 bssid[ETH_ALEN];
 
@@ -129,14 +389,44 @@
 		snprintf(mac, sizeof(mac), "%02x%02x%02x%02x%02x%02x",
 			 bssid[0], bssid[1], bssid[2],
 			 bssid[3], bssid[4], bssid[5]);
+
+		if (uri_host) {
+			const char *ifname;
+
+			ifname = dut->bridge ? dut->bridge :
+				dut->hostapd_ifname;
+			if (get_ip_addr(ifname, 0, ip, sizeof(ip)) < 0) {
+				sigma_dut_print(dut, DUT_MSG_INFO,
+						"Could not get IP address for AP mode: bridge=%s hostapd_ifname=%s",
+						dut->bridge ? dut->bridge :
+						"N/A",
+						dut->hostapd_ifname);
+				ip[0] = '\0';
+			}
+		}
 	} else {
 		if (get_wpa_status(ifname, "address", mac, sizeof(mac)) < 0) {
 			send_resp(dut, conn, SIGMA_ERROR,
 				  "errorCode,Failed to get own MAC address from wpa_supplicant");
 			return STATUS_SENT_ERROR;
 		}
+
+		if (uri_host &&
+		    get_wpa_status(ifname, "ip_address", ip, sizeof(ip)) < 0) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Could not get IP address for station mode");
+			ip[0] = '\0';
+		}
 	}
 
+	if (uri_host && ip[0] == '\0') {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,IP address not available on wireless interface");
+		return STATUS_SENT_ERROR;
+	}
+	if (uri_host)
+		snprintf(host, sizeof(host), " host=%s", ip);
+
 	pos = mac;
 	while (*pos) {
 		if (*pos == ':')
@@ -145,7 +435,7 @@
 			pos++;
 	}
 
-	if (sigma_dut_is_ap(dut) && dpp_hostapd_run(dut) < 0) {
+	if (sigma_dut_is_ap(dut) && dpp_hostapd_run(dut, false) < 0) {
 		send_resp(dut, conn, SIGMA_ERROR,
 			  "errorCode,Failed to start hostapd");
 		return STATUS_SENT_ERROR;
@@ -155,10 +445,13 @@
 	    (strcmp(chan_list, "0/0") == 0 || chan_list[0] == '\0')) {
 		/* No channel list */
 		res = snprintf(buf, sizeof(buf),
-			       "DPP_BOOTSTRAP_GEN type=%s curve=%s%s%s",
+			       "DPP_BOOTSTRAP_GEN type=%s curve=%s%s%s%s%s%s",
 			       type, curve,
 			       include_mac ? " mac=" : "",
-			       include_mac ? mac : "");
+			       include_mac ? mac : "",
+			       uri_curves ? " supported_curves=" : "",
+			       uri_curves ? uri_curves : "",
+			       host);
 	} else if (chan_list) {
 		/* Channel list override (CTT case) - space separated tuple(s)
 		 * of OperatingClass/Channel; convert to wpa_supplicant/hostapd
@@ -169,9 +462,12 @@
 				*pos = ',';
 		}
 		res = snprintf(buf, sizeof(buf),
-			       "DPP_BOOTSTRAP_GEN type=%s curve=%s chan=%s%s%s",
+			       "DPP_BOOTSTRAP_GEN type=%s curve=%s chan=%s%s%s%s%s%s",
 			       type, curve, resp, include_mac ? " mac=" : "",
-			       include_mac ? mac : "");
+			       include_mac ? mac : "",
+			       uri_curves ? " supported_curves=" : "",
+			       uri_curves ? uri_curves : "",
+			       host);
 	} else {
 		int channel = 11;
 
@@ -181,9 +477,12 @@
 		    dut->ap_channel > 0 && dut->ap_channel <= 13)
 			channel = dut->ap_channel;
 		res = snprintf(buf, sizeof(buf),
-			       "DPP_BOOTSTRAP_GEN type=%s curve=%s chan=81/%d%s%s",
+			       "DPP_BOOTSTRAP_GEN type=%s curve=%s chan=81/%d%s%s%s%s%s",
 			       type, curve, channel, include_mac ? " mac=" : "",
-			       include_mac ? mac : "");
+			       include_mac ? mac : "",
+			       uri_curves ? " supported_curves=" : "",
+			       uri_curves ? uri_curves : "",
+			       host);
 	}
 
 	if (res < 0 || res >= sizeof(buf) ||
@@ -199,6 +498,12 @@
 
 	sigma_dut_print(dut, DUT_MSG_DEBUG, "URI: %s", resp);
 
+	if (dut->dpp_mdns == DPP_MDNS_CONTROLLER) {
+		/* Update mDNS advertisement since the local boostrapping key
+		 * has changed. */
+		dpp_mdns_start(dut, DPP_MDNS_CONTROLLER);
+	}
+
 	if (send_result) {
 		ascii2hexstr(resp, hex);
 		res = snprintf(resp, sizeof(resp), "BootstrappingData,%s", hex);
@@ -212,6 +517,141 @@
 }
 
 
+static void stop_stunnel(struct sigma_dut *dut)
+{
+	FILE *f;
+	int pid;
+
+	f = fopen("/tmp/stunnel-dpp-rest-client.pid", "r");
+	if (!f)
+		return;
+
+	if (fscanf(f, "%d", &pid) == 1 && pid > 1) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Terminate stunnel process %d",
+				pid);
+		kill(pid, SIGTERM);
+	}
+	fclose(f);
+}
+
+
+static enum sigma_cmd_result dpp_post_uri(struct sigma_dut *dut,
+					  struct sigma_conn *conn,
+					  struct sigma_cmd *cmd,
+					  const char *uri)
+{
+	FILE *f;
+	char buf[1000], *pos;
+	size_t len;
+	int status;
+	char tcp_addr[30];
+	unsigned int port;
+	const char *val;
+	bool http;
+
+	f = fopen("/tmp/dppuri.json", "w");
+	if (!f) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Could not write dppuri.json");
+		return STATUS_SENT_ERROR;
+	}
+	fprintf(f, "{\"dppUri\":\"%s\"}", uri);
+	fclose(f);
+
+	if (dpp_mdns_discover(dut, DPP_MDNS_BOOTSTRAPPING,
+			      tcp_addr, sizeof(tcp_addr), &port, NULL) < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Could not discover (mDNS) bootstrapping service");
+		return STATUS_SENT_ERROR;
+	}
+
+	sigma_dut_print(dut, DUT_MSG_INFO, "Bootstrapping service at %s:%u",
+			tcp_addr, port);
+
+	val = get_param(cmd, "DPPBootstrapPOST");
+	http = val && strcmp(val, "HTTP") == 0;
+	if (http)
+		goto skip_stunnel;
+
+	f = fopen("/tmp/stunnel-dpp-rest-client.conf", "w");
+	if (!f) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Could not write stunnel-dpp-rest-client.conf");
+		return STATUS_SENT_ERROR;
+	}
+	fprintf(f, "pid = /tmp/stunnel-dpp-rest-client.pid\n");
+	fprintf(f, "[PSK client]\n");
+	fprintf(f, "client = yes\n");
+	fprintf(f, "accept = 127.0.0.1:33333\n");
+	fprintf(f, "connect = %s:%u\n", tcp_addr, port);
+	fprintf(f, "PSKsecrets = /tmp/stunnel-dpp-rest-client.psk\n");
+	fclose(f);
+
+
+	f = fopen("/tmp/stunnel-dpp-rest-client.psk", "w");
+	if (!f) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Could not write stunnel-dpp-rest-client.psk");
+		return STATUS_SENT_ERROR;
+	}
+	fprintf(f, "dpp-rest:00112233445566778899aabbccddeeff\n");
+	fclose(f);
+
+	stop_stunnel(dut);
+
+	if (system("stunnel /tmp/stunnel-dpp-rest-client.conf") != 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Could not start stunnel");
+		return STATUS_SENT_ERROR;
+	}
+
+skip_stunnel:
+	if (http)
+		snprintf(buf, sizeof(buf),
+			 "curl -i --request POST --header \"Content-Type: application/json\" --data @/tmp/dppuri.json http://%s:%d/dpp/bskey",
+			 tcp_addr, port);
+	else
+		snprintf(buf, sizeof(buf),
+			 "curl -i --request POST --header \"Content-Type: application/json\" --data @/tmp/dppuri.json http://localhost:33333/dpp/bskey");
+	sigma_dut_print(dut, DUT_MSG_INFO, "Run: %s", buf);
+	f = popen(buf, "r");
+	if (!f) {
+		if (!http)
+			stop_stunnel(dut);
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Could not run curl");
+		return STATUS_SENT_ERROR;
+	}
+	len = fread(buf, 1, sizeof(buf) - 1, f);
+	pclose(f);
+	if (!http)
+		stop_stunnel(dut);
+	if (!len) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"curl failed to fetch response");
+		send_resp(dut, conn, SIGMA_COMPLETE, "POSTResult,Failed");
+		return STATUS_SENT_ERROR;
+	}
+	buf[len] = '\0';
+	pos = strchr(buf, ' ');
+	if (!pos || strncmp(buf, "HTTP/", 5) != 0) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Invalid HTTP responder header received");
+		send_resp(dut, conn, SIGMA_COMPLETE, "POSTResult,Failed");
+		return STATUS_SENT;
+	}
+	pos++;
+	status = atoi(pos);
+
+	sigma_dut_print(dut, DUT_MSG_INFO, "curl reported HTTP status code %d",
+			status);
+	snprintf(buf, sizeof(buf), "POSTResult,%d", status);
+	send_resp(dut, conn, SIGMA_COMPLETE, buf);
+	return STATUS_SENT;
+}
+
+
 static enum sigma_cmd_result dpp_set_peer_bootstrap(struct sigma_dut *dut,
 						    struct sigma_conn *conn,
 						    struct sigma_cmd *cmd)
@@ -231,6 +671,11 @@
 		return ERROR_SEND_STATUS;
 	uri[res] = '\0';
 	sigma_dut_print(dut, DUT_MSG_DEBUG, "URI: %s", uri);
+
+	val = get_param(cmd, "DPPNetRole");
+	if (val && strcasecmp(val, "BSConfigurator") == 0)
+		return dpp_post_uri(dut, conn, cmd, uri);
+
 	free(dut->dpp_peer_uri);
 	dut->dpp_peer_uri = strdup(uri);
 
@@ -254,11 +699,13 @@
 	};
 	unsigned int old_timeout;
 	int legacy_akm, dpp_akm;
+	bool sae_akm, psk_akm;
 	char *connector = NULL, *psk = NULL, *csign = NULL,
 		*net_access_key = NULL;
 	char pass[64];
 	int pass_len = 0;
 	int ret = 0;
+	const char *cmd;
 
 	sigma_dut_print(dut, DUT_MSG_INFO,
 			"Update hostapd configuration based on DPP Config Object");
@@ -286,6 +733,8 @@
 			"DPP: Config Object AKM: %s", pos);
 	legacy_akm = strstr(pos, "psk") != NULL || strstr(pos, "sae") != NULL;
 	dpp_akm = strstr(pos, "dpp") != NULL;
+	psk_akm = strstr(pos, "psk") != NULL;
+	sae_akm = strstr(pos, "sae") != NULL;
 
 	res = get_wpa_cli_event(dut, ctrl, "DPP-CONFOBJ-SSID",
 				buf, sizeof(buf));
@@ -382,15 +831,22 @@
 		}
 	}
 
-	if ((!connector || !dpp_akm) &&
-	    wpa_command(ifname, "SET wpa_key_mgmt WPA-PSK") < 0) {
-		send_resp(dut, conn, SIGMA_ERROR,
-			  "errorCode,Failed to update AP security parameters");
-		goto out;
-	}
+	if ((!connector || !dpp_akm) && psk_akm && sae_akm)
+		cmd = "SET wpa_key_mgmt SAE WPA-PSK";
+	else if ((!connector || !dpp_akm) && sae_akm)
+		cmd = "SET wpa_key_mgmt SAE";
+	else if ((!connector || !dpp_akm) && psk_akm)
+		cmd = "SET wpa_key_mgmt WPA-PSK";
+	else if (connector && dpp_akm && legacy_akm && psk_akm && sae_akm)
+		cmd = "SET wpa_key_mgmt DPP SAE WPA-PSK";
+	else if (connector && dpp_akm && legacy_akm && sae_akm)
+		cmd = "SET wpa_key_mgmt DPP SAE";
+	else if (connector && dpp_akm && legacy_akm && psk_akm)
+		cmd = "SET wpa_key_mgmt DPP WPA-PSK";
+	else
+		cmd = "UNKNOWN";
 
-	if (connector && dpp_akm && legacy_akm &&
-	    wpa_command(ifname, "SET wpa_key_mgmt DPP WPA-PSK") < 0) {
+	if (wpa_command(ifname, cmd) < 0) {
 		send_resp(dut, conn, SIGMA_ERROR,
 			  "errorCode,Failed to update AP security parameters");
 		goto out;
@@ -603,6 +1059,15 @@
 	{ "Timeout", "AuthenticationResponse", NULL, 88 },
 	{ "Timeout", "AuthenticationConfirm", NULL, 89 },
 	{ "Timeout", "ConfigurationRequest", NULL, 90 },
+	{ "MissingAttribute", "PeerDiscoveryRequest", "ProtocolVersion", 92 },
+	{ "MissingAttribute", "PeerDiscoveryResponse", "ProtocolVersion", 93 },
+	{ "InvalidValue", "PeerDiscoveryRequest", "ProtocolVersion", 94 },
+	{ "InvalidValue", "PeerDiscoveryResponse", "ProtocolVersion", 95 },
+	{ "InvalidValue", "ReconfigAuthRequest", "ProtocolVersion", 96 },
+	{ "MissingAttribute", "ReconfigAuthRequest", "ProtocolVersion", 97 },
+	{ "InvalidValue", "PBPresAnnc", "RespBSKeyHash", 98 },
+	{ "InvalidValue", "PBPAResponse", "InitBSKeyHash", 99 },
+	{ "InvalidValue", "PBPAResponse", "RespBSKeyHash", 100 },
 	{ NULL, NULL, NULL, 0 }
 };
 
@@ -628,12 +1093,19 @@
 {
 	char buf[200], tmp[20];
 	int res;
+	const char *events[] = { "DPP-TX", "DPP-FAIL", NULL };
 
 	snprintf(tmp, sizeof(tmp), "type=%d", frame_type);
 	for (;;) {
-		res = get_wpa_cli_event(dut, ctrl, "DPP-TX", buf, sizeof(buf));
+		res = get_wpa_cli_events(dut, ctrl, events, buf, sizeof(buf));
 		if (res < 0)
 			return -1;
+		if (strstr(buf, "DPP-FAIL")) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"DPP-FAIL reported while waiting for DPP-TX: %s",
+					buf);
+			return -1;
+		}
 		if (strstr(buf, tmp) != NULL)
 			break;
 	}
@@ -655,6 +1127,9 @@
 			return -1;
 		if (strstr(buf, tmp) != NULL)
 			break;
+		/* Alias for PKEXv2 Exchange Request */
+		if (frame_type == 7 && strstr(buf, "type=18") != NULL)
+			break;
 	}
 
 	res = get_wpa_cli_event(dut, ctrl, "DPP-TX-STATUS",
@@ -933,6 +1408,13 @@
 	}
 	sigma_dut_print(dut, DUT_MSG_DEBUG, "DPP auth result: %s", buf);
 
+	if (strstr(buf, "DPP-PB-RESULT") &&
+	    strstr(buf, "DPP-PB-RESULT success") == NULL) {
+		sigma_dut_print(dut, DUT_MSG_INFO, "DPP PB failed: %s", buf);
+		send_resp(dut, conn, SIGMA_COMPLETE, "BootstrapResult,Failed");
+		return -1;
+	}
+
 	if (strstr(buf, "DPP-RESPONSE-PENDING")) {
 		/* Display own QR code in manual mode */
 		if (action_type && strcasecmp(action_type, "ManualDPP") == 0 &&
@@ -1051,6 +1533,110 @@
 }
 
 
+static bool is_pkex_bs(const char *bs)
+{
+	return strcasecmp(bs, "PKEX") == 0 || strcasecmp(bs, "PKEXv1") == 0 ||
+		strcasecmp(bs, "PKEXv2") == 0;
+}
+
+
+static int dpp_pick_uri_curve(struct sigma_dut *dut, const char *ifname,
+			      const char *uri, char *buf, size_t buflen)
+{
+	char tmp[2000], *pos, *pos2;
+	int id;
+	char *curves = NULL;
+	char *saveptr, *res;
+
+	if (!uri || strlen(uri) > 1900)
+		return -1;
+	snprintf(tmp, sizeof(tmp), "DPP_QR_CODE %s", uri);
+	if (wpa_command_resp(ifname, tmp, tmp, sizeof(tmp)) < 0 ||
+	    strncmp(tmp, "FAIL", 4) == 0) {
+		sigma_dut_print(dut, DUT_MSG_INFO, "Failed to parse peer URI");
+		return -1;
+	}
+	id = atoi(tmp);
+
+	snprintf(tmp, sizeof(tmp), "DPP_BOOTSTRAP_INFO %d", id);
+	if (wpa_command_resp(ifname, tmp, tmp, sizeof(tmp)) < 0 ||
+	    strncmp(tmp, "FAIL", 4) == 0) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Failed to get bootstrap information");
+		return -1;
+	}
+
+	pos = tmp;
+	while (pos) {
+		pos2 = strchr(pos, '\n');
+		if (pos2)
+			*pos2 = '\0';
+		if (strncmp(pos, "supp_curves=", 12) == 0) {
+			pos += 12;
+			curves = pos;
+			break;
+		}
+
+		if (!pos2)
+			break;
+		pos = pos2 + 1;
+	}
+
+	if (!curves) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"No supported curves indication in peer URI");
+
+		return -1;
+	}
+
+	sigma_dut_print(dut, DUT_MSG_DEBUG,
+			"Pick alternative curve from URI: %s", curves);
+	res = strtok_r(curves, ":", &saveptr);
+	while (res) {
+		if (strcmp(res, "P-256") != 0) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG, "Selected %s", res);
+			strlcpy(buf, res, buflen);
+			return 0;
+		}
+		res = strtok_r(NULL, ":", &saveptr);
+	}
+
+	sigma_dut_print(dut, DUT_MSG_INFO,
+			"Peer URI did not include any alternative curve");
+	return -1;
+}
+
+
+static bool dpp_peer_uri_available(struct sigma_dut *dut)
+{
+	FILE *f;
+	char buf[1000];
+	size_t len;
+
+	if (dut->dpp_peer_uri)
+		return true;
+
+	f = fopen("/tmp/dpp-rest-server.uri", "r");
+	if (!f)
+		return false;
+
+	len = fread(buf, 1, sizeof(buf) - 1, f);
+	fclose(f);
+	if (!len)
+		return false;
+	buf[len] = '\0';
+
+	sigma_dut_print(dut, DUT_MSG_DEBUG,
+			"Use the most recently received URI from REST API: %s",
+			buf);
+
+	free(dut->dpp_peer_uri);
+	dut->dpp_peer_uri = strdup(buf);
+
+	return dut->dpp_peer_uri != NULL;
+}
+
+
 static enum sigma_cmd_result dpp_automatic_dpp(struct sigma_dut *dut,
 					       struct sigma_conn *conn,
 					       struct sigma_cmd *cmd)
@@ -1080,6 +1666,7 @@
 	char conf_pass[100];
 	char csrattrs[200];
 	char pkex_identifier[200];
+	const char *pkex_ver = "";
 	struct wpa_ctrl *ctrl;
 	int res;
 	unsigned int old_timeout;
@@ -1092,6 +1679,7 @@
 		"DPP-RESPONSE-PENDING",
 		"DPP-SCAN-PEER-QR-CODE",
 		"DPP-AUTH-DIRECTION",
+		"DPP-PB-RESULT",
 		NULL
 	};
 	const char *conf_events[] = {
@@ -1123,6 +1711,10 @@
 	FILE *f;
 	char *no_mud_url = "";
 	char *mud_url = no_mud_url;
+	char tcp_addr[30];
+	bool pb = strcasecmp(bs, "PBBS") == 0;
+	char conf_extra[1000];
+	bool dpp_3rd_party;
 
 	time(&start);
 
@@ -1131,12 +1723,47 @@
 	if (!self_conf)
 		self_conf = "no";
 
+	if (pb) {
+		if (!prov_role) {
+			if (sigma_dut_is_ap(dut))
+				prov_role = "Configurator";
+			else
+				prov_role = "Enrollee";
+		} else if (sigma_dut_is_ap(dut) &&
+			   strcasecmp(prov_role, "Configurator") != 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,PB AP can only be a Configurator");
+			return STATUS_SENT_ERROR;
+		}
+
+		if (!auth_role) {
+			if (sigma_dut_is_ap(dut) ||
+			    strcasecmp(prov_role, "Configurator") == 0)
+				auth_role = "Initiator";
+			else
+				auth_role = "Responder";
+		} else if (sigma_dut_is_ap(dut) &&
+			   strcasecmp(auth_role, "Initiator") != 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,PB AP can only be an Initiator");
+			return STATUS_SENT_ERROR;
+		}
+	}
+
 	if (!prov_role) {
 		send_resp(dut, conn, SIGMA_ERROR,
 			  "errorCode,Missing DPPProvisioningRole");
 		return STATUS_SENT_ERROR;
 	}
 
+	val = get_param(cmd, "DPPPrivNetIntro");
+	if (val && strcasecmp(val, "Yes") == 0 && !sigma_dut_is_ap(dut) &&
+	    wpa_command(ifname, "SET dpp_connector_privacy_default 1") < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Could not enable Connector privacy");
+			return STATUS_SENT_ERROR;
+	}
+
 	val = get_param(cmd, "DPPConfEnrolleeRole");
 	if (val) {
 		enrollee_ap = strcasecmp(val, "AP") == 0;
@@ -1171,6 +1798,21 @@
 		return STATUS_SENT_ERROR;
 	}
 
+	if (sigma_dut_is_ap(dut)) {
+		if (!dut->hostapd_ifname) {
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"hostapd ifname not specified (-j)");
+			return ERROR_SEND_STATUS;
+		}
+		ifname = dut->hostapd_ifname;
+
+		if (dpp_hostapd_run(dut, pb) < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Failed to start hostapd");
+			return STATUS_SENT_ERROR;
+		}
+	}
+
 	val = get_param(cmd, "MUDURL");
 	if (val) {
 		snprintf(buf, sizeof(buf), "SET dpp_mud_url %s", val);
@@ -1181,27 +1823,39 @@
 		}
 	}
 
-	if (sigma_dut_is_ap(dut)) {
-		if (!dut->hostapd_ifname) {
-			sigma_dut_print(dut, DUT_MSG_ERROR,
-					"hostapd ifname not specified (-j)");
-			return ERROR_SEND_STATUS;
-		}
-		ifname = dut->hostapd_ifname;
-
-		if (dpp_hostapd_run(dut) < 0) {
-			send_resp(dut, conn, SIGMA_ERROR,
-				  "errorCode,Failed to start hostapd");
-			return STATUS_SENT_ERROR;
-		}
-	}
-
 	if (strcasecmp(prov_role, "Configurator") == 0 ||
 	    strcasecmp(prov_role, "Both") == 0) {
+		bool nak_curve_set = get_param(cmd, "DPPNAKECC") != NULL;
+		const char *nak_curve, *sign_curve;
+		char sel[20];
+
+		nak_curve = dpp_get_curve(cmd, "DPPNAKECC");
+		sign_curve = dpp_get_curve(cmd, "DPPSigningKeyECC");
+		if (strcasecmp(nak_curve, "URI") == 0 ||
+		    strcasecmp(sign_curve, "URI") == 0) {
+			if (dpp_pick_uri_curve(dut, ifname, dut->dpp_peer_uri,
+					       sel, sizeof(sel)) < 0) {
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "errorCode,Failed to select alternative curve from URI");
+				return STATUS_SENT_ERROR;
+			}
+
+			if (strcasecmp(nak_curve, "URI") == 0)
+				nak_curve = sel;
+			if (strcasecmp(sign_curve, "URI") == 0)
+				sign_curve = sel;
+		}
+
 		if (dut->dpp_conf_id < 0) {
-			snprintf(buf, sizeof(buf),
-				 "DPP_CONFIGURATOR_ADD curve=%s",
-				 dpp_get_curve(cmd, "DPPSigningKeyECC"));
+			if (nak_curve_set) {
+				snprintf(buf, sizeof(buf),
+					 "DPP_CONFIGURATOR_ADD curve=%s net_access_key_curve=%s",
+					 sign_curve, nak_curve);
+			} else {
+				snprintf(buf, sizeof(buf),
+					 "DPP_CONFIGURATOR_ADD curve=%s",
+					 sign_curve);
+			}
 			if (wpa_command_resp(ifname, buf,
 					     buf, sizeof(buf)) < 0) {
 				send_resp(dut, conn, SIGMA_ERROR,
@@ -1209,6 +1863,11 @@
 				return STATUS_SENT_ERROR;
 			}
 			dut->dpp_conf_id = atoi(buf);
+		} else if (nak_curve_set) {
+			snprintf(buf, sizeof(buf),
+				 "DPP_CONFIGURATOR_SET %d net_access_key_curve=%s",
+				 dut->dpp_conf_id, nak_curve);
+			wpa_command(ifname, buf);
 		}
 		if (strcasecmp(prov_role, "Configurator") == 0)
 			role = "configurator";
@@ -1222,8 +1881,35 @@
 		return STATUS_SENT_ERROR;
 	}
 
+	if (auth_role && strcasecmp(auth_role, "Initiator") == 0 &&
+	    tcp && strcasecmp(tcp, "mDNS") == 0) {
+		enum dpp_mdns_role role;
+
+		/* Discover Controller/Relay IP address using mDNS */
+		if (strcasecmp(prov_role, "Configurator") == 0)
+			role = DPP_MDNS_RELAY;
+		else
+			role = DPP_MDNS_CONTROLLER;
+		if (dpp_mdns_discover(dut, role,
+				      tcp_addr, sizeof(tcp_addr), NULL,
+				      NULL) < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Could not discover Controller/Relay IP address using mDNS");
+			return STATUS_SENT_ERROR;
+		}
+		tcp = tcp_addr;
+	}
+
+	if (auth_role && strcasecmp(auth_role, "Initiator") == 0 &&
+	    tcp && strcasecmp(tcp, "URI") == 0) {
+		/* Use the address/port from the host entry in peer URI */
+		tcp = "from-uri";
+	}
+
+	wpa_command(ifname, "SET dpp_discard_public_action 0");
+
 	pkex_identifier[0] = '\0';
-	if (strcasecmp(bs, "PKEX") == 0) {
+	if (is_pkex_bs(bs)) {
 		if (sigma_dut_is_ap(dut) && dut->ap_channel != 6) {
 			/* For now, have to make operating channel match DPP
 			 * listen channel. This should be removed once hostapd
@@ -1268,6 +1954,9 @@
 			return STATUS_SENT_ERROR;
 		}
 		own_pkex_id = atoi(buf);
+
+		if (strcasecmp(bs, "PKEXv1") == 0)
+			pkex_ver = " ver=1";
 	}
 
 	ctrl = open_wpa_mon(ifname);
@@ -1293,6 +1982,7 @@
 	csrattrs[0] = '\0';
 	group_id[0] = '\0';
 	conf2[0] = '\0';
+	conf_extra[0] = '\0';
 	if (!enrollee_configurator) {
 		val = get_param(cmd, "DPPConfIndex");
 		if (val)
@@ -1485,6 +2175,23 @@
 		snprintf(group_id, sizeof(group_id), " group_id=%s",
 			 group_id_str);
 
+	val = get_param(cmd, "DPP3rdParty");
+	dpp_3rd_party = val && strcasecmp(val, "Yes") == 0;
+	if (dpp_3rd_party) {
+		wpa_command(ifname, "SET dpp_extra_conf_req_name com.example");
+		wpa_command(ifname,
+			    "SET dpp_extra_conf_req_value \"sample-info\"");
+	}
+
+	snprintf(conf_extra, sizeof(conf_extra),
+		 "configurator=%d%s%s%s%s%s%s",
+		 dut->dpp_conf_id, group_id,
+		 akm_use_selector ? " akm_use_selector=1" : "",
+		 conn_status ? " conn_status=1" : "",
+		 csrattrs,
+		 dpp_3rd_party ? " conf_extra_name=com.example conf_extra_value=2273616d706c652d696e666f22" : "",
+		 conf2);
+
 	if (force_gas_fragm) {
 		char spaces[1500];
 
@@ -1565,24 +2272,15 @@
 				goto out;
 			}
 			snprintf(buf, sizeof(buf),
-				 "SET dpp_configurator_params  conf=%s %s %s configurator=%d%s%s%s%s%s",
-				 conf_role, conf_ssid, conf_pass,
-				 dut->dpp_conf_id, group_id,
-				 akm_use_selector ? " akm_use_selector=1" : "",
-				 conn_status ? " conn_status=1" : "",
-				 csrattrs, conf2);
+				 "SET dpp_configurator_params  conf=%s %s %s %s",
+				 conf_role, conf_ssid, conf_pass, conf_extra);
 			if (wpa_command(ifname, buf) < 0) {
 				send_resp(dut, conn, SIGMA_ERROR,
 					  "errorCode,Failed to set configurator parameters");
 				goto out;
 			}
-			snprintf(buf, sizeof(buf),
-				 "conf=%s %s %s configurator=%d%s%s%s%s%s",
-				 conf_role, conf_ssid, conf_pass,
-				 dut->dpp_conf_id, group_id,
-				 akm_use_selector ? " akm_use_selector=1" : "",
-				 conn_status ? " conn_status=1" : "", csrattrs,
-				 conf2);
+			snprintf(buf, sizeof(buf), "conf=%s %s %s %s",
+				 conf_role, conf_ssid, conf_pass, conf_extra);
 		} else {
 			buf[0] = '\0';
 			enrollee = 1;
@@ -1768,7 +2466,7 @@
 		}
 
 		if (strcasecmp(bs, "QR") == 0) {
-			if (!dut->dpp_peer_uri) {
+			if (!dpp_peer_uri_available(dut)) {
 				send_resp(dut, conn, SIGMA_ERROR,
 					  "errorCode,Missing peer bootstrapping info");
 				goto out;
@@ -1784,6 +2482,8 @@
 				goto out;
 			}
 			dpp_peer_bootstrap = atoi(buf);
+			free(dut->dpp_peer_uri);
+			dut->dpp_peer_uri = NULL;
 		} else if (strcasecmp(bs, "NFC") == 0 && nfc_handover &&
 			   strcasecmp(nfc_handover, "Static") == 0) {
 			if (!dut->dpp_peer_uri) {
@@ -1853,13 +2553,9 @@
 					goto out;
 				}
 				snprintf(buf, sizeof(buf),
-					 "SET dpp_configurator_params  conf=%s %s %s configurator=%d%s%s%s%s%s",
+					 "SET dpp_configurator_params  conf=%s %s %s %s",
 					 conf_role, conf_ssid, conf_pass,
-					 dut->dpp_conf_id, group_id,
-					 akm_use_selector ?
-					 " akm_use_selector=1" : "",
-					 conn_status ? " conn_status=1" : "",
-					 csrattrs, conf2);
+					 conf_extra);
 				if (wpa_command(ifname, buf) < 0) {
 					send_resp(dut, conn, SIGMA_ERROR,
 						  "errorCode,Failed to set configurator parameters");
@@ -1869,6 +2565,8 @@
 
 			if (tcp && strcasecmp(tcp, "yes") == 0) {
 				wpa_command(ifname, "DPP_STOP_LISTEN");
+				wpa_command(ifname,
+					    "SET dpp_discard_public_action 1");
 				snprintf(buf, sizeof(buf),
 					 "DPP_CONTROLLER_START");
 			} else {
@@ -1887,20 +2585,21 @@
 					  "errorCode,Missing DPPConfIndex");
 				goto out;
 			}
+			if (tcp)
+				wpa_command(ifname,
+					    "SET dpp_discard_public_action 1");
+
 			snprintf(buf, sizeof(buf),
-				 "DPP_AUTH_INIT peer=%d%s role=%s%s%s conf=%s %s %s configurator=%d%s%s%s%s%s%s%s%s",
+				 "DPP_AUTH_INIT peer=%d%s role=%s%s%s conf=%s %s %s %s%s%s %s",
 				 dpp_peer_bootstrap, own_txt, role,
 				 netrole ? " netrole=" : "",
 				 netrole ? netrole : "",
-				 conf_role, conf_ssid, conf_pass,
-				 dut->dpp_conf_id, neg_freq, group_id,
-				 akm_use_selector ? " akm_use_selector=1" : "",
-				 conn_status ? " conn_status=1" : "",
-				 tcp ? " tcp_addr=" : "",
-				 tcp ? tcp : "",
-				 csrattrs, conf2);
+				 conf_role, conf_ssid, conf_pass, neg_freq,
+				 tcp ? " tcp_addr=" : "", tcp ? tcp : "",
+				 conf_extra);
 		} else if (tcp && (strcasecmp(bs, "QR") == 0 ||
 				   strcasecmp(bs, "NFC") == 0)) {
+			wpa_command(ifname, "SET dpp_discard_public_action 1");
 			snprintf(buf, sizeof(buf),
 				 "DPP_AUTH_INIT peer=%d%s role=%s%s%s tcp_addr=%s%s%s",
 				 dpp_peer_bootstrap, own_txt, role,
@@ -1915,7 +2614,7 @@
 				 netrole ? " netrole=" : "",
 				 netrole ? netrole : "",
 				 neg_freq, group_id);
-		} else if (strcasecmp(bs, "PKEX") == 0 &&
+		} else if (is_pkex_bs(bs) &&
 			   (strcasecmp(prov_role, "Configurator") == 0 ||
 			    strcasecmp(prov_role, "Both") == 0)) {
 			if (!conf_role) {
@@ -1923,15 +2622,61 @@
 					  "errorCode,Missing DPPConfIndex");
 				goto out;
 			}
+			if (tcp)
+				wpa_command(ifname, "SET dpp_discard_public_action 1");
 			snprintf(buf, sizeof(buf),
-				 "DPP_PKEX_ADD own=%d init=1 role=%s conf=%s %s %s configurator=%d%s %scode=%s",
-				 own_pkex_id, role, conf_role,
+				 "DPP_PKEX_ADD own=%d init=1%s%s%s role=%s conf=%s %s %s configurator=%d%s %scode=%s",
+				 own_pkex_id, pkex_ver,
+				 tcp ? " tcp_addr=" : "",
+				 tcp ? tcp : "",
+				 role, conf_role,
 				 conf_ssid, conf_pass, dut->dpp_conf_id,
 				 csrattrs, pkex_identifier, pkex_code);
-		} else if (strcasecmp(bs, "PKEX") == 0) {
+		} else if (is_pkex_bs(bs)) {
+			if (tcp)
+				wpa_command(ifname, "SET dpp_discard_public_action 1");
 			snprintf(buf, sizeof(buf),
-				 "DPP_PKEX_ADD own=%d init=1 role=%s %scode=%s",
-				 own_pkex_id, role, pkex_identifier, pkex_code);
+				 "DPP_PKEX_ADD own=%d init=1%s%s%s role=%s %scode=%s",
+				 own_pkex_id, pkex_ver,
+				 tcp ? " tcp_addr=" : "",
+				 tcp ? tcp : "",
+				 role, pkex_identifier, pkex_code);
+		} else if (pb && conf_role && sigma_dut_is_ap(dut)) {
+			dpp_hostapd_beacon(dut);
+			snprintf(buf, sizeof(buf),
+				 "DPP_PUSH_BUTTON role=%s%s%s conf=%s %s %s %s %s",
+				 role,
+				 netrole ? " netrole=" : "",
+				 netrole ? netrole : "",
+				 conf_role, conf_ssid, conf_pass,
+				 neg_freq, conf_extra);
+		} else if (pb && sigma_dut_is_ap(dut)) {
+			dpp_hostapd_beacon(dut);
+			snprintf(buf, sizeof(buf),
+				 "DPP_PUSH_BUTTON");
+		} else if (pb) {
+			int freq = 2437;
+
+			val = get_param(cmd, "DPPListenChannel");
+			if (val) {
+				freq = channel_to_freq(dut, atoi(val));
+				if (freq == 0) {
+					send_resp(dut, conn, SIGMA_ERROR,
+						  "errorCode,Unsupported DPPListenChannel value");
+					goto out;
+				}
+			}
+
+			snprintf(buf, sizeof(buf), "DPP_LISTEN %d", freq);
+			wpa_command(ifname, buf);
+
+			snprintf(buf, sizeof(buf),
+				 "DPP_PUSH_BUTTON role=%s%s%s conf=%s %s %s %s %s",
+				 role,
+				 netrole ? " netrole=" : "",
+				 netrole ? netrole : "",
+				 conf_role, conf_ssid, conf_pass,
+				 neg_freq, conf_extra);
 		} else {
 			send_resp(dut, conn, SIGMA_ERROR,
 				  "errorCode,Unsupported DPPBS");
@@ -1955,7 +2700,7 @@
 		    dut->ap_oper_chn)
 			freq = channel_to_freq(dut, dut->ap_channel);
 
-		if (strcasecmp(bs, "PKEX") == 0) {
+		if (is_pkex_bs(bs)) {
 			/* default: channel 6 for PKEX */
 			freq = 2437;
 		}
@@ -2060,37 +2805,26 @@
 				goto out;
 			}
 			snprintf(buf, sizeof(buf),
-				 "SET dpp_configurator_params  conf=%s %s %s configurator=%d%s%s%s%s%s",
-				 conf_role, conf_ssid, conf_pass,
-				 dut->dpp_conf_id, group_id,
-				 akm_use_selector ? " akm_use_selector=1" : "",
-				 conn_status ? " conn_status=1" : "", csrattrs,
-				 conf2);
+				 "SET dpp_configurator_params  conf=%s %s %s %s",
+				 conf_role, conf_ssid, conf_pass, conf_extra);
 			if (wpa_command(ifname, buf) < 0) {
 				send_resp(dut, conn, SIGMA_ERROR,
 					  "errorCode,Failed to set configurator parameters");
 				goto out;
 			}
 		}
-		if (strcasecmp(bs, "PKEX") == 0) {
-			snprintf(buf, sizeof(buf),
-				 "DPP_PKEX_ADD own=%d role=%s %scode=%s",
-				 own_pkex_id, role, pkex_identifier, pkex_code);
-			if (wpa_command(ifname, buf) < 0) {
-				send_resp(dut, conn, SIGMA_ERROR,
-					  "errorCode,Failed to configure DPP PKEX");
-				goto out;
-			}
-		}
 
 		if (chirp) {
 			snprintf(buf, sizeof(buf),
 				 "DPP_CHIRP own=%d iter=10 listen=%d",
 				 dut->dpp_local_bootstrap, freq);
 		} else if (tcp && strcasecmp(tcp, "yes") == 0) {
+			wpa_command(ifname, "SET dpp_discard_public_action 1");
 			snprintf(buf, sizeof(buf), "DPP_CONTROLLER_START%s",
 				 (strcasecmp(bs, "QR") == 0 && mutual) ?
 				 " qr=mutual" : "");
+		} else if (pb) {
+			snprintf(buf, sizeof(buf), "DPP_PUSH_BUTTON");
 		} else {
 			snprintf(buf, sizeof(buf),
 				 "DPP_LISTEN %d role=%s%s%s%s",
@@ -2106,6 +2840,18 @@
 			goto out;
 		}
 
+		if (is_pkex_bs(bs)) {
+			snprintf(buf, sizeof(buf),
+				 "DPP_PKEX_ADD own=%d%s role=%s %scode=%s",
+				 own_pkex_id, pkex_ver, role,
+				 pkex_identifier, pkex_code);
+			if (wpa_command(ifname, buf) < 0) {
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "errorCode,Failed to configure DPP PKEX");
+				goto out;
+			}
+		}
+
 		if (!(tcp && strcasecmp(tcp, "yes") == 0) &&
 		    get_driver_type(dut) == DRIVER_OPENWRT) {
 			snprintf(buf, sizeof(buf), "iwconfig %s channel %d",
@@ -2304,18 +3050,20 @@
 		goto out;
 	}
 
-	if (!frametype && strcasecmp(bs, "PKEX") == 0 &&
+	if (!frametype && is_pkex_bs(bs) &&
 	    auth_role && strcasecmp(auth_role, "Responder") == 0) {
-		if (dpp_wait_tx_status(dut, ctrl, 10) < 0) {
+		/* TODO: PKEX timeout check for over-TCP case? */
+		if (!tcp && dpp_wait_tx_status(dut, ctrl, 10) < 0) {
 			send_resp(dut, conn, SIGMA_COMPLETE,
 				  "BootstrapResult,Timeout");
 			goto out;
 		}
 	}
 
-	if (!frametype && strcasecmp(bs, "PKEX") == 0 &&
+	if (!frametype && is_pkex_bs(bs) &&
 	    auth_role && strcasecmp(auth_role, "Initiator") == 0) {
-		if (dpp_wait_tx(dut, ctrl, 0) < 0) {
+		/* TODO: PKEX timeout check for over-TCP case? */
+		if (!tcp && dpp_wait_tx(dut, ctrl, 0) < 0) {
 			send_resp(dut, conn, SIGMA_COMPLETE,
 				  "BootstrapResult,Timeout");
 			goto out;
@@ -2456,8 +3204,15 @@
 		memcpy(mud_url, ",MUDURL,", 8);
 		memcpy(mud_url + 8, pos, url_len + 1);
 
-		res = get_wpa_cli_events(dut, ctrl, conf_events,
-					 buf, sizeof(buf));
+		/* DPP-MUD-URL can be returned multiple times when configuration
+		 * exchange needs to perform multiple GAS queries, e.g., for
+		 * CSR or key changes. */
+		for (;;) {
+			res = get_wpa_cli_events(dut, ctrl, conf_events,
+						 buf, sizeof(buf));
+			if (res < 0 || !strstr(buf, "DPP-MUD-URL "))
+				break;
+		}
 	}
 	if (res < 0) {
 		send_resp(dut, conn, SIGMA_COMPLETE,
@@ -2473,6 +3228,22 @@
 		goto out;
 	}
 
+	pos = strstr(buf, " conf_status=");
+	if (pos && strstr(buf, "DPP-CONF-SENT")) {
+		int status;
+
+		pos += 13;
+		status = atoi(pos);
+		if (status) {
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"Configurator rejected configuration with status %d",
+					status);
+			send_resp(dut, conn, SIGMA_COMPLETE,
+				  "BootstrapResult,OK,AuthResult,OK,ConfResult,FAILED");
+			goto out;
+		}
+	}
+
 	if (conn_status && strstr(buf, "DPP-CONF-SENT") &&
 	    strstr(buf, "wait_conn_status=1")) {
 		res = get_wpa_cli_event(dut, ctrl, "DPP-CONN-STATUS-RESULT",
@@ -2738,6 +3509,9 @@
 			     struct sigma_cmd *cmd)
 {
 	const char *val;
+	const char *step = get_param(cmd, "DPPStep");
+	const char *frametype = get_param(cmd, "DPPFrameType");
+	const char *attr = get_param(cmd, "DPPIEAttribute");
 	int freq;
 	struct wpa_ctrl *ctrl = NULL;
 	const char *ifname;
@@ -2763,6 +3537,8 @@
 		"DPP-CONF-FAILED",
 		NULL
 	};
+	unsigned int old_timeout = dut->default_timeout;
+	bool controller_started = false;
 
 	if (sigma_dut_is_ap(dut)) {
 		if (!dut->hostapd_ifname) {
@@ -2993,6 +3769,26 @@
 		}
 	}
 
+	if (step && frametype) {
+		int test;
+
+		test = dpp_get_test(step, frametype, attr);
+		if (test <= 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Unsupported DPPStep/DPPFrameType/DPPIEAttribute");
+			goto out;
+		}
+
+		snprintf(buf, sizeof(buf), "SET dpp_test %d", test);
+		if (wpa_command(ifname, buf) < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Failed to set dpp_test");
+			goto out;
+		}
+	} else {
+		wpa_command(ifname, "SET dpp_test 0");
+	}
+
 	snprintf(buf, sizeof(buf),
 		 "SET dpp_configurator_params  conf=%s %s %s configurator=%d%s%s%s%s%s",
 		 conf_role, conf_ssid, conf_pass,
@@ -3013,6 +3809,13 @@
 		return ERROR_SEND_STATUS;
 	}
 
+	val = get_param(cmd, "DPPTimeout");
+	if (val && atoi(val) > 0) {
+		dut->default_timeout = atoi(val);
+		sigma_dut_print(dut, DUT_MSG_DEBUG, "DPP timeout: %u",
+				dut->default_timeout);
+	}
+
 	val = get_param(cmd, "DPPListenChannel");
 	if (val) {
 		freq = channel_to_freq(dut, atoi(val));
@@ -3030,6 +3833,27 @@
 		}
 	}
 
+	val = get_param(cmd, "DPPOverTCP");
+	if (val && strcasecmp(val, "yes") == 0) {
+		wpa_command(ifname, "DPP_STOP_LISTEN");
+		wpa_command(ifname, "SET dpp_discard_public_action 1");
+		wpa_command(ifname, "DPP_CONTROLLER_START");
+		controller_started = true;
+	} else {
+		wpa_command(ifname, "SET dpp_discard_public_action 0");
+	}
+
+	if (frametype && strcasecmp(frametype, "ReconfigAuthRequest") == 0) {
+		const char *result;
+
+		if (dpp_wait_tx_status(dut, ctrl, 15) < 0)
+			result = "ReconfigAuthResult,Timeout";
+		else
+			result = "ReconfigAuthResult,Errorsent";
+		send_resp(dut, conn, SIGMA_COMPLETE, result);
+		goto out;
+	}
+
 	res = get_wpa_cli_event(dut, ctrl, "DPP-CONF-REQ-RX",
 				buf, sizeof(buf));
 	if (res < 0) {
@@ -3080,6 +3904,9 @@
 		wpa_ctrl_detach(ctrl);
 		wpa_ctrl_close(ctrl);
 	}
+	if (controller_started)
+		wpa_command(ifname, "DPP_CONTROLLER_STOP");
+	dut->default_timeout = old_timeout;
 	return STATUS_SENT;
 err:
 	send_resp(dut, conn, SIGMA_ERROR, NULL);
@@ -3235,6 +4062,265 @@
 }
 
 
+#define AVAHI_SERVICE "/etc/avahi/services/sigma_dut-dpp.service"
+
+int dpp_mdns_start(struct sigma_dut *dut, enum dpp_mdns_role role)
+{
+	FILE *f;
+	const char *org = "Qualcomm DPP testing";
+	const char *location = "Somewhere in the test network";
+	const char *bskeyhash = NULL;
+	const char *ifname = get_station_ifname(dut);
+	char buf[2000];
+
+	if (sigma_dut_is_ap(dut)) {
+		if (!dut->hostapd_ifname) {
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"hostapd ifname not specified (-j)");
+			return -1;
+		}
+		ifname = dut->hostapd_ifname;
+	}
+
+	if (role == DPP_MDNS_CONTROLLER && dut->dpp_local_bootstrap >= 0) {
+		char *pos, *pos2;
+		unsigned char pkhash[32];
+		int pkhash_len = -1;
+
+		snprintf(buf, sizeof(buf), "DPP_BOOTSTRAP_INFO %d",
+			 dut->dpp_local_bootstrap);
+		if (wpa_command_resp(ifname, buf, buf, sizeof(buf)) < 0 ||
+		    strncmp(buf, "FAIL", 4) == 0) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Failed to get bootstrap information");
+			return -1;
+		}
+
+		pos = buf;
+		while (pos) {
+			pos2 = strchr(pos, '\n');
+			if (pos2)
+				*pos2 = '\0';
+			if (strncmp(pos, "pkhash=", 7) == 0) {
+				pkhash_len = parse_hexstr(pos + 7, pkhash,
+							  sizeof(pkhash));
+				break;
+			}
+
+			if (!pos2)
+				break;
+			pos = pos2 + 1;
+		}
+
+		if (pkhash_len != 32 ||
+		    base64_encode((char *) pkhash, pkhash_len,
+				  buf, sizeof(buf)) < 0) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"Failed to get own bootstrapping public key hash");
+			return -1;
+		}
+
+		bskeyhash = buf;
+	}
+
+	f = fopen(AVAHI_SERVICE, "w");
+	if (!f) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Could not write Avahi service file (%s)",
+				AVAHI_SERVICE);
+		return -1;
+	}
+
+	fprintf(f, "<?xml version=\"1.0\" standalone=\"no\"?>\n");
+	fprintf(f, "<!DOCTYPE service-group SYSTEM \"avahi-service.dtd\">\n");
+	fprintf(f, "<service-group>\n");
+	fprintf(f, "  <name replace-wildcards=\"yes\">%%h</name>\n");
+	fprintf(f, "  <service>\n");
+	fprintf(f, "    <type>_dpp._tcp</type>\n");
+	fprintf(f, "    <subtype>_%s._sub._dpp._tcp</subtype>\n",
+		dpp_mdns_role_txt(role));
+	fprintf(f, "    <port>8908</port>\n");
+	fprintf(f, "    <txt-record>txtversion=1</txt-record>\n");
+	fprintf(f, "    <txt-record>organization=%s</txt-record>\n", org);
+	fprintf(f, "    <txt-record>location=%s</txt-record>\n", location);
+	if (bskeyhash)
+		fprintf(f, "    <txt-record>bskeyhash=%s</txt-record>\n",
+			bskeyhash);
+	if (role == DPP_MDNS_RELAY) {
+		int opclass, chan;
+
+		chan = dut->ap_channel;
+		if (!chan)
+			chan = 11;
+		if (chan >= 1 && chan <= 13)
+			opclass = 81;
+		else if (chan >= 36 && chan <= 48)
+			opclass = 115;
+		else if (chan >= 52 && chan <= 64)
+			opclass = 118;
+		else if (chan >= 100 && chan <= 144)
+			opclass = 121;
+		else if (chan >= 149 && chan <= 177)
+			opclass = 125;
+		else
+			opclass = 0;
+
+		fprintf(f, "    <txt-record>channellist=%d/%d</txt-record>\n",
+			opclass, chan);
+	}
+	fprintf(f, "  </service>\n");
+	fprintf(f, "</service-group>\n");
+
+	fclose(f);
+
+	sigma_dut_print(dut, DUT_MSG_INFO, "Started DPP mDNS advertisement");
+	dut->dpp_mdns = role;
+
+	return 0;
+}
+
+
+void dpp_mdns_stop(struct sigma_dut *dut)
+{
+	dut->dpp_mdns = DPP_MDNS_NOT_RUNNING;
+
+	if (file_exists(AVAHI_SERVICE)) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Stopping DPP mDNS service advertisement");
+		unlink(AVAHI_SERVICE);
+	}
+}
+
+
+static enum sigma_cmd_result dpp_set_mdns_advertise(struct sigma_dut *dut,
+						    struct sigma_conn *conn,
+						    struct sigma_cmd *cmd,
+						    const char *role)
+{
+	int ret = -1;
+
+	if (strcasecmp(role, "Relay") == 0) {
+		ret = dpp_mdns_start(dut, DPP_MDNS_RELAY);
+	} else if (strcasecmp(role, "Controller") == 0) {
+		const char *curve = dpp_get_curve(cmd, "DPPCryptoIdentifier");
+
+		if (sigma_dut_is_ap(dut) && dpp_hostapd_run(dut, false) < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Failed to start hostapd");
+			return STATUS_SENT_ERROR;
+		}
+
+		if (dut->dpp_local_bootstrap < 0) {
+			char buf[200], resp[200];
+			int res;
+			const char *ifname = get_station_ifname(dut);
+
+			if (sigma_dut_is_ap(dut)) {
+				if (!dut->hostapd_ifname) {
+					sigma_dut_print(dut, DUT_MSG_ERROR,
+							"hostapd ifname not specified (-j)");
+					return ERROR_SEND_STATUS;
+				}
+				ifname = dut->hostapd_ifname;
+			}
+
+
+			res = snprintf(buf, sizeof(buf),
+				       "DPP_BOOTSTRAP_GEN type=qrcode curve=%s",
+				       curve);
+			if (res < 0 || res >= sizeof(buf) ||
+			    wpa_command_resp(ifname, buf, resp,
+					     sizeof(resp)) < 0 ||
+			    strncmp(resp, "FAIL", 4) == 0) {
+				sigma_dut_print(dut, DUT_MSG_INFO,
+						"Failed to generate own bootstrapping key");
+				return ERROR_SEND_STATUS;
+			}
+			dut->dpp_local_bootstrap = atoi(resp);
+		}
+		ret = dpp_mdns_start(dut, DPP_MDNS_CONTROLLER);
+	} else if (strcasecmp(role, "Bootstrapping") == 0) {
+		ret = dpp_mdns_start(dut, DPP_MDNS_BOOTSTRAPPING);
+	} else {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"Unsupported DPPmDNSAdvertise role: %s", role);
+	}
+
+	return ret < 0 ? ERROR_SEND_STATUS : SUCCESS_SEND_STATUS;
+}
+
+
+static int dpp_check_mdns_discovery_result(struct sigma_dut *dut)
+{
+	if (sigma_dut_is_ap(dut) && dut->ap_dpp_conf_addr &&
+	    strcasecmp(dut->ap_dpp_conf_addr, "mDNS") == 0 &&
+	    dpp_mdns_discover_relay_params(dut) < 0) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"Failed to discover Controller for AP Relay using mDNS");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static enum sigma_cmd_result dpp_set_parameter(struct sigma_dut *dut,
+					       struct sigma_conn *conn,
+					       struct sigma_cmd *cmd)
+{
+	const char *val;
+	enum sigma_cmd_result res = SUCCESS_SEND_STATUS;
+
+	val = get_param(cmd, "DPPmDNSAdvertise");
+	if (val &&
+	    dpp_set_mdns_advertise(dut, conn, cmd, val) < 0)
+		res = ERROR_SEND_STATUS;
+
+	val = get_param(cmd, "DPPmDNSEnable");
+	if (val && strcasecmp(val, "Yes") == 0 &&
+	    dpp_check_mdns_discovery_result(dut) < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,mDNS discovery has not succeeded");
+		return STATUS_SENT_ERROR;
+	}
+
+	return res;
+}
+
+
+static enum sigma_cmd_result dpp_get_peer_bootstrap(struct sigma_dut *dut,
+						    struct sigma_conn *conn,
+						    struct sigma_cmd *cmd)
+{
+	char uri[1000], hex[2000], resp[2100];
+	FILE *f;
+	int res;
+	size_t len;
+
+	f = fopen("/tmp/dpp-rest-server.uri", "r");
+	if (!f) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,No DPP URI received through REST API");
+		return STATUS_SENT_ERROR;
+	}
+
+	len = fread(uri, 1, sizeof(uri), f);
+	fclose(f);
+	if (len == 0 || len == sizeof(uri)) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Could not read the received DPP URI");
+		return STATUS_SENT_ERROR;
+	}
+	uri[len] = '\0';
+
+	ascii2hexstr(uri, hex);
+	res = snprintf(resp, sizeof(resp), "BootstrappingData,%s", hex);
+	send_resp(dut, conn, SIGMA_COMPLETE,
+		  res >= 0 && res < sizeof(resp) ? resp : NULL);
+	return STATUS_SENT;
+}
+
+
 enum sigma_cmd_result dpp_dev_exec_action(struct sigma_dut *dut,
 					  struct sigma_conn *conn,
 					  struct sigma_cmd *cmd)
@@ -3250,6 +4336,10 @@
 
 	if (strcasecmp(type, "DPPReconfigure") == 0)
 		return dpp_reconfigure(dut, conn, cmd);
+	if (strcasecmp(type, "SetParameter") == 0)
+		return dpp_set_parameter(dut, conn, cmd);
+	if (strcasecmp(type, "GetPeerBootstrap") == 0)
+		return dpp_get_peer_bootstrap(dut, conn, cmd);
 
 	if (!bs) {
 		send_resp(dut, conn, SIGMA_ERROR,
diff --git a/server.c b/server.c
index bd07f22..0d2da05 100644
--- a/server.c
+++ b/server.c
@@ -245,8 +245,12 @@
 				SERVER_DB);
 		return -1;
 	}
-	sql = sqlite3_mprintf("DELETE FROM cert_enroll WHERE mac_addr=%Q",
-			      addr);
+
+	if (strcasecmp(addr, "any") == 0)
+		sql = sqlite3_mprintf("DELETE FROM cert_enroll");
+	else
+		sql = sqlite3_mprintf("DELETE FROM cert_enroll WHERE mac_addr=%Q",
+				      addr);
 	if (!sql) {
 		sqlite3_close(db);
 		return -1;
@@ -321,7 +325,7 @@
 	}
 
 	prog = sigma_program_to_enum(var);
-	if (prog != PROGRAM_HS2_R2 && prog != PROGRAM_HS2_R3) {
+	if (!is_passpoint_r2_or_newer(prog)) {
 		send_resp(dut, conn, SIGMA_ERROR,
 			  "errorCode,Unsupported program");
 		return STATUS_SENT;
@@ -485,8 +489,11 @@
 {
 	char *sql, *last_serial = NULL;
 
-	sql = sqlite3_mprintf("SELECT serialnum FROM cert_enroll WHERE mac_addr=%Q",
-			      addr);
+	if (!addr || strcasecmp(addr, "any") == 0)
+		sql = sqlite3_mprintf("SELECT serialnum FROM cert_enroll");
+	else
+		sql = sqlite3_mprintf("SELECT serialnum FROM cert_enroll WHERE mac_addr=%Q",
+				      addr);
 	if (!sql)
 		return NULL;
 	sigma_dut_print(dut, DUT_MSG_DEBUG, "SQL: %s", sql);
@@ -877,7 +884,7 @@
 	}
 
 	prog = sigma_program_to_enum(var);
-	if (prog != PROGRAM_HS2_R2 && prog != PROGRAM_HS2_R3) {
+	if (!is_passpoint_r2_or_newer(prog)) {
 		send_resp(dut, conn, SIGMA_ERROR,
 			  "errorCode,Unsupported program");
 		return STATUS_SENT;
@@ -936,7 +943,7 @@
 		return aaa_auth_status(dut, conn, cmd, resp, timeout);
 	}
 
-	if (osu && status && strcasecmp(status, "OSU") == 0 && addr)
+	if (osu && status && strcasecmp(status, "OSU") == 0)
 		return osu_cert_enroll_status(dut, conn, cmd, addr, timeout);
 
 	if (osu && status && strcasecmp(status, "PolicyProvisioning") == 0 &&
@@ -1005,7 +1012,7 @@
 	}
 
 	prog = sigma_program_to_enum(var);
-	if (prog != PROGRAM_HS2_R2 && prog != PROGRAM_HS2_R3) {
+	if (!is_passpoint_r2_or_newer(prog)) {
 		send_resp(dut, conn, SIGMA_ERROR,
 			  "errorCode,Unsupported program");
 		return STATUS_SENT;
diff --git a/sigma_dut.c b/sigma_dut.c
index c4d13aa..07bfc15 100644
--- a/sigma_dut.c
+++ b/sigma_dut.c
@@ -860,6 +860,7 @@
 #ifdef ANDROID
 	dut->dscp_use_iptables = 1;
 #endif /* ANDROID */
+	dut->autoconnect_default = 1;
 }
 
 
@@ -999,7 +1000,7 @@
 
 static void usage(void)
 {
-	printf("usage: sigma_dut [-aABdfGqDIntuVW234] [-p<port>] "
+	printf("usage: sigma_dut [-aABdfGqDIntuVW2347] [-p<port>] "
 	       "[-s<sniffer>] [-m<set_maccaddr.sh>] \\\n"
 	       "       [-M<main ifname>] [-R<radio ifname>] "
 	       "[-S<station ifname>] [-P<p2p_ifname>]\\\n"
@@ -1053,7 +1054,7 @@
 
 	for (;;) {
 		c = getopt(argc, argv,
-			   "aAb:Bc:C:dDE:e:fF:gGhH:j:J:i:Ik:K:l:L:m:M:nN:o:O:p:P:qQr:R:s:S:tT:uv:VWw:x:y:z:Z:2345:6:");
+			   "aAb:Bc:C:dDE:e:fF:gGhH:j:J:i:Ik:K:l:L:m:M:nN:o:O:p:P:qQr:R:s:S:tT:uv:VWw:x:y:z:Z:2345:6:7");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -1288,6 +1289,9 @@
 				exit(1);
 			}
 			break;
+		case '7':
+			sigma_dut.autoconnect_default = 0;
+			break;
 		case 'h':
 		default:
 			usage();
diff --git a/sigma_dut.h b/sigma_dut.h
index 60ce86c..73a234a 100644
--- a/sigma_dut.h
+++ b/sigma_dut.h
@@ -94,6 +94,7 @@
 #define MAX_RADIO 3
 
 #define NAN_AWARE_IFACE "wifi-aware0"
+#define BROADCAST_ADDR "255.255.255.255"
 
 /* Set default operating channel width 80 MHz */
 #define VHT_DEFAULT_OPER_CHWIDTH AP_80_VHT_OPER_CHWIDTH
@@ -397,6 +398,13 @@
 	struct dscp_policy_data *next;
 };
 
+enum dpp_mdns_role {
+	DPP_MDNS_NOT_RUNNING,
+	DPP_MDNS_RELAY,
+	DPP_MDNS_CONTROLLER,
+	DPP_MDNS_BOOTSTRAPPING,
+};
+
 struct sigma_dut {
 	const char *main_ifname;
 	char *main_ifname_2g;
@@ -893,6 +901,8 @@
 		PROGRAM_HE,
 		PROGRAM_HS2_R3,
 		PROGRAM_QM,
+		PROGRAM_HS2_R4,
+		PROGRAM_HS2_2022,
 	} program;
 
 	enum device_type {
@@ -988,6 +998,7 @@
 	int dpp_local_bootstrap;
 	int dpp_conf_id;
 	int dpp_network_id;
+	enum dpp_mdns_role dpp_mdns;
 
 	u8 fils_hlp;
 	pthread_t hlp_thread;
@@ -1041,6 +1052,8 @@
 	unsigned int prev_disable_scs_support;
 	unsigned int prev_disable_mscs_support;
 	int dscp_use_iptables;
+	int autoconnect_default;
+	int dhcp_client_running;
 };
 
 
@@ -1182,6 +1195,7 @@
 int wil6210_set_force_mcs(struct sigma_dut *dut, int force, int mcs);
 int sta_set_addba_buf_size(struct sigma_dut *dut,
 			   const char *intf, int bufsize);
+int wcn_set_he_gi(struct sigma_dut *dut, const char *intf, u8 gi_val);
 #ifdef NL80211_SUPPORT
 int wcn_set_he_ltf(struct sigma_dut *dut, const char *intf,
 		   enum qca_wlan_he_ltf_cfg ltf);
@@ -1209,6 +1223,8 @@
 
 /* utils.c */
 enum sigma_program sigma_program_to_enum(const char *prog);
+bool is_passpoint_r2_or_newer(enum sigma_program prog);
+bool is_passpoint(enum sigma_program prog);
 int hex_byte(const char *str);
 int parse_hexstr(const char *hex, unsigned char *buf, size_t buflen);
 int parse_mac_address(struct sigma_dut *dut, const char *arg,
@@ -1233,6 +1249,7 @@
 
 int get_wps_forced_version(struct sigma_dut *dut, const char *str);
 int base64_encode(const char *src, size_t len, char *out, size_t out_len);
+unsigned char * base64_decode(const char *src, size_t len, size_t *out_len);
 int random_get_bytes(char *buf, size_t len);
 int get_enable_disable(const char *val);
 int wcn_driver_cmd(const char *ifname, char *buf);
@@ -1274,6 +1291,9 @@
 enum sigma_cmd_result dpp_dev_exec_action(struct sigma_dut *dut,
 					  struct sigma_conn *conn,
 					  struct sigma_cmd *cmd);
+int dpp_mdns_discover_relay_params(struct sigma_dut *dut);
+int dpp_mdns_start(struct sigma_dut *dut, enum dpp_mdns_role role);
+void dpp_mdns_stop(struct sigma_dut *dut);
 
 /* dhcp.c */
 void process_fils_hlp(struct sigma_dut *dut);
@@ -1282,6 +1302,8 @@
 #ifdef NL80211_SUPPORT
 struct nl80211_ctx * nl80211_init(struct sigma_dut *dut);
 void nl80211_deinit(struct sigma_dut *dut, struct nl80211_ctx *ctx);
+int nl80211_open_event_sock(struct sigma_dut *dut);
+void nl80211_close_event_sock(struct sigma_dut *dut);
 struct nl_msg * nl80211_drv_msg(struct sigma_dut *dut, struct nl80211_ctx *ctx,
 				int ifindex, int flags,
 				uint8_t cmd);
@@ -1307,5 +1329,7 @@
 void miracast_register_cmds(void);
 int set_ipv6_addr(struct sigma_dut *dut, const char *ip, const char *mask,
 		  const char *ifname);
+void kill_pid(struct sigma_dut *dut, const char *pid_file);
+int get_ip_addr(const char *ifname, int ipv6, char *buf, size_t len);
 
 #endif /* SIGMA_DUT_H */
diff --git a/sta.c b/sta.c
index 2940757..fd1a2c5 100644
--- a/sta.c
+++ b/sta.c
@@ -34,6 +34,7 @@
 #include "wpa_helpers.h"
 #include "miracast.h"
 #include "qca-vendor_copy.h"
+#include "nl80211_copy.h"
 
 /* Temporary files for sta_send_addba */
 #define VI_QOS_TMP_FILE     "/tmp/vi-qos.tmp"
@@ -1288,6 +1289,8 @@
 			sleep(1);
 		}
 	}
+
+	dut->dhcp_client_running = 0;
 #endif /* __linux__ */
 }
 
@@ -1328,6 +1331,8 @@
 #endif /* ANDROID */
 		}
 	}
+
+	dut->dhcp_client_running = 1;
 #endif /* __linux__ */
 
 	return 0;
@@ -1491,6 +1496,15 @@
 			sigma_dut_print(dut, DUT_MSG_INFO, "Using IPv6 "
 					"stateless address autoconfiguration");
 #ifdef ANDROID
+			snprintf(buf, sizeof(buf),
+				 "sysctl net.ipv6.conf.%s.disable_ipv6=0",
+				 ifname);
+			sigma_dut_print(dut, DUT_MSG_DEBUG, "Run: %s", buf);
+			if (system(buf) != 0) {
+				sigma_dut_print(dut, DUT_MSG_DEBUG,
+						"Failed to enable IPv6 address");
+			}
+
 			/*
 			 * This sleep is required as the assignment in case of
 			 * Android is taking time and is done by the kernel.
@@ -1584,9 +1598,24 @@
 	val = get_param(cmd, "primary-dns");
 	if (val) {
 #ifdef ANDROID
-		/* TODO */
-		sigma_dut_print(dut, DUT_MSG_INFO, "Ignored primary-dns %s "
-				"setting", val);
+		char dns_cmd[200];
+		int len;
+		char dnsmasq[100];
+
+		kill_pid(dut, concat_sigma_tmpdir(dut, "/sigma_dut-dnsmasq.pid",
+						  dnsmasq, sizeof(dnsmasq)));
+
+		len = snprintf(dns_cmd, sizeof(dns_cmd),
+			       "/system/bin/dnsmasq -uroot --no-resolv -S%s -x/%s", val,
+			       dnsmasq);
+		if (len < 0 || len >= sizeof(dns_cmd))
+			return ERROR_SEND_STATUS;
+		sigma_dut_print(dut, DUT_MSG_DEBUG, "Running %s", dns_cmd);
+		if (system(dns_cmd) != 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "ErrorCode,Failed to set primary-dns");
+			return STATUS_SENT_ERROR;
+		}
 #else /* ANDROID */
 		char dns_cmd[200];
 		int len;
@@ -2586,6 +2615,30 @@
 		}
 	}
 
+	val = get_param(cmd, "imsiPrivacyCert");
+	if (val) {
+		snprintf(buf, sizeof(buf), "%s/%s", sigma_cert_path, val);
+#ifdef __linux__
+		if (!file_exists(buf)) {
+			char msg[300];
+
+			snprintf(msg, sizeof(msg),
+				 "ErrorCode,imsiPrivacyCert file (%s) not found",
+				 buf);
+			send_resp(dut, conn, SIGMA_ERROR, msg);
+			return STATUS_SENT_ERROR;
+		}
+#endif /* __linux__ */
+		if (set_network_quoted(ifname, id, "imsi_privacy_cert",
+				       buf) < 0)
+			return ERROR_SEND_STATUS;
+	}
+
+	val = get_param(cmd, "imsiPrivacyCertID");
+	if (val && set_network_quoted(ifname, id, "imsi_privacy_attr",
+				      val) < 0)
+		return ERROR_SEND_STATUS;
+
 	if (dut->akm_values &
 	    ((1 << AKM_FILS_SHA256) |
 	     (1 << AKM_FILS_SHA384) |
@@ -2632,6 +2685,7 @@
 						struct sigma_conn *conn,
 						struct sigma_cmd *cmd)
 {
+	const char *type = get_param(cmd, "Type");
 	const char *intf = get_param(cmd, "Interface");
 	const char *ifname, *val;
 	int id;
@@ -2751,6 +2805,16 @@
 		}
 	}
 
+	if (set_network(ifname, id, "ocsp", "1") < 0)
+		return ERROR_SEND_STATUS;
+
+	if (type && strcasecmp(type, "EAPTLS_1_3") == 0) {
+		sigma_dut_print(dut, DUT_MSG_INFO, "Enable only TLS v1.3");
+		if (set_network_quoted(ifname, id, "phase1",
+				       "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0") < 0)
+			return ERROR_SEND_STATUS;
+	}
+
 	return 1;
 }
 
@@ -3089,7 +3153,8 @@
 	    strcasecmp(type, "PSK-SAE") == 0 ||
 	    strcasecmp(type, "SAE") == 0)
 		return cmd_sta_set_psk(dut, conn, cmd);
-	if (strcasecmp(type, "EAPTLS") == 0)
+	if (strcasecmp(type, "EAPTLS") == 0 ||
+	    strcasecmp(type, "EAPTLS_1_3") == 0)
 		return cmd_sta_set_eaptls(dut, conn, cmd);
 	if (strcasecmp(type, "EAPTTLS") == 0)
 		return cmd_sta_set_eapttls(dut, conn, cmd);
@@ -4605,7 +4670,8 @@
 
 		if ((dut->program == PROGRAM_WPA3 &&
 		     dut->sta_associate_wait_connect) ||
-		    dut->program == PROGRAM_QM) {
+		    dut->program == PROGRAM_QM ||
+		    (dut->dhcp_client_running && dut->client_privacy)) {
 			ctrl = open_wpa_mon(get_station_ifname(dut));
 			if (!ctrl)
 				return ERROR_SEND_STATUS;
@@ -4716,6 +4782,22 @@
 		}
 
 		if (strstr(buf, "CTRL-EVENT-CONNECTED")) {
+			if (dut->dhcp_client_running && dut->client_privacy) {
+				/*
+				 * Interface MAC address will be changed by
+				 * wpa_supplicant before connection attempt when
+				 * client privacy enabled. Restart DHCP client
+				 * to make sure DHCP frames use the correct
+				 * source MAC address.
+				 * */
+				kill_dhcp_client(dut, ifname);
+				if (start_dhcp_client(dut, ifname) < 0) {
+					send_resp(dut, conn, SIGMA_COMPLETE,
+						  "Result,DHCP client start failed");
+					ret = STATUS_SENT_ERROR;
+					break;
+				}
+			}
 			if (tod >= 0) {
 				sigma_dut_print(dut, DUT_MSG_DEBUG,
 						"Network profile TOD policy update: %d -> %d",
@@ -5941,27 +6023,34 @@
 
 	val = get_param(cmd, "FT_DS");
 	if (val) {
+		int sta_ft_ds;
+
 		if (strcasecmp(val, "Enable") == 0) {
-			dut->sta_ft_ds = 1;
+			sta_ft_ds = 1;
 		} else if (strcasecmp(val, "Disable") == 0) {
-			dut->sta_ft_ds = 0;
+			sta_ft_ds = 0;
 		} else {
 			send_resp(dut, conn, SIGMA_ERROR,
 				  "errorCode,Unsupported value for FT_DS");
 			return STATUS_SENT_ERROR;
 		}
-		if (get_driver_type(dut) == DRIVER_WCN &&
+
+		if (dut->sta_ft_ds != sta_ft_ds &&
+		    get_driver_type(dut) == DRIVER_WCN &&
 		    sta_config_params(dut, intf, STA_SET_FT_DS,
-				      dut->sta_ft_ds) != 0) {
+				      sta_ft_ds) != 0) {
 			send_resp(dut, conn, SIGMA_ERROR,
 				  "errorCode,Failed to enable/disable FT_DS");
 			return STATUS_SENT_ERROR;
 		}
+
+		dut->sta_ft_ds = sta_ft_ds;
 	}
 
 	val = get_param(cmd, "Program");
 	if (val && (strcasecmp(val, "HS2-R2") == 0 ||
-		    strcasecmp(val, "HS2-R3") == 0))
+		    strcasecmp(val, "HS2-R3") == 0 ||
+		    strcasecmp(val, "HS2-R4") == 0))
 		return cmd_sta_preset_testparameters_hs2_r2(dut, conn, intf,
 							    cmd);
 
@@ -6410,6 +6499,35 @@
 	if (val)
 		dut->dscp_reject_resp_code = atoi(val);
 
+	val = get_param(cmd, "Deauth_Reconnect_Policy");
+	if (val) {
+		char buf[35];
+		int len;
+
+		if (strcasecmp(val, "0") == 0) {
+			len = snprintf(buf, sizeof(buf),
+				       "STA_AUTOCONNECT %d",
+				       dut->autoconnect_default);
+		} else if (strcasecmp(val, "1") == 0) {
+			len = snprintf(buf, sizeof(buf),
+				       "STA_AUTOCONNECT 0");
+		} else if (strcasecmp(val, "2") == 0) {
+			len = snprintf(buf, sizeof(buf),
+				       "STA_AUTOCONNECT 1");
+		} else {
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"Invalid Deauth_Reconnect_Policy");
+			return INVALID_SEND_STATUS;
+		}
+
+		if (len < 0 || len >= sizeof(buf) ||
+		    wpa_command(intf, buf) != 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "ErrorCode,Failed to update Deauth_Reconnect_Policy");
+			return STATUS_SENT_ERROR;
+		}
+	}
+
 	return 1;
 }
 
@@ -8732,6 +8850,136 @@
 }
 
 
+int wcn_set_he_gi(struct sigma_dut *dut, const char *intf, u8 gi_val)
+{
+ #ifdef NL80211_SUPPORT
+	struct nlattr *attr;
+	struct nlattr *attr1;
+	int ifindex, ret;
+	struct nl_msg *msg;
+
+	ifindex = if_nametoindex(intf);
+	if (ifindex == 0) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: Index for interface %s failed",
+				__func__, intf);
+		return -1;
+	}
+
+	if (!(msg = nl80211_drv_msg(dut, dut->nl_ctx, ifindex, 0,
+				    NL80211_CMD_SET_TX_BITRATE_MASK)) ||
+	    !(attr = nla_nest_start(msg, NL80211_ATTR_TX_RATES))) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: NL80211_CMD_SET_TX_BITRATE_MASK msg failed",
+				__func__);
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "%s: Setting HE GI %d",
+			__func__, gi_val);
+
+	attr1 = nla_nest_start(msg, NL80211_BAND_2GHZ);
+	if (!attr1) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: Netlink nest start failed for NL80211_BAND_2GHZ",
+				__func__);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_put_u8(msg, NL80211_TXRATE_HE_GI, gi_val);
+	nla_nest_end(msg, attr1);
+
+	attr1 = nla_nest_start(msg, NL80211_BAND_5GHZ);
+	if (!attr1) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: Netlink nest start failed for NL80211_BAND_5GHZ",
+				__func__);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_put_u8(msg, NL80211_TXRATE_HE_GI, gi_val);
+	nla_nest_end(msg, attr1);
+
+	nla_nest_end(msg, attr);
+	ret = send_and_recv_msgs(dut, dut->nl_ctx, msg, NULL, NULL);
+	if (ret) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: send_and_recv_msgs failed, ret=%d",
+				__func__, ret);
+	}
+	return ret;
+#else /* NL80211_SUPPORT */
+	return -1;
+#endif /* NL80211_SUPPORT */
+}
+
+
+static int sta_set_vht_gi(struct sigma_dut *dut, const char *intf, u8 gi_val)
+{
+ #ifdef NL80211_SUPPORT
+	struct nlattr *attr;
+	struct nlattr *attr1;
+	int ifindex, ret;
+	struct nl_msg *msg;
+
+	ifindex = if_nametoindex(intf);
+	if (ifindex == 0) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: Index for interface %s failed",
+				__func__, intf);
+		return -1;
+	}
+
+	if (!(msg = nl80211_drv_msg(dut, dut->nl_ctx, ifindex, 0,
+				    NL80211_CMD_SET_TX_BITRATE_MASK)) ||
+	    !(attr = nla_nest_start(msg, NL80211_ATTR_TX_RATES))) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: NL80211_CMD_SET_TX_BITRATE_MASK msg failed",
+				__func__);
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "%s: Setting VHT GI %d",
+			__func__, gi_val);
+
+	attr1 = nla_nest_start(msg, NL80211_BAND_2GHZ);
+	if (!attr1) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: Netlink nest start failed for NL80211_BAND_2GHZ",
+				__func__);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_put_u8(msg, NL80211_TXRATE_GI, gi_val);
+	nla_nest_end(msg, attr1);
+
+	attr1 = nla_nest_start(msg, NL80211_BAND_5GHZ);
+	if (!attr1) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: Netlink nest start failed for NL80211_BAND_5GHZ",
+				__func__);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_put_u8(msg, NL80211_TXRATE_GI, gi_val);
+	nla_nest_end(msg, attr1);
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(dut, dut->nl_ctx, msg, NULL, NULL);
+	if (ret) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"%s: send_and_recv_msgs failed, ret=%d",
+				__func__, ret);
+	}
+	return ret;
+#else /* NL80211_SUPPORT */
+	return -1;
+#endif /* NL80211_SUPPORT */
+}
+
+
 static void sta_reset_default_wcn(struct sigma_dut *dut, const char *intf,
 				  const char *type)
 {
@@ -8772,6 +9020,8 @@
 		sta_set_scan_unicast_probe(dut, intf, 0);
 
 #ifdef NL80211_SUPPORT
+		nl80211_close_event_sock(dut);
+
 		/* Reset the device HE capabilities to its default supported
 		 * configuration. */
 		sta_set_he_testbed_def(dut, intf, 0);
@@ -9027,6 +9277,11 @@
 	char buf[100];
 	int ret;
 
+#ifdef ANDROID
+	kill_pid(dut, concat_sigma_tmpdir(dut, "/sigma_dut-dnsmasq.pid",
+					  buf, sizeof(buf)));
+#endif /* ANDROID */
+
 	if (dut->station_ifname_2g &&
 	    strcmp(dut->station_ifname_2g, intf) == 0)
 		dut->use_5g = 0;
@@ -9087,7 +9342,7 @@
 	    lowi_cmd_sta_reset_default(dut, conn, cmd) < 0)
 		return ERROR_SEND_STATUS;
 
-	if (dut->program == PROGRAM_HS2_R2 || dut->program == PROGRAM_HS2_R3) {
+	if (is_passpoint_r2_or_newer(dut->program)) {
 		unlink("SP/wi-fi.org/pps.xml");
 		if (system("rm -r SP/*") != 0) {
 		}
@@ -9189,14 +9444,12 @@
 
 	set_ps(intf, dut, 0);
 
-	if (dut->program == PROGRAM_HS2 || dut->program == PROGRAM_HS2_R2 ||
-	    dut->program == PROGRAM_HS2_R3) {
+	if (is_passpoint(dut->program)) {
 		wpa_command(intf, "SET interworking 1");
 		wpa_command(intf, "SET hs20 1");
 	}
 
-	if (dut->program == PROGRAM_HS2_R2 ||
-	    dut->program == PROGRAM_HS2_R3 ||
+	if (is_passpoint_r2_or_newer(dut->program) ||
 	    dut->program == PROGRAM_OCE) {
 		wpa_command(intf, "SET pmf 1");
 	} else {
@@ -9250,6 +9503,12 @@
 	dut->dpp_local_bootstrap = -1;
 	wpa_command(intf, "SET dpp_config_processing 2");
 	wpa_command(intf, "SET dpp_mud_url ");
+	wpa_command(intf, "SET dpp_extra_conf_req_name ");
+	wpa_command(intf, "SET dpp_extra_conf_req_value ");
+	wpa_command(intf, "SET dpp_connector_privacy_default 0");
+	dpp_mdns_stop(dut);
+	unlink("/tmp/dpp-rest-server.uri");
+	unlink("/tmp/dpp-rest-server.id");
 
 	wpa_command(intf, "VENDOR_ELEM_REMOVE 13 *");
 
@@ -9328,6 +9587,11 @@
 	dut->prev_disable_scs_support = 0;
 	dut->prev_disable_mscs_support = 0;
 
+	if (dut->autoconnect_default)
+		wpa_command(intf, "STA_AUTOCONNECT 1");
+	else
+		wpa_command(intf, "STA_AUTOCONNECT 0");
+
 	if (dut->program != PROGRAM_VHT)
 		return cmd_sta_p2p_reset(dut, conn, cmd);
 
@@ -9672,11 +9936,12 @@
 
 static int twt_async_event_wait(struct sigma_dut *dut, unsigned int twt_op)
 {
-	struct nl_cb *cb;
+	struct nl_cb *cb = NULL;
 	int err_code = 0, select_retval = 0;
 	struct wait_event wait_info;
 
-	cb = nl_socket_get_cb(dut->nl_ctx->event_sock);
+	if (dut->nl_ctx->event_sock)
+		cb = nl_socket_get_cb(dut->nl_ctx->event_sock);
 	if (!cb) {
 		sigma_dut_print(dut, DUT_MSG_ERROR,
 				"event callback not found");
@@ -10289,9 +10554,18 @@
 	val = get_param(cmd, "SGI80");
 	if (val) {
 		int sgi80;
+		enum nl80211_txrate_gi gi_val;
 
 		sgi80 = strcmp(val, "1") == 0 || strcasecmp(val, "Enable") == 0;
-		run_iwpriv(dut, intf, "shortgi %d", sgi80);
+		if (sgi80)
+			gi_val = NL80211_TXRATE_FORCE_LGI;
+		else
+			gi_val = NL80211_TXRATE_FORCE_SGI;
+		if (sta_set_vht_gi(dut, intf, (u8) gi_val)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"sta_set_vht_gi failed, using iwpriv");
+			run_iwpriv(dut, intf, "shortgi %d", sgi80);
+		}
 	}
 
 	val = get_param(cmd, "TxBF");
@@ -11011,6 +11285,20 @@
 		return STATUS_SENT_ERROR;
 	}
 
+	val = get_param(cmd, "GKH_G2_Tx");
+	if (val) {
+		char buf[50];
+
+		snprintf(buf, sizeof(buf), "SET disable_eapol_g2_tx %d",
+			 strcasecmp(val, "disable") == 0);
+
+		if (wpa_command(intf, buf) < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Failed to enable/disable G2 transmit");
+			return STATUS_SENT_ERROR;
+		}
+	}
+
 	return cmd_sta_set_wireless_common(intf, dut, conn, cmd);
 }
 
@@ -11022,6 +11310,8 @@
 	const char *val;
 
 	val = get_param(cmd, "Program");
+	if (!val)
+		val = get_param(cmd, "Prog");
 	if (val) {
 		if (strcasecmp(val, "11n") == 0)
 			return cmd_sta_set_11n(dut, conn, cmd);
@@ -13784,7 +14074,8 @@
 		return cmd_sta_send_frame_tdls(dut, conn, cmd);
 	if (val && (strcasecmp(val, "HS2") == 0 ||
 		    strcasecmp(val, "HS2-R2") == 0 ||
-		    strcasecmp(val, "HS2-R3") == 0))
+		    strcasecmp(val, "HS2-R3") == 0 ||
+		    strcasecmp(val, "HS2-R4") == 0))
 		return cmd_sta_send_frame_hs2(dut, conn, cmd);
 	if (val && strcasecmp(val, "VHT") == 0)
 		return cmd_sta_send_frame_vht(dut, conn, cmd);
@@ -13994,7 +14285,8 @@
 	val = get_param(cmd, "program");
 	if (val && (strcasecmp(val, "HS2") == 0 ||
 		    strcasecmp(val, "HS2-R2") == 0 ||
-		    strcasecmp(val, "HS2-R3") == 0))
+		    strcasecmp(val, "HS2-R3") == 0 ||
+		    strcasecmp(val, "HS2-R4") == 0))
 		return cmd_sta_set_parameter_hs2(dut, conn, cmd, intf);
 
 	return -1;
@@ -14511,34 +14803,42 @@
 	val = get_param(cmd, "GI");
 	if (val) {
 		int fix_rate_sgi;
+		u8 he_gi_val = 0;
 
 		if (strcmp(val, "0.8") == 0) {
 			snprintf(buf, sizeof(buf), "iwpriv %s shortgi 9", intf);
 			fix_rate_sgi = 1;
+			he_gi_val = NL80211_RATE_INFO_HE_GI_0_8;
 		} else if (strcmp(val, "1.6") == 0) {
 			snprintf(buf, sizeof(buf), "iwpriv %s shortgi 10",
 				 intf);
 			fix_rate_sgi = 2;
+			he_gi_val = NL80211_RATE_INFO_HE_GI_1_6;
 		} else if (strcmp(val, "3.2") == 0) {
 			snprintf(buf, sizeof(buf), "iwpriv %s shortgi 11",
 				 intf);
 			fix_rate_sgi = 3;
+			he_gi_val = NL80211_RATE_INFO_HE_GI_3_2;
 		} else {
 			send_resp(dut, conn, SIGMA_ERROR,
 				  "errorCode,GI value not supported");
 			return STATUS_SENT_ERROR;
 		}
-		if (system(buf) != 0) {
-			send_resp(dut, conn, SIGMA_ERROR,
-				  "errorCode,Failed to set shortgi");
-			return STATUS_SENT_ERROR;
-		}
-		snprintf(buf, sizeof(buf), "iwpriv %s shortgi %d",
-				intf, fix_rate_sgi);
-		if (system(buf) != 0) {
-			send_resp(dut, conn, SIGMA_ERROR,
-				  "errorCode,Failed to set fix rate shortgi");
-			return STATUS_SENT_ERROR;
+		if (wcn_set_he_gi(dut, intf, he_gi_val)) {
+			sigma_dut_print(dut, DUT_MSG_INFO,
+					"wcn_set_he_gi failed, using iwpriv");
+			if (system(buf) != 0) {
+				send_resp(dut, conn, SIGMA_ERROR,
+						"errorCode,Failed to set shortgi");
+				return STATUS_SENT_ERROR;
+			}
+			snprintf(buf, sizeof(buf), "iwpriv %s shortgi %d",
+					intf, fix_rate_sgi);
+			if (system(buf) != 0) {
+				send_resp(dut, conn, SIGMA_ERROR,
+						"errorCode,Failed to set fix rate shortgi");
+				return STATUS_SENT_ERROR;
+			}
 		}
 	}
 
@@ -14613,6 +14913,11 @@
 
 	val = get_param(cmd, "TWT_Setup");
 	if (val) {
+#ifdef NL80211_SUPPORT
+		if (dut->sta_async_twt_supp && nl80211_open_event_sock(dut))
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"Failed to open nl80211 event socket");
+#endif /* NL80211_SUPPORT */
 		if (strcasecmp(val, "Request") == 0) {
 			if (set_power_save_wcn(dut, intf, 1) < 0)
 				sigma_dut_print(dut, DUT_MSG_ERROR,
@@ -14633,6 +14938,11 @@
 
 	val = get_param(cmd, "TWT_Operation");
 	if (val) {
+#ifdef NL80211_SUPPORT
+		if (dut->sta_async_twt_supp && nl80211_open_event_sock(dut))
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"Failed to open nl80211 event socket");
+#endif /* NL80211_SUPPORT */
 		if (strcasecmp(val, "Suspend") == 0) {
 			if (sta_twt_suspend_or_nudge(dut, conn, cmd)) {
 				send_resp(dut, conn, SIGMA_ERROR,
@@ -15624,6 +15934,7 @@
 	int info_avail = 0;
 	unsigned int old_timeout;
 	int res;
+	const char *events[] = { "RX-VENUE-URL", "ANQP-QUERY-DONE", NULL };
 
 	if (get_wpa_status(intf, "bssid", bssid, sizeof(bssid)) < 0) {
 		send_resp(dut, conn, SIGMA_ERROR,
@@ -15649,18 +15960,32 @@
 
 	old_timeout = dut->default_timeout;
 	dut->default_timeout = 2;
-	res = get_wpa_cli_event(dut, ctrl, "RX-VENUE-URL", buf, sizeof(buf));
+	for (;;) {
+		res = get_wpa_cli_events(dut, ctrl, events, buf, sizeof(buf));
+		if (res < 0)
+			break;
+		if (strstr(buf, "ANQP-QUERY-DONE") != NULL) {
+			res = -1;
+			break;
+		}
+		pos = strchr(buf, ' ');
+		if (!pos)
+			continue;
+		pos++;
+		pos = strchr(pos, ' ');
+		if (!pos)
+			continue;
+		pos++;
+
+		if (strncmp(pos, "https://", 8) == 0)
+			break;
+
+		sigma_dut_print(dut, DUT_MSG_DEBUG,
+				"Ignore non-HTTPS venue URL: %s", pos);
+	}
 	dut->default_timeout = old_timeout;
 	if (res < 0)
 		goto done;
-	pos = strchr(buf, ' ');
-	if (!pos)
-		goto done;
-	pos++;
-	pos = strchr(pos, ' ');
-	if (!pos)
-		goto done;
-	pos++;
 	info_avail = 1;
 	snprintf(params, sizeof(params), "browser %s", pos);
 
@@ -15888,7 +16213,7 @@
 		return 0;
 	}
 
-	if (dut->program == PROGRAM_HS2_R2 || dut->program == PROGRAM_HS2_R3) {
+	if (is_passpoint_r2_or_newer(dut->program)) {
 		/*
 		 * Set provisioning_sp for the test cases where SIM/USIM
 		 * provisioning is used.
diff --git a/traffic.c b/traffic.c
index ee2dbc0..c6c5968 100644
--- a/traffic.c
+++ b/traffic.c
@@ -41,6 +41,7 @@
 	int dscp = 0, use_dscp = 0;
 	char extra[100], int_arg[100], intf_arg[100], ip_dst[100], ping[100];
 	struct in6_addr ip6_addr;
+	bool broadcast = false;
 
 	val = get_param(cmd, "Type");
 	if (!val)
@@ -78,6 +79,14 @@
 	if (val == NULL)
 		return INVALID_SEND_STATUS;
 	size = atoi(val);
+	if (type != 2 && strcmp(dst, BROADCAST_ADDR) == 0) {
+		if (size > 1472) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "ErrorCode,Unsupported broadcast ping frame size");
+			return STATUS_SENT;
+		}
+		broadcast = true;
+	}
 
 	val = get_param(cmd, "frameRate");
 	if (val == NULL)
@@ -141,10 +150,11 @@
 	else
 		intf_arg[0] = '\0';
 	fprintf(f, "#!" SHELL "\n"
-		"ping%s -c %d%s -s %d%s -q%s %s > %s"
+		"ping%s%s -c %d%s -s %d%s -q%s %s > %s"
 		"/sigma_dut-ping.%d &\n"
 		"echo $! > %s/sigma_dut-ping-pid.%d\n",
-		type == 2 ? "6" : "", pkts, int_arg, size, extra,
+		type == 2 ? "6" : "", broadcast ? " -b" : "",
+		pkts, int_arg, size, extra,
 		intf_arg, dst, dut->sigma_tmpdir, id, dut->sigma_tmpdir, id);
 
 	fclose(f);
@@ -259,7 +269,7 @@
 }
 
 
-static int get_ip_addr(const char *ifname, int ipv6, char *buf, size_t len)
+int get_ip_addr(const char *ifname, int ipv6, char *buf, size_t len)
 {
 	struct ifaddrs *ifa, *ifa_tmp;
 	bool non_ll_addr_found = false;
diff --git a/utils.c b/utils.c
index cb69a99..21f5cc3 100644
--- a/utils.c
+++ b/utils.c
@@ -10,6 +10,7 @@
 #include "sigma_dut.h"
 #include <sys/ioctl.h>
 #include <sys/stat.h>
+#include <signal.h>
 #include "wpa_helpers.h"
 
 enum driver_type wifi_chip_type = DRIVER_NOT_SET;
@@ -140,6 +141,10 @@
 		return PROGRAM_HS2_R2;
 	if (strcasecmp(prog, "HS2-R3") == 0)
 		return PROGRAM_HS2_R3;
+	if (strcasecmp(prog, "HS2-2022") == 0)
+		return PROGRAM_HS2_2022;
+	if (strcasecmp(prog, "HS2-R4") == 0)
+		return PROGRAM_HS2_R4;
 	if (strcasecmp(prog, "WFD") == 0)
 		return PROGRAM_WFD;
 	if (strcasecmp(prog, "DisplayR2") == 0)
@@ -177,6 +182,22 @@
 }
 
 
+bool is_passpoint_r2_or_newer(enum sigma_program prog)
+{
+	return prog == PROGRAM_HS2_R2 ||
+		prog == PROGRAM_HS2_R3 ||
+		prog == PROGRAM_HS2_2022 ||
+		prog == PROGRAM_HS2_R4;
+}
+
+
+bool is_passpoint(enum sigma_program prog)
+{
+	return prog == PROGRAM_HS2 ||
+		is_passpoint_r2_or_newer(prog);
+}
+
+
 static int parse_hex(char c)
 {
 	if (c >= '0' && c <= '9')
@@ -616,8 +637,6 @@
 struct nl80211_ctx * nl80211_init(struct sigma_dut *dut)
 {
 	struct nl80211_ctx *ctx;
-	struct nl_cb *cb = NULL;
-	int ret;
 
 	ctx = calloc(1, sizeof(struct nl80211_ctx));
 	if (!ctx) {
@@ -662,19 +681,42 @@
 		goto cleanup;
 	}
 
+	return ctx;
+
+cleanup:
+	if (ctx->sock)
+		nl_socket_free(ctx->sock);
+
+	free(ctx);
+	return NULL;
+}
+
+
+int nl80211_open_event_sock(struct sigma_dut *dut)
+{
+	struct nl_cb *cb = NULL;
+	int ret;
+	struct nl80211_ctx *ctx = dut->nl_ctx;
+
+	if (!ctx) {
+		sigma_dut_print(dut, DUT_MSG_ERROR, "nl80211 context is NULL");
+		return -1;
+	}
+
+	nl80211_close_event_sock(dut);
 	ctx->event_sock = nl_socket_alloc();
 	if (!ctx->event_sock) {
 		sigma_dut_print(dut, DUT_MSG_ERROR,
 				"Failed to create NL event socket, err: %s",
 				strerror(errno));
-		goto cleanup;
+		return -1;
 	}
 
 	if (nl_connect(ctx->event_sock, NETLINK_GENERIC)) {
 		sigma_dut_print(dut, DUT_MSG_ERROR,
 				"Could not connect event socket, err: %s",
 				strerror(errno));
-		goto cleanup;
+		return -1;
 	}
 
 	if (nl_socket_set_buffer_size(ctx->event_sock, SOCK_BUF_SIZE, 0) < 0) {
@@ -687,7 +729,7 @@
 	if (!cb) {
 		sigma_dut_print(dut, DUT_MSG_INFO,
 				"Failed to get NL control block for event socket port");
-		goto cleanup;
+		return -1;
 	}
 
 	ret = nl_get_multicast_id(dut, ctx, "nl80211", "vendor");
@@ -706,16 +748,7 @@
 	nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
 	nl_cb_put(cb);
 
-	return ctx;
-
-cleanup:
-	if (ctx->sock)
-		nl_socket_free(ctx->sock);
-	if (ctx->event_sock)
-		nl_socket_free(ctx->event_sock);
-
-	free(ctx);
-	return NULL;
+	return 0;
 }
 
 
@@ -734,6 +767,17 @@
 }
 
 
+void nl80211_close_event_sock(struct sigma_dut *dut)
+{
+	struct nl80211_ctx *ctx = dut->nl_ctx;
+
+	if (ctx && ctx->event_sock) {
+		nl_socket_free(ctx->event_sock);
+		ctx->event_sock = NULL;
+	}
+}
+
+
 static struct nl_msg *
 wcn_create_wifi_test_config_msg(struct sigma_dut *dut, const char *intf)
 {
@@ -960,6 +1004,74 @@
 }
 
 
+unsigned char * base64_decode(const char *src, size_t len, size_t *out_len)
+{
+	unsigned char dtable[256], *out, *pos, block[4], tmp;
+	size_t i, count, olen;
+	int pad = 0;
+	size_t extra_pad;
+
+	memset(dtable, 0x80, 256);
+	for (i = 0; i < sizeof(base64_table) - 1; i++)
+		dtable[(unsigned char) base64_table[i]] = (unsigned char) i;
+	dtable['='] = 0;
+
+	count = 0;
+	for (i = 0; i < len; i++) {
+		if (dtable[(unsigned char) src[i]] != 0x80)
+			count++;
+	}
+
+	if (count == 0)
+		return NULL;
+	extra_pad = (4 - count % 4) % 4;
+
+	olen = (count + extra_pad) / 4 * 3;
+	pos = out = malloc(olen);
+	if (!out)
+		return NULL;
+
+	count = 0;
+	for (i = 0; i < len + extra_pad; i++) {
+		unsigned char val;
+
+		if (i >= len)
+			val = '=';
+		else
+			val = src[i];
+		tmp = dtable[val];
+		if (tmp == 0x80)
+			continue;
+
+		if (val == '=')
+			pad++;
+		block[count] = tmp;
+		count++;
+		if (count == 4) {
+			*pos++ = (block[0] << 2) | (block[1] >> 4);
+			*pos++ = (block[1] << 4) | (block[2] >> 2);
+			*pos++ = (block[2] << 6) | block[3];
+			count = 0;
+			if (pad) {
+				if (pad == 1)
+					pos--;
+				else if (pad == 2)
+					pos -= 2;
+				else {
+					/* Invalid padding */
+					free(out);
+					return NULL;
+				}
+				break;
+			}
+		}
+	}
+
+	*out_len = pos - out;
+	return out;
+}
+
+
 int random_get_bytes(char *buf, size_t len)
 {
 	FILE *f;
@@ -1043,3 +1155,32 @@
 {
 	return res < 0 || (unsigned int) res >= size;
 }
+
+
+void kill_pid(struct sigma_dut *dut, const char *pid_file)
+{
+	int pid;
+	FILE *f;
+
+	f = fopen(pid_file, "r");
+	if (!f)
+		return; /* process is not running */
+
+	if (fscanf(f, "%d", &pid) != 1 || pid <= 0) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"No PID for process in %s", pid_file);
+		fclose(f);
+		unlink(pid_file);
+		return;
+	}
+	fclose(f);
+
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "Process PID found in %s: %d",
+			pid_file, pid);
+	if (kill(pid, SIGINT) < 0 && errno != ESRCH)
+		sigma_dut_print(dut, DUT_MSG_DEBUG, "kill failed: %s",
+				strerror(errno));
+
+	unlink(pid_file);
+	sleep(1);
+}