[PATCH v2] wpa_supplicant: always have data frame filtering

Johannes Berg johannes at sipsolutions.net
Mon Mar 9 04:02:14 PDT 2026


From: Johannes Berg <johannes.berg at intel.com>

Always enable IP unicast in L2/WiFi multicast filtering for
all networks (not just HS2.0 non-DGAF ones) by default, but
provide an option to turn it off in case someone finds it
breaks something.

Also provide an option (but off by default) to always (and
not just in HS2.0 networks) rely on proxy ARP/NS if the AP
advertises it and in that case drop gratuitous ARP (IPv4)
and unsolicited NA (IPv6) packets.

Signed-off-by: Johannes Berg <johannes.berg at intel.com>
---
v2:
 - fix logic
 - add always_use_proxy_arp=2 mode to force it
---
 wpa_supplicant/config.c            |  3 ++
 wpa_supplicant/config_file.c       |  2 ++
 wpa_supplicant/config_ssid.h       | 15 ++++++++
 wpa_supplicant/hs20_supplicant.c   | 37 --------------------
 wpa_supplicant/hs20_supplicant.h   |  1 -
 wpa_supplicant/sme.c               |  4 +--
 wpa_supplicant/wpa_supplicant.c    | 56 +++++++++++++++++++++++++++---
 wpa_supplicant/wpa_supplicant.conf | 19 ++++++++++
 wpa_supplicant/wpa_supplicant_i.h  |  1 +
 9 files changed, 93 insertions(+), 45 deletions(-)

diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 20dd4eaf3c9e..b00f7dcddf03 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2843,6 +2843,8 @@ static const struct parse_data ssid_fields[] = {
 #ifdef CONFIG_PMKSA_PRIVACY
 	{ INT_RANGE(pmksa_privacy, 0, 1)},
 #endif /* CONFIG_PMKSA_PRIVACY */
+	{ INT_RANGE(drop_unicast_ip_in_l2_multicast, 0, 1)},
+	{ INT_RANGE(always_use_proxy_arp, 0, 2)},
 };
 
 #undef OFFSET
@@ -3408,6 +3410,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
 	ssid->mac_addr = WPAS_MAC_ADDR_STYLE_NOT_SET;
 	ssid->max_oper_chwidth = DEFAULT_MAX_OPER_CHWIDTH;
 	ssid->rsn_overriding = RSN_OVERRIDING_NOT_SET;
+	ssid->drop_unicast_ip_in_l2_multicast = 1;
 }
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 2f62e6cee167..42299adf595f 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1040,6 +1040,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid,
 #ifdef CONFIG_PMKSA_PRIVACY
 	INT(pmksa_privacy);
 #endif /* CONFIG_PMKSA_PRIVACY */
+	INT_DEF(drop_unicast_ip_in_l2_multicast, 1);
+	INT_DEF(always_use_proxy_arp, 0);
 #undef STR
 #undef INT
 #undef INT_DEF
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 43a13053090d..6e614b087b62 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -1354,6 +1354,21 @@ struct wpa_ssid {
 	 * connection even if the same cached PMKSA is reused.
 	 */
 	int pmksa_privacy;
+
+	/**
+	 * drop_unicast_ip_in_l2_multicast - drop unicast IP in L2 multicast
+	 *	in all networks, not just HS2.0 non-DGAF networks
+	 */
+	bool drop_unicast_ip_in_l2_multicast;
+
+	/**
+	 * always_use_proxy_arp - always rely on proxy ARP even in non-HS2.0
+	 *	networks
+	 * 0 = disabled
+	 * 1 = check AP's extended capabilities
+	 * 2 = always enable
+	 */
+	u8 always_use_proxy_arp;
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index 627935474f42..ef53c4cf361f 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -29,43 +29,6 @@
 #include "notify.h"
 
 
-void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s)
-{
-	struct wpa_bss *bss = wpa_s->current_bss;
-	const u8 *ie;
-	const u8 *ext_capa;
-	u32 filter = 0;
-
-	if (!bss || !is_hs20_network(wpa_s, wpa_s->current_ssid, bss)) {
-		/* Not configuring frame filtering - BSS is not a Hotspot 2.0
-		 * network */
-		return;
-	}
-
-	ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE);
-
-	/* Check if DGAF disabled bit is zero (5th byte in the IE) */
-	if (!ie || ie[1] < 5)
-		wpa_printf(MSG_DEBUG,
-			   "Not configuring frame filtering - Can't extract DGAF bit");
-	else if (!(ie[6] & HS20_DGAF_DISABLED))
-		filter |= WPA_DATA_FRAME_FILTER_FLAG_GTK;
-
-	ext_capa = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
-	if (!ext_capa || ext_capa[1] < 2) {
-		wpa_printf(MSG_DEBUG,
-			   "Not configuring frame filtering - Can't extract Proxy ARP bit");
-		return;
-	}
-
-	if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_PROXY_ARP))
-		filter |= WPA_DATA_FRAME_FILTER_FLAG_ARP |
-			WPA_DATA_FRAME_FILTER_FLAG_NA;
-
-	wpa_drv_configure_frame_filters(wpa_s, filter);
-}
-
-
 void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id, int ap_release)
 {
 	int release;
diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h
index ce12bfb0ee0b..0bfdd847e7e5 100644
--- a/wpa_supplicant/hs20_supplicant.h
+++ b/wpa_supplicant/hs20_supplicant.h
@@ -8,7 +8,6 @@
 #ifndef HS20_SUPPLICANT_H
 #define HS20_SUPPLICANT_H
 
-void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s);
 void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id,
 			      int ap_release);
 void wpas_hs20_add_roam_cons_sel(struct wpabuf *buf,
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 8c6bc23e644d..a9ee381aa24f 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1434,9 +1434,7 @@ no_fils:
 	if (old_ssid != wpa_s->current_ssid)
 		wpas_notify_network_changed(wpa_s);
 
-#ifdef CONFIG_HS20
-	hs20_configure_frame_filters(wpa_s);
-#endif /* CONFIG_HS20 */
+	wpas_configure_frame_filters(wpa_s);
 
 #ifdef CONFIG_P2P
 	/*
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 937a26265e5b..4cd4ef3bcc63 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -4054,11 +4054,12 @@ static u8 * wpas_populate_assoc_ies(
 			}
 			wpabuf_free(hs20);
 
-			hs20_configure_frame_filters(wpa_s);
 		}
 	}
 #endif /* CONFIG_HS20 */
 
+	wpas_configure_frame_filters(wpa_s);
+
 	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
 		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
 		size_t len;
@@ -5048,9 +5049,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
 #endif /* CONFIG_P2P */
 	    ssid->bssid_set) {
 		wpa_s->current_bss = bss;
-#ifdef CONFIG_HS20
-		hs20_configure_frame_filters(wpa_s);
-#endif /* CONFIG_HS20 */
+		wpas_configure_frame_filters(wpa_s);
 	}
 
 	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
@@ -10231,3 +10230,52 @@ void wpas_update_dfs_ap_info(struct wpa_supplicant *wpa_s, int freq,
 				       disconnect_evt);
 #endif /* CONFIG_P2P */
 }
+
+
+void wpas_configure_frame_filters(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss = wpa_s->current_bss;
+	const u8 *ie;
+	u32 filter = 0;
+	bool hs20, proxy_arp_capa;
+
+	if (!bss)
+		return;
+
+#ifdef CONFIG_HS20
+	hs20 = is_hs20_network(wpa_s, wpa_s->current_ssid, bss);
+#else
+	hs20 = false;
+#endif
+
+	if (wpa_s->current_ssid &&
+	    wpa_s->current_ssid->drop_unicast_ip_in_l2_multicast) {
+		filter |= WPA_DATA_FRAME_FILTER_FLAG_GTK;
+	} else {
+		if (!hs20) {
+			/* Not configuring frame filtering - BSS is not a
+			 * Hotspot 2.0 network */
+			return;
+		}
+
+		ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE);
+
+		/* Check if DGAF disabled bit is zero (5th byte in the IE) */
+		if (!ie || ie[1] < 5)
+			wpa_printf(MSG_DEBUG,
+				   "Not configuring frame filtering - Can't extract DGAF bit");
+		else if (!(ie[6] & HS20_DGAF_DISABLED))
+			filter |= WPA_DATA_FRAME_FILTER_FLAG_GTK;
+	}
+
+	proxy_arp_capa = wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_PROXY_ARP);
+
+	if ((hs20 && proxy_arp_capa) ||
+	    wpa_s->current_ssid->always_use_proxy_arp == 2 ||
+	    (proxy_arp_capa && wpa_s->current_ssid &&
+	     wpa_s->current_ssid->always_use_proxy_arp == 1))
+		filter |= WPA_DATA_FRAME_FILTER_FLAG_ARP |
+			  WPA_DATA_FRAME_FILTER_FLAG_NA;
+
+	wpa_drv_configure_frame_filters(wpa_s, filter);
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 2ff7b2ebeb31..40d0c8a444e6 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1795,6 +1795,25 @@ fast_reauth=1
 # 2 = Supports Multi-AP profile 2 as defined in Wi-Fi EasyMesh specification
 #multi_ap_profile=2
 
+# IP unicast in L2 multicast frame filtering
+# Allow turning off IP unicast in L2 multicast frame filtering,
+# which is enabled by default. This option can also be given in
+# the global section to apply to all network blocks.
+#drop_unicast_ip_in_l2_multicast=1
+
+# Proxy ARP/NS reliance
+# Enable Proxy ARP/NS reliance, dropping gratuitous ARP and unsolicited NA
+# frames when the AP supports proxy ARP even on networks that aren't HS2.0.
+# If set to 0 (the default), this is only done on HS2.0 networks.
+# If set to 1, the AP's extended capabilities are checked, and proxy ARP/NS
+# is used when available.
+# If set to 2, the feature is enabled regardless of the AP's extended
+# capabilities, to allow forcing it in networks known to have it, since
+# the extended capabilities are not cryptographically checked.
+# This option can also be given in the global section to apply to all
+# network blocks.
+#always_use_proxy_arp=0
+
 ##### Fast Session Transfer (FST) support #####################################
 #
 # The options in this section are only available when the build configuration
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 4148e94ea06d..967ba6850d1c 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -2114,5 +2114,6 @@ int wpas_get_owe_trans_network(const u8 *owe_ie, const u8 **bssid,
 void wpas_update_dfs_ap_info(struct wpa_supplicant *wpa_s, int freq,
 			     enum chan_width ap_ch_width,
 			     bool disconnect_evt);
+void wpas_configure_frame_filters(struct wpa_supplicant *wpa_s);
 
 #endif /* WPA_SUPPLICANT_I_H */
-- 
2.53.0




More information about the Hostap mailing list