[PATCH] Expose the Time Zone element from scan results

David Woodhouse dwmw2 at infradead.org
Wed Oct 28 04:17:59 PDT 2026


An AP configured with hostapd's time_advertisement=2 advertises the
local time zone in its Beacon and Probe Response frames, as a Time Zone
element containing a string in IEEE Std 1003.1 TZ format. Nothing on
the client side ever got to see it.

Add a TimeZone property on the D-Bus BSS interface and a tz= field in
the BSS control interface command output, so that a connection manager
can offer to set the local time zone when the user connects to a given
AP.

Since consumers may use the string to configure the system time zone,
reject any element containing characters outside printable ASCII rather
than passing it on.

Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 doc/dbus.doxygen                        |  7 +++++
 src/common/wpa_ctrl.h                   |  1 +
 wpa_supplicant/bss.c                    | 35 +++++++++++++++++++++++++
 wpa_supplicant/bss.h                    |  1 +
 wpa_supplicant/ctrl_iface.c             | 11 ++++++++
 wpa_supplicant/dbus/dbus_new.c          |  5 ++++
 wpa_supplicant/dbus/dbus_new_handlers.c | 32 ++++++++++++++++++++++
 wpa_supplicant/dbus/dbus_new_handlers.h |  1 +
 8 files changed, 93 insertions(+)

diff --git a/doc/dbus.doxygen b/doc/dbus.doxygen
index fa73e8a1b..a5882a6bf 100644
--- a/doc/dbus.doxygen
+++ b/doc/dbus.doxygen
@@ -2467,6 +2467,13 @@ scan results.
 	</table>
 	<p>Unnamed ANQP elements have a generic entry name 'anqp[id]' where 'id' is the InfoID of the ANQP element as described in IEEE Std 802.11-2020, Table 9-331 (ANQP-element definitions).</p>
       </li>
+      <li>
+	<h3>TimeZone - s - (read)</h3>
+	<p>Local time zone advertised by the BSS (IEEE Std 802.11 Time Zone
+	element), as a string in IEEE Std 1003.1 TZ environment variable
+	format. Empty if the BSS does not advertise a time zone or the
+	advertised value contains characters outside printable ASCII.</p>
+      </li>
     </ul>
 
 \subsection dbus_bss_signals Signals
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index ae93e1e4e..f93a7c9b7 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -558,6 +558,7 @@ extern "C" {
 #define WPA_BSS_MASK_RNR		BIT(25)
 #define WPA_BSS_MASK_ML			BIT(26)
 #define WPA_BSS_MASK_AP_MLD_ADDR	BIT(27)
+#define WPA_BSS_MASK_TIME_ZONE		BIT(28)
 
 
 /* VENDOR_ELEM_* frame id values */
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 3f8b46e1d..727f1fb4e 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -1390,6 +1390,41 @@ const u8 * wpa_bss_get_ie_ext(const struct wpa_bss *bss, u8 ext)
 }
 
 
+/**
+ * wpa_bss_get_time_zone - Fetch the advertised time zone from a BSS entry
+ * @bss: BSS table entry
+ * @buf: Buffer for returning the time zone string
+ * @buflen: Length of the buffer in octets
+ * Returns: Length of the time zone string or -1 if not available
+ *
+ * This function fetches the Time Zone element (IEEE Std 802.11) from the
+ * Probe Response or Beacon frame IEs of the BSS entry. The returned string
+ * is in the format specified in IEEE Std 1003.1 for the TZ environment
+ * variable. Since consumers may use the string to configure the local time
+ * zone, any element containing characters outside printable ASCII is
+ * rejected rather than returned.
+ */
+int wpa_bss_get_time_zone(const struct wpa_bss *bss, char *buf, size_t buflen)
+{
+	const u8 *ie;
+	size_t len, i;
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_TIME_ZONE);
+	if (!ie || ie[1] == 0 || (size_t) ie[1] + 1 > buflen)
+		return -1;
+
+	len = ie[1];
+	for (i = 0; i < len; i++) {
+		if (ie[2 + i] < 32 || ie[2 + i] > 126)
+			return -1;
+	}
+
+	os_memcpy(buf, &ie[2], len);
+	buf[len] = '\0';
+	return len;
+}
+
+
 /**
  * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry
  * @bss: BSS table entry
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index b38b5e51a..b4817ed66 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -186,6 +186,7 @@ struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
 const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
 const u8 * wpa_bss_get_ie_beacon(const struct wpa_bss *bss, u8 ie);
 const u8 * wpa_bss_get_ie_ext(const struct wpa_bss *bss, u8 ext);
+int wpa_bss_get_time_zone(const struct wpa_bss *bss, char *buf, size_t buflen);
 const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
 const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
 					u32 vendor_type);
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 44d400020..8e7b07fa1 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -5981,6 +5981,17 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 	if (mask & WPA_BSS_MASK_ML)
 		pos += print_ml(bss, pos, end);
 
+	if (mask & WPA_BSS_MASK_TIME_ZONE) {
+		char tz[256];
+
+		if (wpa_bss_get_time_zone(bss, tz, sizeof(tz)) > 0) {
+			ret = os_snprintf(pos, end - pos, "tz=%s\n", tz);
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		}
+	}
+
 	if (mask & WPA_BSS_MASK_DELIM) {
 		ret = os_snprintf(pos, end - pos, "====\n");
 		if (os_snprintf_error(end - pos, ret))
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 01555aaf8..019cbb0dd 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -3122,6 +3122,11 @@ static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
 	  NULL,
 	  NULL,
 	},
+	{ "TimeZone", WPAS_DBUS_NEW_IFACE_BSS, "s",
+	  wpas_dbus_getter_bss_time_zone,
+	  NULL,
+	  NULL
+	},
 	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 2774a7838..e686a2e79 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -5790,6 +5790,38 @@ dbus_bool_t wpas_dbus_getter_bss_age(
 }
 
 
+/**
+ * wpas_dbus_getter_bss_time_zone - Return the time zone advertised by the BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "TimeZone" property. Returns the Time Zone element contents in
+ * IEEE Std 1003.1 TZ environment variable format, or an empty string if the
+ * BSS does not advertise a (printable ASCII) time zone.
+ */
+dbus_bool_t wpas_dbus_getter_bss_time_zone(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *res;
+	char tz[256];
+	const char *str = tz;
+
+	res = get_bss_helper(args, error, __func__);
+	if (!res)
+		return FALSE;
+
+	if (wpa_bss_get_time_zone(res, tz, sizeof(tz)) < 0)
+		tz[0] = '\0';
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &str,
+						error);
+}
+
+
 /**
  * wpas_dbus_getter_bss_anqp - Return all the ANQP fields of a BSS
  * @iter: Pointer to incoming dbus message iter
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 24ee678e1..20fc38f46 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -221,6 +221,7 @@ DECLARE_ACCESSOR(wpas_dbus_getter_bss_wps);
 DECLARE_ACCESSOR(wpas_dbus_getter_bss_ies);
 DECLARE_ACCESSOR(wpas_dbus_getter_bss_age);
 DECLARE_ACCESSOR(wpas_dbus_getter_bss_anqp);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_time_zone);
 DECLARE_ACCESSOR(wpas_dbus_getter_enabled);
 DECLARE_ACCESSOR(wpas_dbus_setter_enabled);
 DECLARE_ACCESSOR(wpas_dbus_getter_network_properties);
-- 
2.43.0

-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 6179 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/hostap/attachments/20261028/82f2f304/attachment.p7s>


More information about the Hostap mailing list