Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame
@ 2026-05-15 23:38 Masashi Honma
  2026-05-15 23:38 ` [PATCH v6 2/6] wifi: mac80211: Use struct instead of macro for PREP frame Masashi Honma
                   ` (5 more replies)
  0 siblings, 6 replies; 17+ messages in thread
From: Masashi Honma @ 2026-05-15 23:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 43 +++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 68 +++++++++++++++++-----------------
 2 files changed, 76 insertions(+), 35 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 4b829bcb38b6..623894140300 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -28,12 +28,40 @@ struct ieee80211s_hdr {
 	u8 eaddr2[ETH_ALEN];
 } __packed __aligned(2);
 
+struct ieee80211_mesh_hwmp_preq_target {
+	u8 flags;
+	u8 addr[ETH_ALEN];
+	__le32 sn;
+} __packed;
+
+struct ieee80211_mesh_hwmp_preq_top {
+	u8 flags;
+	u8 hopcount;
+	u8 ttl;
+	__le32 preq_id;
+	u8 orig_addr[ETH_ALEN];
+	__le32 orig_sn;
+
+	/* optional AE, lifetime, metric, target */
+	u8 variable[];
+} __packed;
+
+struct ieee80211_mesh_hwmp_preq_bottom {
+	__le32 lifetime;
+	__le32 metric;
+	u8 target_count;
+	struct ieee80211_mesh_hwmp_preq_target targets[];
+} __packed;
+
 /* Mesh flags */
 #define MESH_FLAGS_AE_A4 	0x1
 #define MESH_FLAGS_AE_A5_A6	0x2
 #define MESH_FLAGS_AE		0x3
 #define MESH_FLAGS_PS_DEEP	0x4
 
+/* HWMP IE processing macros */
+#define AE_F			(1<<6)
+
 /**
  * enum ieee80211_preq_flags - mesh PREQ element flags
  *
@@ -227,4 +255,19 @@ enum ieee80211_root_mode_identifier {
 	IEEE80211_PROACTIVE_RANN = 4,
 };
 
+static inline bool ieee80211_mesh_preq_prep_ae_enabled(const u8 *ie)
+{
+	return ie[0] & AE_F;
+}
+
+static inline struct ieee80211_mesh_hwmp_preq_bottom *
+	ieee80211_mesh_hwmp_preq_get_bottom(const u8 *ie)
+{
+	struct ieee80211_mesh_hwmp_preq_top *top =
+		(struct ieee80211_mesh_hwmp_preq_top *)ie;
+	return (struct ieee80211_mesh_hwmp_preq_bottom *)
+		&top->variable[ieee80211_mesh_preq_prep_ae_enabled(ie) ?
+			ETH_ALEN : 0];
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 9d89ebcce1c1..e0c8ba8cb1e0 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -35,24 +35,11 @@ static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
 }
 
 /* HWMP IE processing macros */
-#define AE_F			(1<<6)
 #define AE_F_SET(x)		(*x & AE_F)
-#define PREQ_IE_FLAGS(x)	(*(x))
-#define PREQ_IE_HOPCOUNT(x)	(*(x + 1))
-#define PREQ_IE_TTL(x)		(*(x + 2))
-#define PREQ_IE_PREQ_ID(x)	u32_field_get(x, 3, 0)
-#define PREQ_IE_ORIG_ADDR(x)	(x + 7)
-#define PREQ_IE_ORIG_SN(x)	u32_field_get(x, 13, 0)
-#define PREQ_IE_LIFETIME(x)	u32_field_get(x, 17, AE_F_SET(x))
-#define PREQ_IE_METRIC(x) 	u32_field_get(x, 21, AE_F_SET(x))
-#define PREQ_IE_TARGET_F(x)	(*(AE_F_SET(x) ? x + 32 : x + 26))
-#define PREQ_IE_TARGET_ADDR(x) 	(AE_F_SET(x) ? x + 33 : x + 27)
-#define PREQ_IE_TARGET_SN(x) 	u32_field_get(x, 33, AE_F_SET(x))
-
-
-#define PREP_IE_FLAGS(x)	PREQ_IE_FLAGS(x)
-#define PREP_IE_HOPCOUNT(x)	PREQ_IE_HOPCOUNT(x)
-#define PREP_IE_TTL(x)		PREQ_IE_TTL(x)
+
+#define PREP_IE_FLAGS(x)	(*(x))
+#define PREP_IE_HOPCOUNT(x)	(*(x + 1))
+#define PREP_IE_TTL(x)		(*(x + 2))
 #define PREP_IE_ORIG_ADDR(x)	(AE_F_SET(x) ? x + 27 : x + 21)
 #define PREP_IE_ORIG_SN(x)	u32_field_get(x, 27, AE_F_SET(x))
 #define PREP_IE_LIFETIME(x)	u32_field_get(x, 13, AE_F_SET(x))
@@ -415,11 +402,16 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
 
 	switch (action) {
 	case MPATH_PREQ:
-		orig_addr = PREQ_IE_ORIG_ADDR(hwmp_ie);
-		orig_sn = PREQ_IE_ORIG_SN(hwmp_ie);
-		orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie);
-		orig_metric = PREQ_IE_METRIC(hwmp_ie);
-		hopcount = PREQ_IE_HOPCOUNT(hwmp_ie) + 1;
+		struct ieee80211_mesh_hwmp_preq_top *preq_elem_top =
+			(struct ieee80211_mesh_hwmp_preq_top *)hwmp_ie;
+		struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
+			ieee80211_mesh_hwmp_preq_get_bottom(hwmp_ie);
+
+		orig_addr = preq_elem_top->orig_addr;
+		orig_sn = get_unaligned_le32(&preq_elem_top->orig_sn);
+		orig_lifetime = get_unaligned_le32(&preq_elem_bottom->lifetime);
+		orig_metric = get_unaligned_le32(&preq_elem_bottom->metric);
+		hopcount = preq_elem_top->hopcount + 1;
 		break;
 	case MPATH_PREP:
 		/* Originator here refers to the MP that was the target in the
@@ -579,6 +571,12 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 				    const u8 *preq_elem, u32 orig_metric)
 {
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+	struct ieee80211_mesh_hwmp_preq_top *preq_elem_top =
+		(struct ieee80211_mesh_hwmp_preq_top *)preq_elem;
+	struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
+		ieee80211_mesh_hwmp_preq_get_bottom(preq_elem);
+	struct ieee80211_mesh_hwmp_preq_target *target =
+		preq_elem_bottom->targets;
 	struct mesh_path *mpath = NULL;
 	const u8 *target_addr, *orig_addr;
 	const u8 *da;
@@ -589,13 +587,13 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 	bool root_is_gate;
 
 	/* Update target SN, if present */
-	target_addr = PREQ_IE_TARGET_ADDR(preq_elem);
-	orig_addr = PREQ_IE_ORIG_ADDR(preq_elem);
-	target_sn = PREQ_IE_TARGET_SN(preq_elem);
-	orig_sn = PREQ_IE_ORIG_SN(preq_elem);
-	target_flags = PREQ_IE_TARGET_F(preq_elem);
+	target_addr = target[0].addr;
+	orig_addr = preq_elem_top->orig_addr;
+	target_sn = get_unaligned_le32(&target[0].sn);
+	orig_sn = get_unaligned_le32(&preq_elem_top->orig_sn);
+	target_flags = target[0].flags;
 	/* Proactive PREQ gate announcements */
-	flags = PREQ_IE_FLAGS(preq_elem);
+	flags = preq_elem_top->flags;
 	root_is_gate = !!(flags & RANN_FLAG_IS_GATE);
 
 	mhwmp_dbg(sdata, "received PREQ from %pM\n", orig_addr);
@@ -655,7 +653,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 	}
 
 	if (reply) {
-		lifetime = PREQ_IE_LIFETIME(preq_elem);
+		lifetime = get_unaligned_le32(&preq_elem_bottom->lifetime);
 		ttl = ifmsh->mshcfg.element_ttl;
 		if (ttl != 0) {
 			mhwmp_dbg(sdata, "replying to the PREQ\n");
@@ -673,22 +671,22 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 		u32 preq_id;
 		u8 hopcount;
 
-		ttl = PREQ_IE_TTL(preq_elem);
-		lifetime = PREQ_IE_LIFETIME(preq_elem);
+		ttl = preq_elem_top->ttl;
+		lifetime = get_unaligned_le32(&preq_elem_bottom->lifetime);
 		if (ttl <= 1) {
 			ifmsh->mshstats.dropped_frames_ttl++;
 			return;
 		}
 		mhwmp_dbg(sdata, "forwarding the PREQ from %pM\n", orig_addr);
 		--ttl;
-		preq_id = PREQ_IE_PREQ_ID(preq_elem);
-		hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1;
+		preq_id = get_unaligned_le32(&preq_elem_top->preq_id);
+		hopcount = preq_elem_top->hopcount + 1;
 		da = (mpath && mpath->is_root) ?
 			mpath->rann_snd_addr : broadcast_addr;
 
 		if (flags & IEEE80211_PREQ_PROACTIVE_PREP_FLAG) {
-			target_addr = PREQ_IE_TARGET_ADDR(preq_elem);
-			target_sn = PREQ_IE_TARGET_SN(preq_elem);
+			target_addr = target[0].addr;
+			target_sn = get_unaligned_le32(&target[0].sn);
 		}
 
 		mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr,
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 2/6] wifi: mac80211: Use struct instead of macro for PREP frame
  2026-05-15 23:38 [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame Masashi Honma
@ 2026-05-15 23:38 ` Masashi Honma
  2026-05-15 23:38 ` [PATCH v6 3/6] wifi: mac80211: Use struct instead of macro for PERR frame Masashi Honma
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 17+ messages in thread
From: Masashi Honma @ 2026-05-15 23:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 28 ++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 47 +++++++++++++++++-----------------
 2 files changed, 51 insertions(+), 24 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 623894140300..093c6b80a983 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -53,6 +53,24 @@ struct ieee80211_mesh_hwmp_preq_bottom {
 	struct ieee80211_mesh_hwmp_preq_target targets[];
 } __packed;
 
+struct ieee80211_mesh_hwmp_prep_top {
+	u8 flags;
+	u8 hopcount;
+	u8 ttl;
+	u8 target_addr[ETH_ALEN];
+	__le32 target_sn;
+
+	/* optional Target External Address */
+	u8 variable[];
+} __packed;
+
+struct ieee80211_mesh_hwmp_prep_bottom {
+	__le32 lifetime;
+	__le32 metric;
+	u8 orig_addr[ETH_ALEN];
+	__le32 orig_sn;
+} __packed;
+
 /* Mesh flags */
 #define MESH_FLAGS_AE_A4 	0x1
 #define MESH_FLAGS_AE_A5_A6	0x2
@@ -270,4 +288,14 @@ static inline struct ieee80211_mesh_hwmp_preq_bottom *
 			ETH_ALEN : 0];
 }
 
+static inline struct ieee80211_mesh_hwmp_prep_bottom *
+	ieee80211_mesh_hwmp_prep_get_bottom(const u8 *ie)
+{
+	struct ieee80211_mesh_hwmp_prep_top *top =
+		(struct ieee80211_mesh_hwmp_prep_top *)ie;
+	return (struct ieee80211_mesh_hwmp_prep_bottom *)
+		&top->variable[ieee80211_mesh_preq_prep_ae_enabled(ie) ?
+			ETH_ALEN : 0];
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index e0c8ba8cb1e0..d9394091cea9 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -37,16 +37,6 @@ static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
 /* HWMP IE processing macros */
 #define AE_F_SET(x)		(*x & AE_F)
 
-#define PREP_IE_FLAGS(x)	(*(x))
-#define PREP_IE_HOPCOUNT(x)	(*(x + 1))
-#define PREP_IE_TTL(x)		(*(x + 2))
-#define PREP_IE_ORIG_ADDR(x)	(AE_F_SET(x) ? x + 27 : x + 21)
-#define PREP_IE_ORIG_SN(x)	u32_field_get(x, 27, AE_F_SET(x))
-#define PREP_IE_LIFETIME(x)	u32_field_get(x, 13, AE_F_SET(x))
-#define PREP_IE_METRIC(x)	u32_field_get(x, 17, AE_F_SET(x))
-#define PREP_IE_TARGET_ADDR(x)	(x + 3)
-#define PREP_IE_TARGET_SN(x)	u32_field_get(x, 9, 0)
-
 #define PERR_IE_TTL(x)		(*(x))
 #define PERR_IE_TARGET_FLAGS(x)	(*(x + 2))
 #define PERR_IE_TARGET_ADDR(x)	(x + 3)
@@ -419,11 +409,16 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
 		 * so that we can easily use a single function to gather path
 		 * information from both PREQ and PREP frames.
 		 */
-		orig_addr = PREP_IE_TARGET_ADDR(hwmp_ie);
-		orig_sn = PREP_IE_TARGET_SN(hwmp_ie);
-		orig_lifetime = PREP_IE_LIFETIME(hwmp_ie);
-		orig_metric = PREP_IE_METRIC(hwmp_ie);
-		hopcount = PREP_IE_HOPCOUNT(hwmp_ie) + 1;
+		struct ieee80211_mesh_hwmp_prep_top *prep_elem_top =
+			(struct ieee80211_mesh_hwmp_prep_top *)hwmp_ie;
+		struct ieee80211_mesh_hwmp_prep_bottom *prep_elem_bottom =
+			ieee80211_mesh_hwmp_prep_get_bottom(hwmp_ie);
+
+		orig_addr = prep_elem_top->target_addr;
+		orig_sn = get_unaligned_le32(&prep_elem_top->target_sn);
+		orig_lifetime = get_unaligned_le32(&prep_elem_bottom->lifetime);
+		orig_metric = get_unaligned_le32(&prep_elem_bottom->metric);
+		hopcount = prep_elem_top->hopcount + 1;
 		break;
 	default:
 		rcu_read_unlock();
@@ -715,6 +710,10 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
 				    const u8 *prep_elem, u32 metric)
 {
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+	struct ieee80211_mesh_hwmp_prep_top *prep_elem_top =
+		(struct ieee80211_mesh_hwmp_prep_top *)prep_elem;
+	struct ieee80211_mesh_hwmp_prep_bottom *prep_elem_bottom =
+		ieee80211_mesh_hwmp_prep_get_bottom(prep_elem);
 	struct mesh_path *mpath;
 	const u8 *target_addr, *orig_addr;
 	u8 ttl, hopcount, flags;
@@ -722,9 +721,9 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
 	u32 target_sn, orig_sn, lifetime;
 
 	mhwmp_dbg(sdata, "received PREP from %pM\n",
-		  PREP_IE_TARGET_ADDR(prep_elem));
+		  prep_elem_top->target_addr);
 
-	orig_addr = PREP_IE_ORIG_ADDR(prep_elem);
+	orig_addr = prep_elem_bottom->orig_addr;
 	if (ether_addr_equal(orig_addr, sdata->vif.addr))
 		/* destination, no forwarding required */
 		return;
@@ -732,7 +731,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
 	if (!ifmsh->mshcfg.dot11MeshForwarding)
 		return;
 
-	ttl = PREP_IE_TTL(prep_elem);
+	ttl = prep_elem_top->ttl;
 	if (ttl <= 1) {
 		sdata->u.mesh.mshstats.dropped_frames_ttl++;
 		return;
@@ -751,12 +750,12 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
 	memcpy(next_hop, next_hop_deref_protected(mpath)->sta.addr, ETH_ALEN);
 	spin_unlock_bh(&mpath->state_lock);
 	--ttl;
-	flags = PREP_IE_FLAGS(prep_elem);
-	lifetime = PREP_IE_LIFETIME(prep_elem);
-	hopcount = PREP_IE_HOPCOUNT(prep_elem) + 1;
-	target_addr = PREP_IE_TARGET_ADDR(prep_elem);
-	target_sn = PREP_IE_TARGET_SN(prep_elem);
-	orig_sn = PREP_IE_ORIG_SN(prep_elem);
+	flags = prep_elem_top->flags;
+	lifetime = get_unaligned_le32(&prep_elem_bottom->lifetime);
+	hopcount = prep_elem_top->hopcount + 1;
+	target_addr = prep_elem_top->target_addr;
+	target_sn = get_unaligned_le32(&prep_elem_top->target_sn);
+	orig_sn = get_unaligned_le32(&prep_elem_bottom->orig_sn);
 
 	mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr, orig_sn, 0,
 			       target_addr, target_sn, next_hop, hopcount,
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 3/6] wifi: mac80211: Use struct instead of macro for PERR frame
  2026-05-15 23:38 [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame Masashi Honma
  2026-05-15 23:38 ` [PATCH v6 2/6] wifi: mac80211: Use struct instead of macro for PREP frame Masashi Honma
@ 2026-05-15 23:38 ` Masashi Honma
  2026-05-15 23:38 ` [PATCH v6 4/6] wifi: mac80211: Fix overread in PREQ frame processing Masashi Honma
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 17+ messages in thread
From: Masashi Honma @ 2026-05-15 23:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 26 ++++++++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 32 ++++++--------------------------
 2 files changed, 32 insertions(+), 26 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 093c6b80a983..6b56f462fc21 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -71,6 +71,20 @@ struct ieee80211_mesh_hwmp_prep_bottom {
 	__le32 orig_sn;
 } __packed;
 
+struct ieee80211_mesh_hwmp_perr_dst {
+	u8 flags;
+	u8 addr[ETH_ALEN];
+	__le32 sn;
+	/* optional Destination External Address */
+	u8 variable[];
+} __packed;
+
+struct ieee80211_mesh_hwmp_perr {
+	u8 ttl;
+	u8 number_of_dst;
+	struct ieee80211_mesh_hwmp_perr_dst dsts[];
+} __packed;
+
 /* Mesh flags */
 #define MESH_FLAGS_AE_A4 	0x1
 #define MESH_FLAGS_AE_A5_A6	0x2
@@ -298,4 +312,16 @@ static inline struct ieee80211_mesh_hwmp_prep_bottom *
 			ETH_ALEN : 0];
 }
 
+static inline __le16 ieee80211_mesh_hwmp_perr_get_rcode(
+	const u8 *ie, u8 dst_idx)
+{
+	struct ieee80211_mesh_hwmp_perr *perr_ie =
+		(struct ieee80211_mesh_hwmp_perr *)ie;
+	struct ieee80211_mesh_hwmp_perr_dst *dst =
+		&perr_ie->dsts[dst_idx];
+
+	return get_unaligned_le16(&dst->variable[
+		(dst->flags & AE_F) ? ETH_ALEN : 0]);
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index d9394091cea9..044555c42a86 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -20,29 +20,7 @@
 
 static void mesh_queue_preq(struct mesh_path *, u8);
 
-static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)
-{
-	if (ae)
-		offset += 6;
-	return get_unaligned_le32(preq_elem + offset);
-}
-
-static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
-{
-	if (ae)
-		offset += 6;
-	return get_unaligned_le16(preq_elem + offset);
-}
-
 /* HWMP IE processing macros */
-#define AE_F_SET(x)		(*x & AE_F)
-
-#define PERR_IE_TTL(x)		(*(x))
-#define PERR_IE_TARGET_FLAGS(x)	(*(x + 2))
-#define PERR_IE_TARGET_ADDR(x)	(x + 3)
-#define PERR_IE_TARGET_SN(x)	u32_field_get(x, 9, 0)
-#define PERR_IE_TARGET_RCODE(x)	u16_field_get(x, 13, 0)
-
 #define MSEC_TO_TU(x) (x*1000/1024)
 #define SN_GT(x, y) ((s32)(y - x) < 0)
 #define SN_LT(x, y) ((s32)(x - y) < 0)
@@ -776,6 +754,8 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
 				    const u8 *perr_elem)
 {
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+	struct ieee80211_mesh_hwmp_perr *perr_elem_s =
+		(struct ieee80211_mesh_hwmp_perr *)perr_elem;
 	struct mesh_path *mpath;
 	u8 ttl;
 	const u8 *ta, *target_addr;
@@ -783,15 +763,15 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
 	u16 target_rcode;
 
 	ta = mgmt->sa;
-	ttl = PERR_IE_TTL(perr_elem);
+	ttl = perr_elem_s->ttl;
 	if (ttl <= 1) {
 		ifmsh->mshstats.dropped_frames_ttl++;
 		return;
 	}
 	ttl--;
-	target_addr = PERR_IE_TARGET_ADDR(perr_elem);
-	target_sn = PERR_IE_TARGET_SN(perr_elem);
-	target_rcode = PERR_IE_TARGET_RCODE(perr_elem);
+	target_addr = perr_elem_s->dsts[0].addr;
+	target_sn = get_unaligned_le32(&perr_elem_s->dsts[0].sn);
+	target_rcode = ieee80211_mesh_hwmp_perr_get_rcode(perr_elem, 0);
 
 	rcu_read_lock();
 	mpath = mesh_path_lookup(sdata, target_addr);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 4/6] wifi: mac80211: Fix overread in PREQ frame processing
  2026-05-15 23:38 [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame Masashi Honma
  2026-05-15 23:38 ` [PATCH v6 2/6] wifi: mac80211: Use struct instead of macro for PREP frame Masashi Honma
  2026-05-15 23:38 ` [PATCH v6 3/6] wifi: mac80211: Use struct instead of macro for PERR frame Masashi Honma
@ 2026-05-15 23:38 ` Masashi Honma
  2026-05-20 11:15   ` Johannes Berg
  2026-05-15 23:38 ` [PATCH v6 5/6] wifi: mac80211: Fix overread in PREP " Masashi Honma
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 17+ messages in thread
From: Masashi Honma @ 2026-05-15 23:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma

When the AF flag is enabled, hwmp_preq_frame_process() overreads target_addr
by 2 bytes. Since this occurs within the socket buffer, it does not read across
memory boundaries and therefore poses no security risk; however, we will fix it
as a precaution.

In this fix, a new function mesh_path_parse_request_frame() is established to
separate the implementation of frame format validation and the check for
unsupported features. This is intended to facilitate future work when
implementing the currently unsupported parts.

Assisted-by: Claude:Sonnet 4.6
Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 31 +++++++++++++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 12 ++++++++++--
 net/mac80211/parse.c           |  9 +++++++--
 3 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 6b56f462fc21..2ff94da770b1 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -324,4 +324,35 @@ static inline __le16 ieee80211_mesh_hwmp_perr_get_rcode(
 		(dst->flags & AE_F) ? ETH_ALEN : 0]);
 }
 
+/* IEEE Std 802.11-2016 9.4.2.113 PREQ element */
+static inline bool ieee80211_mesh_preq_size_ok(const u8 *pos, u8 elen)
+{
+	struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
+		ieee80211_mesh_hwmp_preq_get_bottom(pos);
+	u8 target_count;
+	u8 needed;
+
+	/* Check if the element contains flags */
+	if (elen < 1)
+		return false;
+
+	/* Check if the element contains target_count */
+	needed = sizeof(struct ieee80211_mesh_hwmp_preq_top) +
+		 (ieee80211_mesh_preq_prep_ae_enabled(pos) ? ETH_ALEN : 0)
+		 /* Originator External Address */ +
+		 sizeof(struct ieee80211_mesh_hwmp_preq_bottom);
+	if (elen < needed)
+		return false;
+
+	target_count = preq_elem_bottom->target_count;
+	if (target_count < 1 || target_count > 20)
+		return false;
+
+	needed += target_count * sizeof(struct ieee80211_mesh_hwmp_preq_target);
+	if (elen != needed)
+		return false;
+
+	return true;
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 044555c42a86..70973d53605d 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -932,9 +932,17 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
 		return;
 
 	if (elems->preq) {
-		if (elems->preq_len != 37)
-			/* Right now we support just 1 destination and no AE */
+		struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
+			ieee80211_mesh_hwmp_preq_get_bottom(elems->preq);
+
+		/* Right now we do not support AE (Address Extension) */
+		if (ieee80211_mesh_preq_prep_ae_enabled(elems->preq))
 			goto free;
+
+		/* Right now we only supports 1 target */
+		if (preq_elem_bottom->target_count != 1)
+			goto free;
+
 		path_metric = hwmp_route_info_get(sdata, mgmt, elems->preq,
 						  MPATH_PREQ);
 		if (path_metric)
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index 5e61457be0f3..9e52cc48fc18 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -547,8 +547,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
 				elems->awake_window = (void *)pos;
 			break;
 		case WLAN_EID_PREQ:
-			elems->preq = pos;
-			elems->preq_len = elen;
+			if (ieee80211_mesh_preq_size_ok(pos, elen)) {
+				elems->preq = pos;
+				elems->preq_len = elen;
+			} else {
+				elem_parse_failed =
+					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;
+			}
 			break;
 		case WLAN_EID_PREP:
 			elems->prep = pos;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 5/6] wifi: mac80211: Fix overread in PREP frame processing
  2026-05-15 23:38 [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame Masashi Honma
                   ` (2 preceding siblings ...)
  2026-05-15 23:38 ` [PATCH v6 4/6] wifi: mac80211: Fix overread in PREQ frame processing Masashi Honma
@ 2026-05-15 23:38 ` Masashi Honma
  2026-05-20 11:16   ` Johannes Berg
  2026-05-15 23:38 ` [PATCH v6 6/6] wifi: mac80211: Fix PERR " Masashi Honma
  2026-05-20 11:14 ` [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame Johannes Berg
  5 siblings, 1 reply; 17+ messages in thread
From: Masashi Honma @ 2026-05-15 23:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma

When the AF flag is enabled, hwmp_prep_frame_process() overreads orig_addr
by 2 bytes. Since this occurs within the socket buffer, it does not read across
memory boundaries and therefore poses no security risk; however, we will fix it
as a precaution.

In this fix, a new function mesh_path_parse_reply_frame() is established to
separate the implementation of frame format validation and the check for
unsupported features. This is intended to facilitate future work when
implementing the currently unsupported parts.

Assisted-by: Claude:Sonnet 4.6
Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 19 +++++++++++++++++++
 net/mac80211/mesh_hwmp.c       |  4 ++--
 net/mac80211/parse.c           |  9 +++++++--
 3 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 2ff94da770b1..8c32a52dbd6a 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -355,4 +355,23 @@ static inline bool ieee80211_mesh_preq_size_ok(const u8 *pos, u8 elen)
 	return true;
 }
 
+/* IEEE Std 802.11-2016 9.4.2.114 PREP element */
+static inline bool ieee80211_mesh_prep_size_ok(const u8 *pos, u8 elen)
+{
+	u8 needed;
+
+	/* Check if the element contains flags */
+	if (elen < 1)
+		return false;
+
+	needed = sizeof(struct ieee80211_mesh_hwmp_prep_top) +
+		 (ieee80211_mesh_preq_prep_ae_enabled(pos) ? ETH_ALEN : 0)
+		 /* Target External Address */ +
+		 sizeof(struct ieee80211_mesh_hwmp_prep_bottom);
+	if (elen != needed)
+		return false;
+
+	return true;
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 70973d53605d..fdde827bc1b0 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -950,8 +950,8 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
 						path_metric);
 	}
 	if (elems->prep) {
-		if (elems->prep_len != 31)
-			/* Right now we support no AE */
+		/* Right now we do not support AE (Address Extension) */
+		if (ieee80211_mesh_preq_prep_ae_enabled(elems->prep))
 			goto free;
 		path_metric = hwmp_route_info_get(sdata, mgmt, elems->prep,
 						  MPATH_PREP);
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index 9e52cc48fc18..bbd1e1bc77b4 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -556,8 +556,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
 			}
 			break;
 		case WLAN_EID_PREP:
-			elems->prep = pos;
-			elems->prep_len = elen;
+			if (ieee80211_mesh_prep_size_ok(pos, elen)) {
+				elems->prep = pos;
+				elems->prep_len = elen;
+			} else {
+				elem_parse_failed =
+					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;
+			}
 			break;
 		case WLAN_EID_PERR:
 			elems->perr = pos;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 6/6] wifi: mac80211: Fix PERR frame processing
  2026-05-15 23:38 [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame Masashi Honma
                   ` (3 preceding siblings ...)
  2026-05-15 23:38 ` [PATCH v6 5/6] wifi: mac80211: Fix overread in PREP " Masashi Honma
@ 2026-05-15 23:38 ` Masashi Honma
  2026-05-20 11:14 ` [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame Johannes Berg
  5 siblings, 0 replies; 17+ messages in thread
From: Masashi Honma @ 2026-05-15 23:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma

There are no issues with the PERR processing itself; however, to maintain
consistency with the previous PREQ/PREP code modifications, I will create a new
mesh_path_parse_error_frame() function to separately implement the frame format
validation and the "not supported" check.

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 45 ++++++++++++++++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 14 +++++++++--
 net/mac80211/parse.c           |  9 +++++--
 3 files changed, 64 insertions(+), 4 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 8c32a52dbd6a..2bcd3704ad3b 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -374,4 +374,49 @@ static inline bool ieee80211_mesh_prep_size_ok(const u8 *pos, u8 elen)
 	return true;
 }
 
+/* IEEE Std 802.11-2016 9.4.2.115 PERR element */
+static inline bool ieee80211_mesh_perr_size_ok(const u8 *pos, u8 elen)
+{
+	struct ieee80211_mesh_hwmp_perr *perr_elem =
+		(struct ieee80211_mesh_hwmp_perr *)pos;
+	u8 number_of_dst;
+	u8 needed;
+	const u8 *start;
+	int i;
+
+	start = pos;
+	needed = sizeof(struct ieee80211_mesh_hwmp_perr);
+	pos += sizeof(struct ieee80211_mesh_hwmp_perr);
+
+	/* Check if the element contains number of dst */
+	if (elen < needed)
+		return false;
+
+	number_of_dst = perr_elem->number_of_dst;
+	if (number_of_dst < 1 || number_of_dst > 19)
+		return false;
+
+	for (i = 0; i < number_of_dst; i++) {
+		struct ieee80211_mesh_hwmp_perr_dst *perr_dst =
+			&perr_elem->dsts[i];
+		u8 dst_len;
+
+		/* Check if the element contains flags */
+		if (elen < pos - start + 1)
+			return false;
+
+		dst_len = sizeof(struct ieee80211_mesh_hwmp_perr_dst) +
+			  ((perr_dst->flags & AE_F) ? ETH_ALEN : 0)
+			  /* Destination External Address */ +
+			  2 /* Reason Code */;
+		needed += dst_len;
+		pos += dst_len;
+	}
+
+	if (elen != needed)
+		return false;
+
+	return true;
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index fdde827bc1b0..45ee9ea1ff56 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -960,9 +960,19 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
 						path_metric);
 	}
 	if (elems->perr) {
-		if (elems->perr_len != 15)
-			/* Right now we support only one destination per PERR */
+		struct ieee80211_mesh_hwmp_perr *perr_elem =
+			(struct ieee80211_mesh_hwmp_perr *)elems->perr;
+		int i;
+
+		/* Right now we support only one destination per PERR */
+		if (perr_elem->number_of_dst != 1)
 			goto free;
+
+		/* Right now we do not support AE (Address Extension) */
+		for (i = 0; i < perr_elem->number_of_dst; i++)
+			if (perr_elem->dsts[i].flags & AE_F)
+				goto free;
+
 		hwmp_perr_frame_process(sdata, mgmt, elems->perr);
 	}
 	if (elems->rann)
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index bbd1e1bc77b4..d84e5e12ad24 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -565,8 +565,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
 			}
 			break;
 		case WLAN_EID_PERR:
-			elems->perr = pos;
-			elems->perr_len = elen;
+			if (ieee80211_mesh_perr_size_ok(pos, elen)) {
+				elems->perr = pos;
+				elems->perr_len = elen;
+			} else {
+				elem_parse_failed =
+					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;
+			}
 			break;
 		case WLAN_EID_RANN:
 			if (elen >= sizeof(struct ieee80211_rann_ie))
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame
  2026-05-15 23:38 [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame Masashi Honma
                   ` (4 preceding siblings ...)
  2026-05-15 23:38 ` [PATCH v6 6/6] wifi: mac80211: Fix PERR " Masashi Honma
@ 2026-05-20 11:14 ` Johannes Berg
  2026-05-21  0:42   ` Ping-Ke Shih
  5 siblings, 1 reply; 17+ messages in thread
From: Johannes Berg @ 2026-05-20 11:14 UTC (permalink / raw)
  To: Masashi Honma, linux-wireless

Hi,

So I was tempted to just take this, but I did have a couple of nit
comments and questions, so maybe let's iterate once more.

On Sat, 2026-05-16 at 08:38 +0900, Masashi Honma wrote:
> Signed-off-by: Masashi Honma <masashi.honma@gmail.com>

It would be nice to have a bit more commit message, but yeah, it doesn't
do that much ...

I'd definitely reformat this:

> +static inline struct ieee80211_mesh_hwmp_preq_bottom *
> +	ieee80211_mesh_hwmp_preq_get_bottom(const u8 *ie)

to not have that tab there, and

> +{
> +	struct ieee80211_mesh_hwmp_preq_top *top =
> +		(struct ieee80211_mesh_hwmp_preq_top *)ie;
> +	return (struct ieee80211_mesh_hwmp_preq_bottom *)

to have a blank line between the var declaration and the code.

In this case I'd also be tempted to just use (void *) casts to make it
much shorter, since the other types are implied by the variable/return
types.

> +		struct ieee80211_mesh_hwmp_preq_top *preq_elem_top =
> +			(struct ieee80211_mesh_hwmp_preq_top *)hwmp_ie;
> +		struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
> +			ieee80211_mesh_hwmp_preq_get_bottom(hwmp_ie);

same here, perhaps.

> +		orig_addr = preq_elem_top->orig_addr;
> +		orig_sn = get_unaligned_le32(&preq_elem_top->orig_sn);
> +		orig_lifetime = get_unaligned_le32(&preq_elem_bottom->lifetime);
> +		orig_metric = get_unaligned_le32(&preq_elem_bottom->metric);

Ok, oops, I just realized my other thought on this was wrong - the
previous PREQ_IE_LIFETIME() was u32_field_get() which loaded an entirely
u32 from there using get_unaligned_le32().

However, another comment: You don't need get_unaligned_le32() here since
the struct is __packed, so you can simplify all of these to just

	orig_sn = le32_to_cpu(preq_elem_top->orig_sn);

etc.

> +	target_sn = get_unaligned_le32(&target[0].sn);
> +	orig_sn = get_unaligned_le32(&preq_elem_top->orig_sn);

same throughout wherever you access through a __packed struct. This also
applies to the other patches.

johannes

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 4/6] wifi: mac80211: Fix overread in PREQ frame processing
  2026-05-15 23:38 ` [PATCH v6 4/6] wifi: mac80211: Fix overread in PREQ frame processing Masashi Honma
@ 2026-05-20 11:15   ` Johannes Berg
  2026-05-21  8:55     ` Masashi Honma
  0 siblings, 1 reply; 17+ messages in thread
From: Johannes Berg @ 2026-05-20 11:15 UTC (permalink / raw)
  To: Masashi Honma, linux-wireless

On Sat, 2026-05-16 at 08:38 +0900, Masashi Honma wrote:
> 
> +		/* Right now we only supports 1 target */

nit: "support"

johannes

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 5/6] wifi: mac80211: Fix overread in PREP frame processing
  2026-05-15 23:38 ` [PATCH v6 5/6] wifi: mac80211: Fix overread in PREP " Masashi Honma
@ 2026-05-20 11:16   ` Johannes Berg
  2026-05-21  8:55     ` Masashi Honma
  0 siblings, 1 reply; 17+ messages in thread
From: Johannes Berg @ 2026-05-20 11:16 UTC (permalink / raw)
  To: Masashi Honma, linux-wireless

On Sat, 2026-05-16 at 08:38 +0900, Masashi Honma wrote:
> 
> +/* IEEE Std 802.11-2016 9.4.2.114 PREP element */
> +static inline bool ieee80211_mesh_prep_size_ok(const u8 *pos, u8 elen)
> +{
...

> +	if (elen != needed)
> +		return false;
> +
> +	return true;

nit: maybe just "return elen == needed;"

johannes

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame
  2026-05-20 11:14 ` [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame Johannes Berg
@ 2026-05-21  0:42   ` Ping-Ke Shih
  2026-05-21  6:24     ` Johannes Berg
  0 siblings, 1 reply; 17+ messages in thread
From: Ping-Ke Shih @ 2026-05-21  0:42 UTC (permalink / raw)
  To: Johannes Berg, Masashi Honma, linux-wireless@vger.kernel.org


> > +             orig_addr = preq_elem_top->orig_addr;
> > +             orig_sn = get_unaligned_le32(&preq_elem_top->orig_sn);
> > +             orig_lifetime = get_unaligned_le32(&preq_elem_bottom->lifetime);
> > +             orig_metric = get_unaligned_le32(&preq_elem_bottom->metric);
> 
> Ok, oops, I just realized my other thought on this was wrong - the
> previous PREQ_IE_LIFETIME() was u32_field_get() which loaded an entirely
> u32 from there using get_unaligned_le32().
> 
> However, another comment: You don't need get_unaligned_le32() here since
> the struct is __packed, so you can simplify all of these to just
> 
>         orig_sn = le32_to_cpu(preq_elem_top->orig_sn);
> 

I think the __packed can cause unaligned. Consider the offset below I added. 

struct ieee80211_mesh_hwmp_preq_top {
       u8 flags;					// offset = 0
       u8 hopcount;					// offset = 1
       u8 ttl;						// offset = 2
       __le32 preq_id;				// offset = 3 (unaligned)
       u8 orig_addr[ETH_ALEN];		// offset = 7
       __le32 orig_sn;				// offset = 13 (unaligned)

       /* optional AE, lifetime, metric, target */
       u8 variable[];
} __packed;

Not sure if the pointer preq_elem_top can adjust offset back to be aligned?
(But I think we don't make this assumption.)

Ping-Ke


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame
  2026-05-21  0:42   ` Ping-Ke Shih
@ 2026-05-21  6:24     ` Johannes Berg
  2026-05-21  7:38       ` Ping-Ke Shih
  0 siblings, 1 reply; 17+ messages in thread
From: Johannes Berg @ 2026-05-21  6:24 UTC (permalink / raw)
  To: Ping-Ke Shih, Masashi Honma, linux-wireless@vger.kernel.org

On Thu, 2026-05-21 at 00:42 +0000, Ping-Ke Shih wrote:
> > > +             orig_addr = preq_elem_top->orig_addr;
> > > +             orig_sn = get_unaligned_le32(&preq_elem_top->orig_sn);
> > > +             orig_lifetime = get_unaligned_le32(&preq_elem_bottom->lifetime);
> > > +             orig_metric = get_unaligned_le32(&preq_elem_bottom->metric);
> > 
> > Ok, oops, I just realized my other thought on this was wrong - the
> > previous PREQ_IE_LIFETIME() was u32_field_get() which loaded an entirely
> > u32 from there using get_unaligned_le32().
> > 
> > However, another comment: You don't need get_unaligned_le32() here since
> > the struct is __packed, so you can simplify all of these to just
> > 
> >         orig_sn = le32_to_cpu(preq_elem_top->orig_sn);
> > 
> 
> I think the __packed can cause unaligned.

Of course.

> Not sure if the pointer preq_elem_top can adjust offset back to be aligned?
> (But I think we don't make this assumption.)

No, that's not going to happen.

The point is that __packed forces the compiler to emit code that doesn't
rely on alignment.

So say e.g. you have

	__le32 *ptr = ...;

	u32 val = cpu_to_le32(*ptr);

In this case, the compiler can emit a load instruction that assumes
alignment, so if 'ptr' can be unaligned, we need to use

	u32 val = get_unaligned_le32(ptr);

instead.

However, if we have

	struct ieee80211_mesh_hwmp_preq_top *ptr = ...;

	u32 val = cpu_to_le32(ptr->preq_id);

then the compiler _cannot_ emit a load instruction that assumes
alignment because of the __packed, and so the compiler has to emit a
(perhaps sequence of) instruction(s) that load the 32-bit value without
relying on alignment. As a consequence, we don't have to explicitly
write it the more complicated way and can just write it the more natural
way.

johannes

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame
  2026-05-21  6:24     ` Johannes Berg
@ 2026-05-21  7:38       ` Ping-Ke Shih
  2026-05-21  7:42         ` Johannes Berg
  0 siblings, 1 reply; 17+ messages in thread
From: Ping-Ke Shih @ 2026-05-21  7:38 UTC (permalink / raw)
  To: Johannes Berg, Masashi Honma, linux-wireless@vger.kernel.org


Johannes Berg <johannes@sipsolutions.net> wrote:
> The point is that __packed forces the compiler to emit code that doesn't
> rely on alignment.

I was not aware that __packed is a hint of unalignment to compiler.
I learned. :)

> 
> So say e.g. you have
> 
>         __le32 *ptr = ...;
> 
>         u32 val = cpu_to_le32(*ptr);
> 
> In this case, the compiler can emit a load instruction that assumes
> alignment, so if 'ptr' can be unaligned, we need to use
> 
>         u32 val = get_unaligned_le32(ptr);

Got it.
With the assignment to pointer, the hint disappeared. 

> 
> instead.
> 
> However, if we have
> 
>         struct ieee80211_mesh_hwmp_preq_top *ptr = ...;
> 
>         u32 val = cpu_to_le32(ptr->preq_id);
> 
> then the compiler _cannot_ emit a load instruction that assumes
> alignment because of the __packed, and so the compiler has to emit a
> (perhaps sequence of) instruction(s) that load the 32-bit value without
> relying on alignment. As a consequence, we don't have to explicitly
> write it the more complicated way and can just write it the more natural
> way.

I'll remember this is better style in practice. Compiler can help.


In my tests with arm-gcc compiler, I did a special case: 

struct foo {
	int a;
	char b;
} __packed;

int bar(struct foo *foo)
{
	return foo->a;
}

It is obviously aligned (offset = 0), so I guessed arm-gcc can generate
a single load instruction, but actually it doesn't (even with -O3).
But this is not the course of this thread. :)

Thank you
Ping-Ke


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame
  2026-05-21  7:38       ` Ping-Ke Shih
@ 2026-05-21  7:42         ` Johannes Berg
  2026-05-21  7:53           ` Ping-Ke Shih
  0 siblings, 1 reply; 17+ messages in thread
From: Johannes Berg @ 2026-05-21  7:42 UTC (permalink / raw)
  To: Ping-Ke Shih, Masashi Honma, linux-wireless@vger.kernel.org

On Thu, 2026-05-21 at 07:38 +0000, Ping-Ke Shih wrote:
> Johannes Berg <johannes@sipsolutions.net> wrote:
> > The point is that __packed forces the compiler to emit code that doesn't
> > rely on alignment.
> 
> I was not aware that __packed is a hint of unalignment to compiler.
> I learned. :)

Yes. Note it goes further:

> In my tests with arm-gcc compiler, I did a special case: 
> 
> struct foo {
> 	int a;
> 	char b;
> } __packed;
> 
> int bar(struct foo *foo)
> {
> 	return foo->a;
> }
> 
> It is obviously aligned (offset = 0), so I guessed arm-gcc can generate
> a single load instruction, but actually it doesn't (even with -O3).

No, it cannot, because in addition it has to assume the pointer 'foo'
itself has no alignment. We use this a lot too, since a struct like your
'struct foo' could be in a frame element where we don't know what came
before it while parsing the elements, so we don't know that 'foo' has
any alignment.

The reasons are related to what happens when you have a

	struct foo array[N];

I think, because then __packed means there's no padding at the end of
'struct foo' either, and array[1] is a completely unaligned pointer.

So no, the compiler couldn't assume alignment even for fields that look
aligned within 'struct foo'.

johannes

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame
  2026-05-21  7:42         ` Johannes Berg
@ 2026-05-21  7:53           ` Ping-Ke Shih
  2026-05-21  8:54             ` Masashi Honma
  0 siblings, 1 reply; 17+ messages in thread
From: Ping-Ke Shih @ 2026-05-21  7:53 UTC (permalink / raw)
  To: Johannes Berg, Masashi Honma, linux-wireless@vger.kernel.org

Johannes Berg <johannes@sipsolutions.net> wrote:
> > In my tests with arm-gcc compiler, I did a special case:
> >
> > struct foo {
> >       int a;
> >       char b;
> > } __packed;
> >
> > int bar(struct foo *foo)
> > {
> >       return foo->a;
> > }
> >
> > It is obviously aligned (offset = 0), so I guessed arm-gcc can generate
> > a single load instruction, but actually it doesn't (even with -O3).
> 
> No, it cannot, because in addition it has to assume the pointer 'foo'
> itself has no alignment. We use this a lot too, since a struct like your
> 'struct foo' could be in a frame element where we don't know what came
> before it while parsing the elements, so we don't know that 'foo' has
> any alignment.
> 
> The reasons are related to what happens when you have a
> 
>         struct foo array[N];
> 
> I think, because then __packed means there's no padding at the end of
> 'struct foo' either, and array[1] is a completely unaligned pointer.
> 
> So no, the compiler couldn't assume alignment even for fields that look
> aligned within 'struct foo'.

I learned more. Super thank you. :)

Ping-Ke


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame
  2026-05-21  7:53           ` Ping-Ke Shih
@ 2026-05-21  8:54             ` Masashi Honma
  0 siblings, 0 replies; 17+ messages in thread
From: Masashi Honma @ 2026-05-21  8:54 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless@vger.kernel.org, Ping-Ke Shih

Thank you so much. I fixed those.

2026年5月21日(木) 16:53 Ping-Ke Shih <pkshih@realtek.com>:
>
> Johannes Berg <johannes@sipsolutions.net> wrote:
> > > In my tests with arm-gcc compiler, I did a special case:
> > >
> > > struct foo {
> > >       int a;
> > >       char b;
> > > } __packed;
> > >
> > > int bar(struct foo *foo)
> > > {
> > >       return foo->a;
> > > }
> > >
> > > It is obviously aligned (offset = 0), so I guessed arm-gcc can generate
> > > a single load instruction, but actually it doesn't (even with -O3).
> >
> > No, it cannot, because in addition it has to assume the pointer 'foo'
> > itself has no alignment. We use this a lot too, since a struct like your
> > 'struct foo' could be in a frame element where we don't know what came
> > before it while parsing the elements, so we don't know that 'foo' has
> > any alignment.
> >
> > The reasons are related to what happens when you have a
> >
> >         struct foo array[N];
> >
> > I think, because then __packed means there's no padding at the end of
> > 'struct foo' either, and array[1] is a completely unaligned pointer.
> >
> > So no, the compiler couldn't assume alignment even for fields that look
> > aligned within 'struct foo'.
>
> I learned more. Super thank you. :)
>
> Ping-Ke
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 4/6] wifi: mac80211: Fix overread in PREQ frame processing
  2026-05-20 11:15   ` Johannes Berg
@ 2026-05-21  8:55     ` Masashi Honma
  0 siblings, 0 replies; 17+ messages in thread
From: Masashi Honma @ 2026-05-21  8:55 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

Thank you, I really need to use LLM to write comment :)

2026年5月20日(水) 20:15 Johannes Berg <johannes@sipsolutions.net>:
>
> On Sat, 2026-05-16 at 08:38 +0900, Masashi Honma wrote:
> >
> > +             /* Right now we only supports 1 target */
>
> nit: "support"
>
> johannes

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 5/6] wifi: mac80211: Fix overread in PREP frame processing
  2026-05-20 11:16   ` Johannes Berg
@ 2026-05-21  8:55     ` Masashi Honma
  0 siblings, 0 replies; 17+ messages in thread
From: Masashi Honma @ 2026-05-21  8:55 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

Thank you, fixed other _size_of functions as well.

2026年5月20日(水) 20:16 Johannes Berg <johannes@sipsolutions.net>:
>
> On Sat, 2026-05-16 at 08:38 +0900, Masashi Honma wrote:
> >
> > +/* IEEE Std 802.11-2016 9.4.2.114 PREP element */
> > +static inline bool ieee80211_mesh_prep_size_ok(const u8 *pos, u8 elen)
> > +{
> ...
>
> > +     if (elen != needed)
> > +             return false;
> > +
> > +     return true;
>
> nit: maybe just "return elen == needed;"
>
> johannes

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2026-05-21  8:56 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-15 23:38 [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame Masashi Honma
2026-05-15 23:38 ` [PATCH v6 2/6] wifi: mac80211: Use struct instead of macro for PREP frame Masashi Honma
2026-05-15 23:38 ` [PATCH v6 3/6] wifi: mac80211: Use struct instead of macro for PERR frame Masashi Honma
2026-05-15 23:38 ` [PATCH v6 4/6] wifi: mac80211: Fix overread in PREQ frame processing Masashi Honma
2026-05-20 11:15   ` Johannes Berg
2026-05-21  8:55     ` Masashi Honma
2026-05-15 23:38 ` [PATCH v6 5/6] wifi: mac80211: Fix overread in PREP " Masashi Honma
2026-05-20 11:16   ` Johannes Berg
2026-05-21  8:55     ` Masashi Honma
2026-05-15 23:38 ` [PATCH v6 6/6] wifi: mac80211: Fix PERR " Masashi Honma
2026-05-20 11:14 ` [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame Johannes Berg
2026-05-21  0:42   ` Ping-Ke Shih
2026-05-21  6:24     ` Johannes Berg
2026-05-21  7:38       ` Ping-Ke Shih
2026-05-21  7:42         ` Johannes Berg
2026-05-21  7:53           ` Ping-Ke Shih
2026-05-21  8:54             ` Masashi Honma

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox