diff mbox series

[1/2] hostapd: add support for unicast beacons

Message ID 20230105200945.761324-1-raphael.melotte@mind.be
State Changes Requested
Headers show
Series [1/2] hostapd: add support for unicast beacons | expand

Commit Message

Raphaël Mélotte Jan. 5, 2023, 8:09 p.m. UTC
In some specific scenarios where a BSS is used for a single station,
it can be useful to be able to configure beacons as unicast instead of
broadcast.

Add a new configuration parameter, 'beacon_da'. When set, its value is
used as the destination address for beacon frames. If unset, set to
zero, or to the broadcast address, the behavior remains the same as
before (i.e. beacons are broadcasted).

Signed-off-by: Raphaël Mélotte <raphael.melotte@mind.be>
---
 hostapd/config_file.c | 6 ++++++
 hostapd/ctrl_iface.c  | 8 ++++++++
 hostapd/hostapd.conf  | 3 +++
 src/ap/ap_config.h    | 1 +
 src/ap/beacon.c       | 7 ++++++-
 5 files changed, 24 insertions(+), 1 deletion(-)

Comments

Jouni Malinen Feb. 1, 2023, 4:48 p.m. UTC | #1
On Thu, Jan 05, 2023 at 09:09:44PM +0100, Raphaël Mélotte wrote:
> In some specific scenarios where a BSS is used for a single station,
> it can be useful to be able to configure beacons as unicast instead of
> broadcast.

That would be explicitly non-compliant with the standard: IEEE Std
802.11-2020, 11.1.3.1: "The Address 1 field of the Beacon or Timing
Advertisement frame shall be set to the broadcast address."

In addition to not being allowed, this would sound like a potentially
quite bad thing to allow on the non-AP STA side since using unicast
Beacon frames might be sufficient to bypass beacon protection (which is
defined using BIP that works only with group-addressed frames) and
because this could be used for targeted attacks against a single non-AP
STA in the BSS. In other words, I'd recommend non-AP STA implementations
to discard any received Beacon frame if Address 1 is not broadcast.

In addition to this, I doubt this would work correctly with most
existing driver implementations on the transmitter side. In particular,
power save buffering of group addressed frames or anything else that is
gated on Beacon frame transmission might get quite interesting if there
is suddenly an expectation of having to retransmit the Beacon frame due
not having received an ACK frame for it.
Raphaël Mélotte March 1, 2023, 4:27 p.m. UTC | #2
On 2/1/23 17:48, Jouni Malinen wrote:
> That would be explicitly non-compliant with the standard: IEEE Std
> 802.11-2020, 11.1.3.1: "The Address 1 field of the Beacon or Timing
> Advertisement frame shall be set to the broadcast address."
> 
> In addition to not being allowed, this would sound like a potentially
> quite bad thing to allow on the non-AP STA side since using unicast
> Beacon frames might be sufficient to bypass beacon protection (which is
> defined using BIP that works only with group-addressed frames) and
> because this could be used for targeted attacks against a single non-AP
> STA in the BSS. In other words, I'd recommend non-AP STA implementations
> to discard any received Beacon frame if Address 1 is not broadcast.
> 
> In addition to this, I doubt this would work correctly with most
> existing driver implementations on the transmitter side. In particular,
> power save buffering of group addressed frames or anything else that is
> gated on Beacon frame transmission might get quite interesting if there
> is suddenly an expectation of having to retransmit the Beacon frame due
> not having received an ACK frame for it.

Interesting, thank you for the feedback.

I realize I also didn't provide the context when submitting this
patch: this was also needed for the "Virtualized BSSs" feature
described in an informative appendix of EasyMesh R5 (Appendix B),
which mentions:

"""
Multi-AP Agents target VBSS to individual clients via unicast beacons instead of broadcast beacons. Because the
beacons are unicast, clients will respond with ACKs, thus allowing the Multi-AP Agents to possibly adjust the MCS of the
unicast beacons to make better use of the channel airtime.
"""

I guess this part will be revisited..
diff mbox series

Patch

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 76f9cf831..8eeab92c8 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -3404,6 +3404,12 @@  static int hostapd_config_fill(struct hostapd_config *conf,
 				   line);
 			return 1;
 		}
+	} else if (os_strcmp(buf, "beacon_da") == 0) {
+		if (hwaddr_aton(pos, bss->beacon_da)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid beacon_da item",
+				   line);
+			return 1;
+		}
 	} else if (os_strcmp(buf, "use_driver_iface_addr") == 0) {
 		conf->use_driver_iface_addr = atoi(pos);
 	} else if (os_strcmp(buf, "ieee80211w") == 0) {
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 464dfc8ee..1ba643bf0 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -983,6 +983,14 @@  static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
 		return pos - buf;
 	pos += ret;
 
+	if (!is_zero_ether_addr(hapd->conf->beacon_da)) {
+		ret = os_snprintf(pos, end - pos, "beacon_da=" MACSTR "\n",
+				  MAC2STR(hapd->conf->beacon_da));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
 	if ((hapd->conf->config_id)) {
 		ret = os_snprintf(pos, end - pos, "config_id=%s\n",
 				  hapd->conf->config_id);
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index c5e74a6a2..c1a71d68e 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -94,6 +94,9 @@  ssid=test
 # UTF-8 SSID: Whether the SSID is to be interpreted using UTF-8 encoding
 #utf8_ssid=1
 
+# Destination address for beacon frames (defaults to broadcast)
+#beacon_da=ff:ff:ff:ff:ff:ff
+
 # Country code (ISO/IEC 3166-1). Used to set regulatory domain.
 # Set as needed to indicate country in which device is operating.
 # This can limit available channels and transmit power.
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 1631cf2aa..03ad4bed7 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -471,6 +471,7 @@  struct hostapd_bss_config {
 	struct hostapd_vlan *vlan;
 
 	macaddr bssid;
+	macaddr beacon_da;
 
 	/*
 	 * Maximum listen interval that STAs can use when associating with this
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index dbc6b062b..0f69a31a3 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -1678,7 +1678,12 @@  int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 	head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 					   WLAN_FC_STYPE_BEACON);
 	head->duration = host_to_le16(0);
-	os_memset(head->da, 0xff, ETH_ALEN);
+	if (is_zero_ether_addr(hapd->conf->beacon_da) ||
+	    is_broadcast_ether_addr(hapd->conf->beacon_da))
+		os_memset(head->da, 0xff, ETH_ALEN);
+	else {
+		os_memcpy(head->da, hapd->conf->beacon_da, ETH_ALEN);
+	}
 
 	os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
 	os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);