[RFC PATCH 2/6] NAN: Discover Services and Peers from Proxy Meta Attributes

Benjamin Berg benjamin at sipsolutions.net
Thu Feb 19 07:55:49 PST 2026


From: Benjamin Berg <benjamin.berg at intel.com>

Services may be published by another peer using a Proxy Meta attribute.
Find these attributes and handle them, also inserting the corresponding
peer as necessary.

Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
---
 src/ap/nan_usd_ap.c             | 15 +++++++++----
 src/common/nan_de.c             | 22 +++++++++++++-------
 src/common/nan_de.h             |  9 ++++++--
 src/common/nan_defs.h           |  1 +
 wpa_supplicant/dbus/dbus_new.c  | 11 +++++++++-
 wpa_supplicant/dbus/dbus_new.h  |  6 ++++--
 wpa_supplicant/nan_supplicant.c | 37 ++++++++++++++++++++++++++++++---
 wpa_supplicant/notify.c         | 15 +++++++++----
 wpa_supplicant/notify.h         |  3 ++-
 9 files changed, 95 insertions(+), 24 deletions(-)

diff --git a/src/ap/nan_usd_ap.c b/src/ap/nan_usd_ap.c
index 18c70be485..5579e7198a 100644
--- a/src/ap/nan_usd_ap.c
+++ b/src/ap/nan_usd_ap.c
@@ -48,9 +48,10 @@ hostapd_nan_de_discovery_result(void *ctx, int subscribe_id,
 				enum nan_service_protocol_type srv_proto_type,
 				const u8 *ssi, size_t ssi_len,
 				int peer_publish_id, const u8 *peer_addr,
-				bool fsd, bool fsd_gas)
+				bool fsd, bool fsd_gas, const u8 *origin_addr)
 {
 	struct hostapd_data *hapd = ctx;
+	char proxied_by[32] = "";
 	char *ssi_hex;
 
 	ssi_hex = os_zalloc(2 * ssi_len + 1);
@@ -58,11 +59,17 @@ hostapd_nan_de_discovery_result(void *ctx, int subscribe_id,
 		return;
 	if (ssi)
 		wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
+
+	if (origin_addr)
+		os_snprintf(proxied_by, sizeof(proxied_by) - 1,
+			    " proxied_by=" MACSTR, MAC2STR(origin_addr));
+
 	wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_DISCOVERY_RESULT
 		"subscribe_id=%d publish_id=%d address=" MACSTR
-		" fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s",
+		" fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s%s",
 		subscribe_id, peer_publish_id, MAC2STR(peer_addr),
-		fsd, fsd_gas, srv_proto_type, ssi_hex);
+		fsd, fsd_gas, srv_proto_type, ssi_hex,
+		proxied_by);
 	os_free(ssi_hex);
 }
 
@@ -180,7 +187,7 @@ void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
 {
 	if (!hapd->nan_de)
 		return;
-	nan_de_rx_sdf(hapd->nan_de, src, a3, freq, buf, len, 0);
+	nan_de_rx_sdf(hapd->nan_de, src, a3, freq, buf, len, 0, NULL);
 }
 
 
diff --git a/src/common/nan_de.c b/src/common/nan_de.c
index 605968a2a3..95d6c5621b 100644
--- a/src/common/nan_de.c
+++ b/src/common/nan_de.c
@@ -942,7 +942,7 @@ void nan_de_tx_wait_ended(struct nan_de *de)
 }
 
 
-static const u8 *
+const u8 *
 nan_de_get_attr(const u8 *buf, size_t len, enum nan_attr_id id,
 		unsigned int skip)
 {
@@ -1173,7 +1173,8 @@ static void nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv,
 			      u8 req_instance_id, u16 sdea_control,
 			      enum nan_service_protocol_type srv_proto_type,
 			      const u8 *ssi, size_t ssi_len,
-			      bool range_limit, int rssi)
+			      bool range_limit, int rssi,
+			      const u8 *origin_addr)
 {
 	if (!nan_de_filter_match(srv, matching_filter, matching_filter_len))
 		return;
@@ -1220,7 +1221,8 @@ send_event:
 			ssi, ssi_len, instance_id,
 			peer_addr,
 			sdea_control & NAN_SDEA_CTRL_FSD_REQ,
-			sdea_control & NAN_SDEA_CTRL_FSD_GAS);
+			sdea_control & NAN_SDEA_CTRL_FSD_GAS,
+			origin_addr);
 }
 
 
@@ -1377,7 +1379,8 @@ static bool nan_srf_match(struct nan_de *de, const u8 *srf, size_t srf_len)
 
 static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
 			  unsigned int freq, const u8 *buf, size_t len,
-			  const u8 *sda, size_t sda_len, int rssi)
+			  const u8 *sda, size_t sda_len, int rssi,
+			  const u8 *origin_addr)
 {
 	const u8 *service_id;
 	u8 instance_id, req_instance_id, ctrl;
@@ -1508,6 +1511,10 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
 						      srv->is_pr);
 		}
 
+		/* Ignore everything other than publish for proxied SDAs */
+		if (origin_addr && type != NAN_SRV_CTRL_PUBLISH)
+			break;
+
 		switch (type) {
 		case NAN_SRV_CTRL_PUBLISH:
 			nan_de_rx_publish(de, srv, peer_addr, a3, instance_id,
@@ -1518,7 +1525,7 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
 					  ssi, ssi_len,
 					  ctrl &
 					  NAN_SRV_CTRL_DISCOVERY_RANGE_LIMITED,
-					  rssi);
+					  rssi, origin_addr);
 			break;
 		case NAN_SRV_CTRL_SUBSCRIBE:
 			nan_de_rx_subscribe(de, srv, peer_addr, a3, instance_id,
@@ -1540,7 +1547,8 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
 
 
 void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
-		   unsigned int freq, const u8 *buf, size_t len, int rssi)
+		   unsigned int freq, const u8 *buf, size_t len, int rssi,
+		   const u8 *origin_addr)
 {
 	const u8 *sda;
 	u16 sda_len;
@@ -1564,7 +1572,7 @@ void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
 		sda_len = WPA_GET_LE16(sda);
 		sda += 2;
 		nan_de_rx_sda(de, peer_addr, a3, freq, buf, len, sda, sda_len,
-			      rssi);
+			      rssi, origin_addr);
 	}
 }
 
diff --git a/src/common/nan_de.h b/src/common/nan_de.h
index c842b90b3c..b14b22b4a0 100644
--- a/src/common/nan_de.h
+++ b/src/common/nan_de.h
@@ -37,7 +37,8 @@ struct nan_callbacks {
 				 enum nan_service_protocol_type srv_proto_type,
 				 const u8 *ssi, size_t ssi_len,
 				 int peer_publish_id,
-				 const u8 *peer_addr, bool fsd, bool fsd_gas);
+				 const u8 *peer_addr, bool fsd, bool fsd_gas,
+				 const u8 *origin_addr);
 
 	void (*replied)(void *ctx, int publish_id, const u8 *peer_addr,
 			int peer_subscribe_id,
@@ -66,6 +67,9 @@ struct nan_callbacks {
 				     unsigned int freq);
 };
 
+const u8 * nan_de_get_attr(const u8 *buf, size_t len, enum nan_attr_id id,
+			   unsigned int skip);
+
 bool nan_de_is_nan_network_id(const u8 *addr);
 bool nan_de_is_p2p_network_id(const u8 *addr);
 struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap,
@@ -82,7 +86,8 @@ void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst);
 void nan_de_tx_wait_ended(struct nan_de *de);
 
 void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
-		   unsigned int freq, const u8 *buf, size_t len, int rssi);
+		   unsigned int freq, const u8 *buf, size_t len, int rssi,
+		   const u8 *origin_addr);
 const u8 * nan_de_get_service_id(struct nan_de *de, int id);
 
 struct nan_publish_params {
diff --git a/src/common/nan_defs.h b/src/common/nan_defs.h
index e8428c2acf..472848a3c4 100644
--- a/src/common/nan_defs.h
+++ b/src/common/nan_defs.h
@@ -51,6 +51,7 @@ enum nan_attr_id {
 	NAN_ATTR_BPBA = 0x2C, /* NAN Pairing Bootstrapping attribute */
 	NAN_ATTR_S3 = 0x2D,
 	NAN_ATTR_TPEA = 0x2E, /* Transmit Power Envelope attribute */
+	NAN_ATTR_PROXY_META = 0x53,
 	NAN_ATTR_VENDOR_SPECIFIC = 0xDD,
 };
 
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 2989002e4a..22e481126f 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -5482,6 +5482,7 @@ void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
  * @peer_addr: MAC address of the peer device
  * @ssi: Service specific information payload
  * @ssi_len: Length of the SSI field
+ * @origin_addr: If proxied, the address of the reporter
  *
  * This is used to indicate the NAN DE DiscoveryResult event.
  */
@@ -5492,12 +5493,14 @@ void wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s,
 					   int peer_publish_id,
 					   const u8 *peer_addr,
 					   bool fsd, bool fsd_gas,
-					   const u8 *ssi, size_t ssi_len)
+					   const u8 *ssi, size_t ssi_len,
+					   const u8 *origin_addr)
 {
 	struct wpas_dbus_priv *iface;
 	DBusMessage *msg;
 	DBusMessageIter iter, dict_iter;
 	char addr_str[20];
+	char origin_addr_str[20];
 
 	iface = wpa_s->global->dbus;
 	/* Do nothing if the interface is not turned on */
@@ -5510,6 +5513,9 @@ void wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s,
 		return;
 
 	snprintf(addr_str, sizeof(addr_str), MACSTR, MAC2STR(peer_addr));
+	if (origin_addr)
+		snprintf(origin_addr_str, sizeof(origin_addr_str),
+			 MACSTR, MAC2STR(origin_addr));
 
 	dbus_message_iter_init_append(msg, &iter);
 
@@ -5528,6 +5534,9 @@ void wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s,
 					      "ssi",
 					      (const char *) ssi,
 					      ssi_len)) ||
+	    (origin_addr &&
+	     !wpa_dbus_dict_append_string(&dict_iter, "proxied_by",
+					  origin_addr_str)) ||
 	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
 		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 	else
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 92b0c70b4e..18efdf8fc4 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -299,7 +299,8 @@ void wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s,
 					   int peer_publish_id,
 					   const u8 *peer_addr,
 					   bool fsd, bool fsd_gas,
-					   const u8 *ssi, size_t ssi_len);
+					   const u8 *ssi, size_t ssi_len,
+					   const u8 *origin_addr);
 void wpas_dbus_signal_nan_replied(struct wpa_supplicant *wpa_s,
 				  enum nan_service_protocol_type srv_proto_type,
 				  int publish_id, int peer_subscribe_id,
@@ -722,7 +723,8 @@ wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s,
 				      int subscribe_id,
 				      int peer_publish_id, const u8 *peer_addr,
 				      bool fsd, bool fsd_gas,
-				      const u8 *ssi, size_t ssi_len)
+				      const u8 *ssi, size_t ssi_len,
+				      const u8 *origin_addr)
 {
 }
 
diff --git a/wpa_supplicant/nan_supplicant.c b/wpa_supplicant/nan_supplicant.c
index 70953eccc7..a42c158425 100644
--- a/wpa_supplicant/nan_supplicant.c
+++ b/wpa_supplicant/nan_supplicant.c
@@ -543,13 +543,14 @@ static void
 wpas_nan_de_discovery_result(void *ctx, int subscribe_id,
 			     enum nan_service_protocol_type srv_proto_type,
 			     const u8 *ssi, size_t ssi_len, int peer_publish_id,
-			     const u8 *peer_addr, bool fsd, bool fsd_gas)
+			     const u8 *peer_addr, bool fsd, bool fsd_gas,
+			     const u8 *origin_addr)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
 	wpas_notify_nan_discovery_result(wpa_s, srv_proto_type, subscribe_id,
 					 peer_publish_id, peer_addr, fsd,
-					 fsd_gas, ssi, ssi_len);
+					 fsd_gas, ssi, ssi_len, origin_addr);
 }
 
 
@@ -681,9 +682,39 @@ void wpas_nan_de_rx_sdf(struct wpa_supplicant *wpa_s, const u8 *src,
 			const u8 *a3, unsigned int freq,
 			const u8 *buf, size_t len, int rssi)
 {
+	unsigned int skip;
+
 	if (!wpa_s->nan_de)
 		return;
-	nan_de_rx_sdf(wpa_s->nan_de, src, a3, freq, buf, len, rssi);
+
+	nan_de_rx_sdf(wpa_s->nan_de, src, a3, freq, buf, len, rssi, NULL);
+
+	for (skip = 0; ; skip++) {
+		const u8 *proxy_meta =
+			nan_de_get_attr(buf, len, NAN_ATTR_PROXY_META, skip);
+		const u8 *proxied_addr;
+		size_t proxy_meta_len;
+
+		if (!proxy_meta)
+			break;
+
+		proxy_meta++;
+		proxy_meta_len = WPA_GET_LE16(proxy_meta);
+		proxy_meta += 2;
+
+		/* Needs at least space for address plus SDA */
+		if (proxy_meta_len < 6 + 12)
+			continue;
+
+		proxied_addr = proxy_meta;
+		proxy_meta += 6;
+		proxy_meta_len -= 6;
+
+		nan_de_rx_sdf(wpa_s->nan_de, proxied_addr, a3, freq,
+			      proxy_meta, proxy_meta_len, rssi, src);
+
+		/* Only one SDA inside each Proxy Meta Attribute */
+	}
 }
 
 
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 36694b48c8..6171243136 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -1105,8 +1105,10 @@ void wpas_notify_nan_discovery_result(struct wpa_supplicant *wpa_s,
 				      int subscribe_id, int peer_publish_id,
 				      const u8 *peer_addr,
 				      bool fsd, bool fsd_gas,
-				      const u8 *ssi, size_t ssi_len)
+				      const u8 *ssi, size_t ssi_len,
+				      const u8 *origin_addr)
 {
+	char proxied_by[32] = "";
 	char *ssi_hex;
 
 	ssi_hex = os_zalloc(2 * ssi_len + 1);
@@ -1114,17 +1116,22 @@ void wpas_notify_nan_discovery_result(struct wpa_supplicant *wpa_s,
 		return;
 	if (ssi)
 		wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
+
+	if (origin_addr)
+		os_snprintf(proxied_by, sizeof(proxied_by) - 1,
+			    " proxied_by=" MACSTR, MAC2STR(origin_addr));
+
 	wpa_msg_global(wpa_s, MSG_INFO, NAN_DISCOVERY_RESULT
 		       "subscribe_id=%d publish_id=%d address=" MACSTR
-		       " fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s",
+		       " fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s%s",
 		       subscribe_id, peer_publish_id, MAC2STR(peer_addr),
-		       fsd, fsd_gas, srv_proto_type, ssi_hex);
+		       fsd, fsd_gas, srv_proto_type, ssi_hex, proxied_by);
 	os_free(ssi_hex);
 
 	wpas_dbus_signal_nan_discovery_result(wpa_s, srv_proto_type,
 					      subscribe_id, peer_publish_id,
 					      peer_addr, fsd, fsd_gas,
-					      ssi, ssi_len);
+					      ssi, ssi_len, origin_addr);
 }
 
 
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 79d7abd564..e7484f11a6 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -190,7 +190,8 @@ void wpas_notify_nan_discovery_result(struct wpa_supplicant *wpa_s,
 				      int subscribe_id, int peer_publish_id,
 				      const u8 *peer_addr,
 				      bool fsd, bool fsd_gas,
-				      const u8 *ssi, size_t ssi_len);
+				      const u8 *ssi, size_t ssi_len,
+				      const u8 *origin_addr);
 void wpas_notify_nan_replied(struct wpa_supplicant *wpa_s,
 			     enum nan_service_protocol_type srv_proto_type,
 			     int publish_id, int peer_subscribe_id,
-- 
2.53.0




More information about the Hostap mailing list