]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Sun, 28 Jun 2026 03:32:56 +0000 (23:32 -0400)
committerSasha Levin <sashal@kernel.org>
Sun, 28 Jun 2026 03:32:56 +0000 (23:32 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
185 files changed:
queue-5.10/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch [new file with mode: 0644]
queue-5.10/batman-adv-dat-prevent-false-sharing-between-vlans.patch [new file with mode: 0644]
queue-5.10/batman-adv-frag-avoid-underflow-of-ttl.patch [new file with mode: 0644]
queue-5.10/batman-adv-frag-ensure-fragment-is-writable-before-m.patch [new file with mode: 0644]
queue-5.10/batman-adv-prevent-elp-transmission-interval-underfl.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-avoid-window-underflow.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-fix-fast-recovery-precondition.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-handle-overlapping-packets.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-initialize-last_recv_time-during.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-prevent-parallel-modifications-o.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch [new file with mode: 0644]
queue-5.10/batman-adv-tt-don-t-merge-change-entries-with-differ.patch [new file with mode: 0644]
queue-5.10/batman-adv-tt-track-roam-count-per-vid.patch [new file with mode: 0644]
queue-5.10/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch [new file with mode: 0644]
queue-5.10/batman-adv-tvlv-enforce-2-byte-alignment.patch [new file with mode: 0644]
queue-5.10/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch [new file with mode: 0644]
queue-5.10/series
queue-5.15/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch [new file with mode: 0644]
queue-5.15/batman-adv-dat-prevent-false-sharing-between-vlans.patch [new file with mode: 0644]
queue-5.15/batman-adv-ensure-bcast-is-writable-before-modifying.patch [new file with mode: 0644]
queue-5.15/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch [new file with mode: 0644]
queue-5.15/batman-adv-frag-avoid-underflow-of-ttl.patch [new file with mode: 0644]
queue-5.15/batman-adv-frag-ensure-fragment-is-writable-before-m.patch [new file with mode: 0644]
queue-5.15/batman-adv-prevent-elp-transmission-interval-underfl.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-avoid-window-underflow.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-fix-fast-recovery-precondition.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-handle-overlapping-packets.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-initialize-last_recv_time-during.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-prevent-parallel-modifications-o.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch [new file with mode: 0644]
queue-5.15/batman-adv-tt-don-t-merge-change-entries-with-differ.patch [new file with mode: 0644]
queue-5.15/batman-adv-tt-track-roam-count-per-vid.patch [new file with mode: 0644]
queue-5.15/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch [new file with mode: 0644]
queue-5.15/batman-adv-tvlv-enforce-2-byte-alignment.patch [new file with mode: 0644]
queue-5.15/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch [new file with mode: 0644]
queue-5.15/series
queue-6.1/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch [new file with mode: 0644]
queue-6.1/batman-adv-dat-prevent-false-sharing-between-vlans.patch [new file with mode: 0644]
queue-6.1/batman-adv-ensure-bcast-is-writable-before-modifying.patch [new file with mode: 0644]
queue-6.1/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch [new file with mode: 0644]
queue-6.1/batman-adv-frag-avoid-underflow-of-ttl.patch [new file with mode: 0644]
queue-6.1/batman-adv-frag-ensure-fragment-is-writable-before-m.patch [new file with mode: 0644]
queue-6.1/batman-adv-prevent-elp-transmission-interval-underfl.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-avoid-window-underflow.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-fix-fast-recovery-precondition.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-handle-overlapping-packets.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-initialize-last_recv_time-during.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-prevent-parallel-modifications-o.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch [new file with mode: 0644]
queue-6.1/batman-adv-tt-don-t-merge-change-entries-with-differ.patch [new file with mode: 0644]
queue-6.1/batman-adv-tt-track-roam-count-per-vid.patch [new file with mode: 0644]
queue-6.1/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch [new file with mode: 0644]
queue-6.1/batman-adv-tvlv-enforce-2-byte-alignment.patch [new file with mode: 0644]
queue-6.1/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch [new file with mode: 0644]
queue-6.1/series
queue-6.12/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch [new file with mode: 0644]
queue-6.12/batman-adv-dat-prevent-false-sharing-between-vlans.patch [new file with mode: 0644]
queue-6.12/batman-adv-ensure-bcast-is-writable-before-modifying.patch [new file with mode: 0644]
queue-6.12/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch [new file with mode: 0644]
queue-6.12/batman-adv-frag-avoid-underflow-of-ttl.patch [new file with mode: 0644]
queue-6.12/batman-adv-frag-ensure-fragment-is-writable-before-m.patch [new file with mode: 0644]
queue-6.12/batman-adv-prevent-elp-transmission-interval-underfl.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-avoid-window-underflow.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-fix-fast-recovery-precondition.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-handle-overlapping-packets.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-initialize-last_recv_time-during.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-prevent-parallel-modifications-o.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch [new file with mode: 0644]
queue-6.12/batman-adv-tt-don-t-merge-change-entries-with-differ.patch [new file with mode: 0644]
queue-6.12/batman-adv-tt-track-roam-count-per-vid.patch [new file with mode: 0644]
queue-6.12/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch [new file with mode: 0644]
queue-6.12/batman-adv-tvlv-enforce-2-byte-alignment.patch [new file with mode: 0644]
queue-6.12/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch [new file with mode: 0644]
queue-6.12/series
queue-6.18/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch [new file with mode: 0644]
queue-6.18/batman-adv-dat-prevent-false-sharing-between-vlans.patch [new file with mode: 0644]
queue-6.18/batman-adv-ensure-bcast-is-writable-before-modifying.patch [new file with mode: 0644]
queue-6.18/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch [new file with mode: 0644]
queue-6.18/batman-adv-frag-avoid-underflow-of-ttl.patch [new file with mode: 0644]
queue-6.18/batman-adv-frag-ensure-fragment-is-writable-before-m.patch [new file with mode: 0644]
queue-6.18/batman-adv-gw-don-t-deselect-gateway-with-active-har.patch [new file with mode: 0644]
queue-6.18/batman-adv-prevent-elp-transmission-interval-underfl.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-avoid-window-underflow.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-fix-fast-recovery-precondition.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-handle-overlapping-packets.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-initialize-last_recv_time-during.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-prevent-parallel-modifications-o.patch [new file with mode: 0644]
queue-6.18/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch [new file with mode: 0644]
queue-6.18/batman-adv-tt-don-t-merge-change-entries-with-differ.patch [new file with mode: 0644]
queue-6.18/batman-adv-tt-track-roam-count-per-vid.patch [new file with mode: 0644]
queue-6.18/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch [new file with mode: 0644]
queue-6.18/batman-adv-tvlv-enforce-2-byte-alignment.patch [new file with mode: 0644]
queue-6.18/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch [new file with mode: 0644]
queue-6.18/series
queue-6.6/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch [new file with mode: 0644]
queue-6.6/batman-adv-dat-prevent-false-sharing-between-vlans.patch [new file with mode: 0644]
queue-6.6/batman-adv-ensure-bcast-is-writable-before-modifying.patch [new file with mode: 0644]
queue-6.6/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch [new file with mode: 0644]
queue-6.6/batman-adv-frag-avoid-underflow-of-ttl.patch [new file with mode: 0644]
queue-6.6/batman-adv-frag-ensure-fragment-is-writable-before-m.patch [new file with mode: 0644]
queue-6.6/batman-adv-prevent-elp-transmission-interval-underfl.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-avoid-window-underflow.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-fix-fast-recovery-precondition.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-handle-overlapping-packets.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-initialize-last_recv_time-during.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-prevent-parallel-modifications-o.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch [new file with mode: 0644]
queue-6.6/batman-adv-tt-don-t-merge-change-entries-with-differ.patch [new file with mode: 0644]
queue-6.6/batman-adv-tt-track-roam-count-per-vid.patch [new file with mode: 0644]
queue-6.6/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch [new file with mode: 0644]
queue-6.6/batman-adv-tvlv-enforce-2-byte-alignment.patch [new file with mode: 0644]
queue-6.6/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch [new file with mode: 0644]
queue-6.6/fs-prepare-for-adding-lsm-blob-to-backing_file.patch [new file with mode: 0644]
queue-6.6/lsm-add-backing_file-lsm-hooks.patch [new file with mode: 0644]
queue-6.6/selinux-fix-overlayfs-mmap-and-mprotect-access-check.patch [new file with mode: 0644]
queue-6.6/series
queue-7.1/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch [new file with mode: 0644]
queue-7.1/batman-adv-dat-prevent-false-sharing-between-vlans.patch [new file with mode: 0644]
queue-7.1/batman-adv-ensure-bcast-is-writable-before-modifying.patch [new file with mode: 0644]
queue-7.1/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch [new file with mode: 0644]
queue-7.1/batman-adv-frag-avoid-underflow-of-ttl.patch [new file with mode: 0644]
queue-7.1/batman-adv-frag-ensure-fragment-is-writable-before-m.patch [new file with mode: 0644]
queue-7.1/batman-adv-gw-don-t-deselect-gateway-with-active-har.patch [new file with mode: 0644]
queue-7.1/batman-adv-prevent-elp-transmission-interval-underfl.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-avoid-window-underflow.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-fix-fast-recovery-precondition.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-handle-overlapping-packets.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-initialize-last_recv_time-during.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-prevent-parallel-modifications-o.patch [new file with mode: 0644]
queue-7.1/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch [new file with mode: 0644]
queue-7.1/batman-adv-tt-don-t-merge-change-entries-with-differ.patch [new file with mode: 0644]
queue-7.1/batman-adv-tt-track-roam-count-per-vid.patch [new file with mode: 0644]
queue-7.1/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch [new file with mode: 0644]
queue-7.1/batman-adv-tvlv-enforce-2-byte-alignment.patch [new file with mode: 0644]
queue-7.1/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch [new file with mode: 0644]
queue-7.1/series

diff --git a/queue-5.10/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-5.10/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
new file mode 100644 (file)
index 0000000..d96c90e
--- /dev/null
@@ -0,0 +1,156 @@
+From 836f1b3edeffe28bc8547cde960bed26f4936979 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:38 +0200
+Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream.
+
+The lasttime field for claim, backbone_gw, and loopdetect tracks the
+jiffies value of the most recent activity and is used to detect timeouts.
+These accesses are not consistently protected by a lock, so
+READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler
+optimizations.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++-------------
+ 1 file changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index 00185ed4940f23..5593ac0aae378a 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -517,7 +517,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig,
+               return NULL;
+       entry->vid = vid;
+-      entry->lasttime = jiffies;
++      WRITE_ONCE(entry->lasttime, jiffies);
+       entry->crc = BATADV_BLA_CRC_INIT;
+       entry->bat_priv = bat_priv;
+       spin_lock_init(&entry->crc_lock);
+@@ -585,7 +585,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv,
+       if (unlikely(!backbone_gw))
+               return;
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       batadv_backbone_gw_put(backbone_gw);
+ }
+@@ -719,7 +719,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+               ether_addr_copy(claim->addr, mac);
+               spin_lock_init(&claim->backbone_lock);
+               claim->vid = vid;
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               kref_get(&backbone_gw->refcount);
+               claim->backbone_gw = backbone_gw;
+               kref_init(&claim->refcount);
+@@ -741,7 +741,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+                       return;
+               }
+       } else {
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               if (claim->backbone_gw == backbone_gw)
+                       /* no need to register a new backbone */
+                       goto claim_free_ref;
+@@ -774,7 +774,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+       spin_lock_bh(&backbone_gw->crc_lock);
+       backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+ claim_free_ref:
+       batadv_claim_put(claim);
+@@ -863,7 +863,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
+               return true;
+       /* handle as ANNOUNCE frame */
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       crc = ntohs(*((__force __be16 *)(&an_addr[4])));
+       batadv_dbg(BATADV_DBG_BLA, bat_priv,
+@@ -1258,7 +1258,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
+                                                 head, hash_entry) {
+                               if (now)
+                                       goto purge_now;
+-                              if (!batadv_has_timed_out(backbone_gw->lasttime,
++                              if (!batadv_has_timed_out(READ_ONCE(backbone_gw->lasttime),
+                                                         BATADV_BLA_BACKBONE_TIMEOUT))
+                                       continue;
+@@ -1339,7 +1339,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
+                                               primary_if->net_dev->dev_addr))
+                               goto skip;
+-                      if (!batadv_has_timed_out(claim->lasttime,
++                      if (!batadv_has_timed_out(READ_ONCE(claim->lasttime),
+                                                 BATADV_BLA_CLAIM_TIMEOUT))
+                               goto skip;
+@@ -1499,7 +1499,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+               eth_random_addr(bat_priv->bla.loopdetect_addr);
+               bat_priv->bla.loopdetect_addr[0] = 0xba;
+               bat_priv->bla.loopdetect_addr[1] = 0xbe;
+-              bat_priv->bla.loopdetect_lasttime = jiffies;
++              WRITE_ONCE(bat_priv->bla.loopdetect_lasttime, jiffies);
+               atomic_set(&bat_priv->bla.loopdetect_next,
+                          BATADV_BLA_LOOPDETECT_PERIODS);
+@@ -1520,7 +1520,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                                               primary_if->net_dev->dev_addr))
+                               continue;
+-                      backbone_gw->lasttime = jiffies;
++                      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+                       batadv_bla_send_announce(bat_priv, backbone_gw);
+                       if (send_loopdetect)
+@@ -1907,7 +1907,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       /* If the packet came too late, don't forward it on the mesh
+        * but don't consider that as loop. It might be a coincidence.
+        */
+-      if (batadv_has_timed_out(bat_priv->bla.loopdetect_lasttime,
++      if (batadv_has_timed_out(READ_ONCE(bat_priv->bla.loopdetect_lasttime),
+                                BATADV_BLA_LOOPDETECT_TIMEOUT))
+               return true;
+@@ -2023,7 +2023,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       if (own_claim) {
+               /* ... allow it in any case */
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               goto allow;
+       }
+@@ -2127,7 +2127,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+               /* if yes, the client has roamed and we have
+                * to unclaim it.
+                */
+-              if (batadv_has_timed_out(claim->lasttime, 100)) {
++              if (batadv_has_timed_out(READ_ONCE(claim->lasttime), 100)) {
+                       /* only unclaim if the last claim entry is
+                        * older than 100 ms to make sure we really
+                        * have a roaming client here.
+@@ -2513,7 +2513,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid,
+       backbone_crc = backbone_gw->crc;
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime);
++      msecs = jiffies_to_msecs(jiffies - READ_ONCE(backbone_gw->lasttime));
+       if (is_own)
+               if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) {
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-5.10/batman-adv-dat-prevent-false-sharing-between-vlans.patch
new file mode 100644 (file)
index 0000000..b01961d
--- /dev/null
@@ -0,0 +1,57 @@
+From d4205c11455b8ac4b18ee868a6d8dacf11d0dc13 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:50 +0200
+Subject: batman-adv: dat: prevent false sharing between VLANs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream.
+
+The local hash of DAT entries is supposed to be VLAN (VID) aware. But
+the adding to the hash and the search in the hash were not checking the VID
+information of the hash entries. The entries would therefore only be
+correctly separated when batadv_hash_dat() didn't select the same buckets
+for different VIDs.
+
+Cc: stable@kernel.org
+Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/distributed-arp-table.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
+index 9d27d7d7b2b4b4..592b61a2e7911e 100644
+--- a/net/batman-adv/distributed-arp-table.c
++++ b/net/batman-adv/distributed-arp-table.c
+@@ -216,10 +216,13 @@ static void batadv_dat_purge(struct work_struct *work)
+  */
+ static bool batadv_compare_dat(const struct hlist_node *node, const void *data2)
+ {
+-      const void *data1 = container_of(node, struct batadv_dat_entry,
+-                                       hash_entry);
++      const struct batadv_dat_entry *entry1;
++      const struct batadv_dat_entry *entry2;
+-      return memcmp(data1, data2, sizeof(__be32)) == 0;
++      entry1 = container_of(node, struct batadv_dat_entry, hash_entry);
++      entry2 = data2;
++
++      return entry1->ip == entry2->ip && entry1->vid == entry2->vid;
+ }
+ /**
+@@ -346,6 +349,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
+               if (dat_entry->ip != ip)
+                       continue;
++              if (dat_entry->vid != vid)
++                      continue;
++
+               if (!kref_get_unless_zero(&dat_entry->refcount))
+                       continue;
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-5.10/batman-adv-frag-avoid-underflow-of-ttl.patch
new file mode 100644 (file)
index 0000000..f394809
--- /dev/null
@@ -0,0 +1,45 @@
+From 6a5ed9c966eba4121f22365627df5eb721c4825e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:42 +0200
+Subject: batman-adv: frag: avoid underflow of TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream.
+
+Packets with a TTL are using it to limit the amount of time this packet can
+be forwarded. But for batadv_frag_packet, the TTL was always only reduced
+but it was never evaluated. It could even underflow without any effect.
+
+Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for
+forwarding. This keeps it in sync with the not fragmented unicast packet.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index 95c88bbdbcbee0..43dfe86f07a9b1 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -421,6 +421,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (packet->ttl < 2) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
+               if (skb_cow(skb, ETH_HLEN) < 0) {
+                       kfree_skb(skb);
+                       *rx_result = NET_RX_DROP;
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-5.10/batman-adv-frag-ensure-fragment-is-writable-before-m.patch
new file mode 100644 (file)
index 0000000..d01f741
--- /dev/null
@@ -0,0 +1,107 @@
+From 5b751cd0ff9e9ba5ef494a22562e732cf0e28e10 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:41 +0200
+Subject: batman-adv: frag: ensure fragment is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or use skb_cow() to ensure that the data part is not
+shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to forward the
+fragment.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 15 ++++++++++++++-
+ net/batman-adv/fragmentation.h |  3 ++-
+ net/batman-adv/routing.c       |  3 +--
+ 3 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index 724e06e3e799f4..95c88bbdbcbee0 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -386,6 +386,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  * @skb: skb to forward
+  * @recv_if: interface that the skb is received on
+  * @orig_node_src: originator that the skb is received from
++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and
++ *  NET_RX_DROP when it was dropped; only valid when true is returned
+  *
+  * Look up the next-hop of the fragments payload and check if the merged packet
+  * will exceed the MTU towards the next-hop. If so, the fragment is forwarded
+@@ -395,7 +397,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  */
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src)
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
+       struct batadv_orig_node *orig_node_dst;
+@@ -418,12 +421,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (skb_cow(skb, ETH_HLEN) < 0) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
++              packet = (struct batadv_frag_packet *)skb->data;
++
+               batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
+               batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
+                                  skb->len + ETH_HLEN);
+               packet->ttl--;
+               batadv_send_unicast_skb(skb, neigh_node);
++              *rx_result = NET_RX_SUCCESS;
+               ret = true;
+       }
+diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h
+index 881ef328b6cd7e..aba6eecb7d0e76 100644
+--- a/net/batman-adv/fragmentation.h
++++ b/net/batman-adv/fragmentation.h
+@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig,
+                           bool (*check_cb)(struct batadv_frag_table_entry *));
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src);
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result);
+ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+                           struct batadv_orig_node *orig_node);
+ int batadv_frag_send_packet(struct sk_buff *skb,
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 9e5c71e406ffc6..f6df66a534c41e 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1143,10 +1143,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb,
+       /* Route the fragment if it is not for us and too big to be merged. */
+       if (!batadv_is_my_mac(bat_priv, frag_packet->dest) &&
+-          batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) {
++          batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) {
+               /* skb was consumed */
+               skb = NULL;
+-              ret = NET_RX_SUCCESS;
+               goto put_orig_node;
+       }
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-5.10/batman-adv-prevent-elp-transmission-interval-underfl.patch
new file mode 100644 (file)
index 0000000..c803a00
--- /dev/null
@@ -0,0 +1,52 @@
+From 19eb8cbdb04997e62a78049f820257384df1ab95 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:39 +0200
+Subject: batman-adv: prevent ELP transmission interval underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream.
+
+batadv_v_elp_start_timer() enqeues a delayed work. The time when it starts
+is randomly chosen between (elp_interval - BATADV_JITTER) and
+(elp_interval + BATADV_JITTER). The configured elp_interval must therefore
+be larger or equal to BATADV_JITTER to avoid that it causes an underflow of
+the unsigned integer. If this would happen, then a "fast" ELP interval
+would turn into a "day long" delay.
+
+At the same time, it must not be larger than the maximum value the variable
+can store.
+
+Cc: stable@kernel.org
+Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/netlink.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
+index 0b5cb03859b25b..e5a0b2c97162d2 100644
+--- a/net/batman-adv/netlink.c
++++ b/net/batman-adv/netlink.c
+@@ -939,9 +939,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb,
+ #ifdef CONFIG_BATMAN_ADV_BATMAN_V
+       if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) {
++              u32 elp_interval;
++
+               attr = info->attrs[BATADV_ATTR_ELP_INTERVAL];
++              elp_interval = nla_get_u32(attr);
++
++              elp_interval = min_t(u32, elp_interval, INT_MAX);
++              elp_interval = max_t(u32, elp_interval, BATADV_JITTER);
+-              atomic_set(&hard_iface->bat_v.elp_interval, nla_get_u32(attr));
++              atomic_set(&hard_iface->bat_v.elp_interval, elp_interval);
+       }
+       if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) {
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-5.10/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
new file mode 100644 (file)
index 0000000..f7eea35
--- /dev/null
@@ -0,0 +1,74 @@
+From 3fa68ff67e2280c13d117673f9723013154072b7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:37 +0200
+Subject: batman-adv: tp_meter: add only finished tp_vars to lists
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream.
+
+When the receiver variables (aka "session") are initialized, then they are
+added to the list of sessions before the timer is set up. A RCU protected
+reader could therefore find the entry and run mod_setup before
+batadv_tp_init_recv() finished the timer initialization.
+
+The same is true for batadv_tp_start(), which must first initialize the
+finish_work and the test_length to avoid a similar problem.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index a4df826e4ece71..a015ff73812f5e 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1106,21 +1106,21 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->prerandom_offset = 0;
+       spin_lock_init(&tp_vars->prerandom_lock);
+-      kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+-      spin_unlock_bh(&bat_priv->tp_list_lock);
+-
+       tp_vars->test_length = test_length;
+       if (!tp_vars->test_length)
+               tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH;
++      /* init work item for finished tp tests */
++      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
++
++      kref_get(&tp_vars->refcount);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      spin_unlock_bh(&bat_priv->tp_list_lock);
++
+       batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                  "Meter: starting throughput meter towards %pM (length=%ums)\n",
+                  dst, test_length);
+-      /* init work item for finished tp tests */
+-      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
+-
+       /* start tp kthread. This way the write() call issued from userspace can
+        * happily return and avoid to block
+        */
+@@ -1438,10 +1438,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
+       kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+       kref_get(&tp_vars->refcount);
+-      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+       batadv_tp_reset_receiver_timer(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-5.10/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
new file mode 100644 (file)
index 0000000..eb510ef
--- /dev/null
@@ -0,0 +1,66 @@
+From b6e51ec44a0c883b75f7e67cbe4d9601b984d298 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:45 +0200
+Subject: batman-adv: tp_meter: annotate last_recv_time access with
+ READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream.
+
+The last_recv_time field for batadv_tp_receiver tracks the jiffies value of
+the most recent activity and is used to detect timeouts. These accesses are
+not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used
+to prevent data races caused by compiler optimizations.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index c7de8dfe9b65bd..91392e48514d64 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1193,7 +1193,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       bat_priv = tp_vars->bat_priv;
+       /* if there is recent activity rearm the timer */
+-      if (!batadv_has_timed_out(tp_vars->last_recv_time,
++      if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time),
+                                 BATADV_TP_RECV_TIMEOUT)) {
+               /* reset the receiver shutdown timer */
+               batadv_tp_reset_receiver_timer(tp_vars);
+@@ -1433,7 +1433,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+       if (tp_vars) {
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+               goto out_unlock;
+       }
+@@ -1463,7 +1463,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+-      tp_vars->last_recv_time = jiffies;
++      WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1514,7 +1514,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                       goto out;
+               }
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-5.10/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
new file mode 100644 (file)
index 0000000..58c4389
--- /dev/null
@@ -0,0 +1,64 @@
+From 089534cc33b4073c018a8ad5c383cd365a8ce1ec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:34 +0200
+Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream.
+
+The cwnd is always MSS <= cwnd <= 0x20000000. But the calculation in
+batadv_tp_update_cwnd() assumes unsigned 32 bit arithmetics.
+
+    ((mss * 8) ** 2) / (cwnd * 8)
+
+In case cwnd is actually 0x20000000, it will be shifted by 3 bit to the
+left end up at 0x100000000 or U32_MAX + 1. It will therefore wrap around
+and be 0 - resulting in:
+
+    ((mss * 8) ** 2) / 0
+
+This is of course invalid and cannot be calculated. The calculation should
+must be simplified to avoid this overflow:
+
+   (mss ** 2) * 8 / cwnd
+
+It will keep the precision enhancement from the scaling (by 8) but avoid
+the overflow in the divisor.
+
+In theory, there could still be an overflow in the dividend. It is at the
+moment fixed to BATADV_TP_PLEN in batadv_tp_recv_ack() - so it is not an
+imminent problem. But allowing it to use the whole u32 bit range, would
+mean that it can still use up to 67 bits. To keep this calculation safe for
+32 bit arithmetic, mss must never use more than floor((32 - 3) / 2) bits -
+or in other words: must never be larger than 16383.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 10953096996ab1..118cb5dd285c2d 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -153,9 +153,12 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss)
+               return;
+       }
++      /* prevent overflow in (mss * mss) << 3 */
++      mss = min_t(u32, mss, (1U << 14) - 1);
++
+       /* increment CWND at least of 1 (section 3.1 of RFC5681) */
+       tp_vars->dec_cwnd += max_t(u32, 1U << 3,
+-                                 ((mss * mss) << 6) / (tp_vars->cwnd << 3));
++                                 ((mss * mss) << 3) / tp_vars->cwnd);
+       if (tp_vars->dec_cwnd < (mss << 3)) {
+               spin_unlock_bh(&tp_vars->cwnd_lock);
+               return;
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-5.10/batman-adv-tp_meter-avoid-window-underflow.patch
new file mode 100644 (file)
index 0000000..bbd56dd
--- /dev/null
@@ -0,0 +1,56 @@
+From 9f8607e45084aa12a17ab12b562ba613bc0dda79 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:33 +0200
+Subject: batman-adv: tp_meter: avoid window underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream.
+
+In batadv_tp_avail(), win_left is calculated with 32-bit unsigned
+arithmetic: win_left = win_limit - tp_vars->last_sent;
+
+During Fast Recovery, cwnd is inflated and last_sent advances rapidly. When
+Fast Recovery ends, cwnd drops abruptly back to ss_threshold. If the newly
+shrunk win_limit is less than last_sent, the unsigned subtraction will
+underflow, wrapping to a massive positive value. Instead of returning that
+the window is full (unavailable), it returns that the sender can continue
+sending.
+
+To handle this situation, it must be checked whether the windows end
+sequence number (win_limit) has to be compared with the last sent sequence
+number. If it would be before the last sent sequence number, then more acks
+are needed before the transmission can be started again.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index d8ad58ccef2608..10953096996ab1 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -825,10 +825,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+ static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars,
+                           size_t payload_len)
+ {
++      u32 last_sent = READ_ONCE(tp_vars->last_sent);
+       u32 win_left, win_limit;
+       win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd;
+-      win_left = win_limit - tp_vars->last_sent;
++
++      if (batadv_seq_before(last_sent, win_limit))
++              win_left = win_limit - last_sent;
++      else
++              win_left = 0;
+       return win_left >= payload_len;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-5.10/batman-adv-tp_meter-fix-fast-recovery-precondition.patch
new file mode 100644 (file)
index 0000000..6a8c193
--- /dev/null
@@ -0,0 +1,48 @@
+From 7e3e1a906a157788ca4d4dd490fdc3ad82f70a09 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:35 +0200
+Subject: batman-adv: tp_meter: fix fast recovery precondition
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream.
+
+The fast recovery precondition checks if the recover (initialized to
+BATADV_TP_FIRST_SEQ) is bigger than the received ack. But since recover is
+only updated when this check is successful, it will never enter the fast
+recovery mode.
+
+According to RFC6582 Section 3.2 step 2, the check should actually be
+different:
+
+> When the third duplicate ACK is received, the TCP sender first
+> checks the value of recover to see if the Cumulative
+> Acknowledgment field covers more than recover
+
+The precondition must therefore check if recover is smaller than the
+received ack - basically swapping the operands of the current check.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 118cb5dd285c2d..cd196842478686 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -738,7 +738,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (recv_ack >= tp_vars->recover)
++              if (tp_vars->recover >= recv_ack)
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-5.10/batman-adv-tp_meter-handle-overlapping-packets.patch
new file mode 100644 (file)
index 0000000..4e106ea
--- /dev/null
@@ -0,0 +1,109 @@
+From a80b9f4c4ce2e7969b2e23ca2f4724152cda9226 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:47 +0200
+Subject: batman-adv: tp_meter: handle overlapping packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit cbde75c38b21f022891525078622587ad557b7c1 upstream.
+
+If the size of the packets would change during the transmission, it could
+happen that some retries of packets are overlapping. In this case, precise
+comparisons of sequence numbers by the receiver would be wrong. It is then
+necessary to check if the start sequence number to the end sequence number
+("seqno + length") would contain a new range.
+
+If this is the case then this is enough to accept this packet. In all other
+cases, the packet still has to be dropped (and not acked).
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 25 +++++++++++--------------
+ 1 file changed, 11 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index c01ff6e72b5da5..3a1561f5791987 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1293,7 +1293,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+ /**
+  * batadv_tp_handle_out_of_order() - store an out of order packet
+  * @tp_vars: the private data of the current TP meter session
+- * @skb: the buffer containing the received packet
++ * @seqno: sequence number of new received packet
++ * @payload_len: length of the received packet
+  *
+  * Store the out of order packet in the unacked list for late processing. This
+  * packets are kept in this list so that they can be ACKed at once as soon as
+@@ -1302,22 +1303,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  * Return: true if the packed has been successfully processed, false otherwise
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+-                                        const struct sk_buff *skb)
++                                        u32 seqno, u32 payload_len)
+       __must_hold(&tp_vars->unacked_lock)
+ {
+-      const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+-      u32 payload_len;
+       bool added = false;
+       new = kmalloc(sizeof(*new), GFP_ATOMIC);
+       if (unlikely(!new))
+               return false;
+-      icmp = (struct batadv_icmp_tp_packet *)skb->data;
+-
+-      new->seqno = ntohl(icmp->seqno);
+-      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      new->seqno = seqno;
+       new->len = payload_len;
+       /* if the list is empty immediately attach this new object */
+@@ -1484,7 +1480,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+-      size_t packet_size;
++      u32 payload_len;
+       u32 to_ack;
+       u32 seqno;
+@@ -1519,15 +1515,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+-      if (batadv_seq_before(seqno, tp_vars->last_recv))
++      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      to_ack = seqno + payload_len;
++      if (batadv_seq_before(to_ack, tp_vars->last_recv))
+               goto send_ack;
+       /* if the packet is out of order enqueue it */
+-      if (ntohl(icmp->seqno) != tp_vars->last_recv) {
++      if (batadv_seq_before(tp_vars->last_recv, seqno)) {
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++              if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
+                       spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
+               }
+@@ -1537,8 +1535,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       }
+       /* if everything was fine count the ACKed bytes */
+-      packet_size = skb->len - sizeof(struct batadv_unicast_packet);
+-      tp_vars->last_recv += packet_size;
++      tp_vars->last_recv = to_ack;
+       /* check if this ordered message filled a gap.... */
+       batadv_tp_ack_unordered(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-5.10/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
new file mode 100644 (file)
index 0000000..5a2d5fa
--- /dev/null
@@ -0,0 +1,40 @@
+From b25b3f5c58ef759f60de8fbd9e1debdd98a2fe82 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:36 +0200
+Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery
+ detection
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream.
+
+The recover variable and the last_sent sequence number are initialized on
+purpose as a really high value which will wrap-around after the first 2000
+bytes. The fast recovery precondition must therefore not use simple integer
+comparisons but use helpers which are aware of the sequence number
+wrap-arounds.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index cd196842478686..a4df826e4ece71 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -738,7 +738,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (tp_vars->recover >= recv_ack)
++              if (!batadv_seq_before(tp_vars->recover, recv_ack))
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-5.10/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
new file mode 100644 (file)
index 0000000..560c166
--- /dev/null
@@ -0,0 +1,40 @@
+From cdb8f527e0043cdd1f8257b7c98126e11d6bc309 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:32 +0200
+Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit febfb1b86224489535312296ecfa3d4bf467f339 upstream.
+
+When batadv_tp_update_cwnd() is called, dec_cwnd is increased. But dec_cwnd
+is only initialixed (to 0) when a duplicate Ack was received or when cwnd
+is below the ss_threshold.
+
+Just initialize the cwnd during the initialization to avoid any potential
+access of uninitialized data.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 1699fb8f8c82d4..d8ad58ccef2608 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1065,6 +1065,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+        * soft_interface, hence its MTU
+        */
+       tp_vars->cwnd = BATADV_TP_PLEN * 3;
++      tp_vars->dec_cwnd = 0;
++
+       /* at the beginning initialise the SS threshold to the biggest possible
+        * window size, hence the AWND size
+        */
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-5.10/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
new file mode 100644 (file)
index 0000000..6045f9d
--- /dev/null
@@ -0,0 +1,42 @@
+From 3033c1b305408031acd97f766e20f0fbde27474a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:31 +0200
+Subject: batman-adv: tp_meter: initialize dup_acks explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b2b68b32a715e0328662801576974aa37b942b00 upstream.
+
+When an ack with a sequence number equal to the last_acked is received, the
+dup_acks counter is increased to decide whether fast retransmit should be
+performed. Only when the sequence numbers are not equal, the dup_acks is
+set to the initial value (0).
+
+But if the initial packet would have the sequence number
+BATADV_TP_FIRST_SEQ, dup_acks would not be initialized and atomic_inc would
+operate on an undefined starting value. It is therefore required to have it
+explicitly initialized during the start of the sender session.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index c837a91879ac2f..1699fb8f8c82d4 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1055,6 +1055,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->icmp_uid = icmp_uid;
+       tp_vars->last_sent = BATADV_TP_FIRST_SEQ;
++      atomic_set(&tp_vars->dup_acks, 0);
+       atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ);
+       tp_vars->fast_recovery = false;
+       tp_vars->recover = BATADV_TP_FIRST_SEQ;
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-5.10/batman-adv-tp_meter-initialize-last_recv_time-during.patch
new file mode 100644 (file)
index 0000000..cb6d949
--- /dev/null
@@ -0,0 +1,68 @@
+From e126b797fc3919ca9c90015d9ea74e6193dea658 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:40 +0200
+Subject: batman-adv: tp_meter: initialize last_recv_time during init
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream.
+
+The last_recv_time is the most important indicator for a receiver session
+to figure out whether a session timed out or not. But this information was
+only initialized after the session was added to the tp_receiver_list and
+after the timer was started.
+
+In the worst case, the timer (function) could have tried to access this
+information before the actual initialization was reached. Like rest of the
+variables of the tp_meter receiver session, this field has to be filled out
+before any other (parallel running) context has the chance to access it.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index a015ff73812f5e..ea5640242ddc2e 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1412,8 +1412,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+-      if (tp_vars)
++      if (tp_vars) {
++              tp_vars->last_recv_time = jiffies;
+               goto out_unlock;
++      }
+       if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) {
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+@@ -1440,6 +1442,8 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      tp_vars->last_recv_time = jiffies;
++
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1488,9 +1492,9 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                                  icmp->orig);
+                       goto out;
+               }
+-      }
+-      tp_vars->last_recv_time = jiffies;
++              tp_vars->last_recv_time = jiffies;
++      }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-5.10/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
new file mode 100644 (file)
index 0000000..51a65e9
--- /dev/null
@@ -0,0 +1,44 @@
+From 2ec7e4f20d84a902f39fe2057c31c2d6942853d3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:30 +0200
+Subject: batman-adv: tp_meter: keep unacked list in ascending ordered
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream.
+
+When batadv_tp_handle_out_of_order inserts a new entry in the list of
+unacked (out of order) packets, it searches from the entry with the newest
+sequence number towards oldest sequence number. If an entry is found which
+is older than the newly entry, the new entry has to be added after the
+found one to keep the ascending order.
+
+But for this operation list_add_tail() was used. But this function adds an
+entry _before_ another one. As result, the list would contain a lot of
+swapped sequence numbers. The consumer of this list
+(batadv_tp_ack_unordered()) would then fail to correctly ack packets.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index fc2f0b49e5a06e..c837a91879ac2f 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1334,7 +1334,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                * one is attached _after_ it. In this way the list is kept in
+                * ascending order
+                */
+-              list_add_tail(&new->list, &un->list);
++              list_add(&new->list, &un->list);
+               added = true;
+               break;
+       }
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-5.10/batman-adv-tp_meter-prevent-parallel-modifications-o.patch
new file mode 100644 (file)
index 0000000..21a11b2
--- /dev/null
@@ -0,0 +1,157 @@
+From e28be785410fc412794c83b8096704f83ead84e3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:46 +0200
+Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream.
+
+When last_recv is updated to store the last receive sequence number, it is
+assuming that nothing is modifying in parallel while:
+
+* check for outdated packets is done
+* out of order check is performed (and packets are stored in out-of-order
+  queue)
+* the out-of-order queue was searched for closed gaps
+* sequence number for next ack is calculated
+
+Nothing of that was actually protected. It could therefore happen that the
+last_recv was updated multiple times in parallel and the final sequence
+number was calculated with deltas which had no connection to the sequence
+number they were added to.
+
+Lock this whole region with the same lock which was already used to protect
+the unacked (out-of-order) list.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 22 +++++++++++++---------
+ net/batman-adv/types.h    |  2 +-
+ 2 files changed, 14 insertions(+), 10 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 91392e48514d64..c01ff6e72b5da5 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1303,6 +1303,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                                         const struct sk_buff *skb)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+@@ -1319,12 +1320,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       payload_len = skb->len - sizeof(struct batadv_unicast_packet);
+       new->len = payload_len;
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
+               tp_vars->unacked_count++;
+-              goto out;
++              return true;
+       }
+       /* otherwise loop over the list and either drop the packet because this
+@@ -1373,9 +1373,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+               tp_vars->unacked_count--;
+       }
+-out:
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+-
+       return true;
+ }
+@@ -1385,6 +1382,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+  * @tp_vars: the private data of the current TP meter session
+  */
+ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       struct batadv_tp_unacked *un, *safe;
+       u32 to_ack;
+@@ -1392,7 +1390,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+       /* go through the unacked packet list and possibly ACK them as
+        * well
+        */
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               /* the list is ordered, therefore it is possible to stop as soon
+                * there is a gap between the last acked seqno and the seqno of
+@@ -1410,7 +1407,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               kfree(un);
+               tp_vars->unacked_count--;
+       }
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+ /**
+@@ -1489,6 +1485,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+       size_t packet_size;
++      u32 to_ack;
+       u32 seqno;
+       icmp = (struct batadv_icmp_tp_packet *)skb->data;
+@@ -1517,6 +1514,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
++      spin_lock_bh(&tp_vars->unacked_lock);
++
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+@@ -1528,8 +1527,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb))
++              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++                      spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
++              }
+               /* send a duplicate ACK */
+               goto send_ack;
+@@ -1543,11 +1544,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       batadv_tp_ack_unordered(tp_vars);
+ send_ack:
++      to_ack = tp_vars->last_recv;
++      spin_unlock_bh(&tp_vars->unacked_lock);
++
+       /* send the ACK. If the received packet was out of order, the ACK that
+        * is going to be sent is a duplicate (the sender will count them and
+        * possibly enter Fast Retransmit as soon as it has reached 3)
+        */
+-      batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv,
++      batadv_tp_send_ack(bat_priv, icmp->orig, to_ack,
+                          icmp->timestamp, icmp->session, icmp->uid);
+ out:
+       if (likely(tp_vars))
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index b12b0851df6e7e..437d651a1cffe3 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1505,7 +1505,7 @@ struct batadv_tp_vars {
+       /** @unacked_list: list of unacked packets (meta-info only) */
+       struct list_head unacked_list;
+-      /** @unacked_lock: protect unacked_list */
++      /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
+       spinlock_t unacked_lock;
+       /** @unacked_count: number of unacked entries */
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-5.10/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
new file mode 100644 (file)
index 0000000..fc7ebde
--- /dev/null
@@ -0,0 +1,124 @@
+From 5ea688ad68a89be8b5ccbdc69c8f7c9751c4843d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:44 +0200
+Subject: batman-adv: tp_meter: restrict number of unacked list entries
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream.
+
+When the unacked_list is unbound, an attacker could send messages with
+small lengths and appropriated seqno + gaps to force the receiver to
+allocate more and more unacked_list entries. And the end either causing an
+out-of-memory situation or increase the management overhead for the (large)
+list that significant portions of CPU cycles are wasted in searching
+through the list.
+
+When limiting the list to a specific number, it is important to still
+correctly add a new entry to the list. But if the list became larger than
+the limit, the last entry of the list (with the highest seqno) must be
+dropped to still allow the earlier seqnos to finish and therefore to
+continue the process. Otherwise, the process might get stuck with too high
+seqnos which are not handled by batadv_tp_ack_unordered().
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++-
+ net/batman-adv/types.h    |  3 +++
+ 2 files changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index ea5640242ddc2e..c7de8dfe9b65bd 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -86,6 +86,11 @@
+ #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \
+                       sizeof(struct batadv_unicast_packet))
++/**
++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack
++ */
++#define BATADV_TP_MAX_UNACKED 100
++
+ static u8 batadv_tp_prerandom[4096] __read_mostly;
+ /**
+@@ -1205,6 +1210,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1317,6 +1323,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
+               goto out;
+       }
+@@ -1347,12 +1354,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                */
+               list_add(&new->list, &un->list);
+               added = true;
++              tp_vars->unacked_count++;
+               break;
+       }
+       /* received packet with smallest seqno out of order; add it to front */
+-      if (!added)
++      if (!added) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
++      }
++
++      /* remove the last (biggest) unacked seqno when list is too large */
++      if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) {
++              un = list_last_entry(&tp_vars->unacked_list,
++                                   struct batadv_tp_unacked, list);
++              list_del(&un->list);
++              kfree(un);
++              tp_vars->unacked_count--;
++      }
+ out:
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1389,6 +1408,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+@@ -1438,6 +1458,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       spin_lock_init(&tp_vars->unacked_lock);
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
++      tp_vars->unacked_count = 0;
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 28f239421f74a3..b12b0851df6e7e 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1508,6 +1508,9 @@ struct batadv_tp_vars {
+       /** @unacked_lock: protect unacked_list */
+       spinlock_t unacked_lock;
++      /** @unacked_count: number of unacked entries */
++      size_t unacked_count;
++
+       /** @last_recv_time: time (jiffies) a msg was received */
+       unsigned long last_recv_time;
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-5.10/batman-adv-tt-don-t-merge-change-entries-with-differ.patch
new file mode 100644 (file)
index 0000000..6ce7f98
--- /dev/null
@@ -0,0 +1,46 @@
+From e25b407d71aa2945e9d8d63eba360cad2f7a40d2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:48 +0200
+Subject: batman-adv: tt: don't merge change entries with different VIDs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream.
+
+batadv_tt_local_event() merges/cancels events for the same client which
+would conflict or be duplicates. The matching of the queued events only
+compares the MAC address - the VLAN ID stored in each event is ignored.
+
+If a MAC would now appear on multiple VID, the two ADD change events (for
+VID 1 and VID 2) would be merged to a single vid event. The remote can
+therefore not calculate the correct TT table and desync. A full translation
+table exchange is required to recover from this state.
+
+A check of VID is therefore necessary to avoid such wrong merges/cancels.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 79da90b9cf0659..c323660897d43d 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -486,6 +486,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
+               if (!batadv_compare_eth(entry->change.addr, common->addr))
+                       continue;
++              if (entry->change.vid != tt_change_node->change.vid)
++                      continue;
++
+               /* DEL+ADD in the same orig interval have no effect and can be
+                * removed to avoid silly behaviour on the receiver side. The
+                * other way around (ADD+DEL) can happen in case of roaming of
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tt-track-roam-count-per-vid.patch b/queue-5.10/batman-adv-tt-track-roam-count-per-vid.patch
new file mode 100644 (file)
index 0000000..085499a
--- /dev/null
@@ -0,0 +1,89 @@
+From bf37b2bb7138f9d53e9df7185103e5d620b76d4b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:49 +0200
+Subject: batman-adv: tt: track roam count per VID
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream.
+
+batadv_tt_check_roam_count() is supposed to track roaming of a TT entry.
+But TT entries are for a MAC + VID. The VID was completely missed and thus
+leads to incorrect detection of ROAM counts when a client MAC exists in
+multiple VLANs.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 9 +++++++--
+ net/batman-adv/types.h             | 3 +++
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index c323660897d43d..4b3f39d6ddaf5b 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -3753,6 +3753,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  * batadv_tt_check_roam_count() - check if a client has roamed too frequently
+  * @bat_priv: the bat priv with all the soft interface information
+  * @client: mac address of the roaming client
++ * @vid: VLAN identifier
+  *
+  * This function checks whether the client already reached the
+  * maximum number of possible roaming phases. In this case the ROAMING_ADV
+@@ -3760,7 +3761,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  *
+  * Return: true if the ROAMING_ADV can be sent, false otherwise
+  */
+-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid)
+ {
+       struct batadv_tt_roam_node *tt_roam_node;
+       bool ret = false;
+@@ -3773,6 +3774,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               if (!batadv_compare_eth(tt_roam_node->addr, client))
+                       continue;
++              if (tt_roam_node->vid != vid)
++                      continue;
++
+               if (batadv_has_timed_out(tt_roam_node->first_time,
+                                        BATADV_ROAMING_MAX_TIME))
+                       continue;
+@@ -3794,6 +3798,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               atomic_set(&tt_roam_node->counter,
+                          BATADV_ROAMING_MAX_COUNT - 1);
+               ether_addr_copy(tt_roam_node->addr, client);
++              tt_roam_node->vid = vid;
+               list_add(&tt_roam_node->list, &bat_priv->tt.roam_list);
+               ret = true;
+@@ -3830,7 +3835,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
+       /* before going on we have to check whether the client has
+        * already roamed to us too many times
+        */
+-      if (!batadv_tt_check_roam_count(bat_priv, client))
++      if (!batadv_tt_check_roam_count(bat_priv, client, vid))
+               goto out;
+       batadv_dbg(BATADV_DBG_TT, bat_priv,
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 437d651a1cffe3..88831374e81747 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -2059,6 +2059,9 @@ struct batadv_tt_roam_node {
+       /** @addr: mac address of the client in the roaming phase */
+       u8 addr[ETH_ALEN];
++      /** @vid: VLAN identifier */
++      u16 vid;
++
+       /**
+        * @counter: number of allowed roaming events per client within a single
+        * OGM interval (changes are committed with each OGM)
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-5.10/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
new file mode 100644 (file)
index 0000000..4758636
--- /dev/null
@@ -0,0 +1,155 @@
+From 45e167d455f760a3395459b8f99b3bfbf459f436 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:52 +0200
+Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream.
+
+TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to
+signal that the OGM handler should be called (with NULL for data) when the
+specific TVLV container was not found in the OGM. This is used by:
+
+* DAT
+* GW
+* Multicast (OGM + Tracker)
+
+The state whether the handler was executed was stored in the struct
+batadv_tvlv_handler. But the TVLV processing is started without any lock.
+Multiple parallel contexts processing TVLVs would therefore overwrite each
+others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared
+batadv_tvlv_handler.
+
+Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine,
+per TVLV buffer, whether a matching container was present by scanning the
+packet's buffer.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tvlv.c  | 63 ++++++++++++++++++++++++++++++++++++++----
+ net/batman-adv/types.h |  7 -----
+ 2 files changed, 57 insertions(+), 13 deletions(-)
+
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 84f0fb175b3313..2c224951d0195c 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -394,7 +394,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+               tvlv_handler->ogm_handler(bat_priv, orig_node,
+                                         BATADV_NO_FLAGS,
+                                         tvlv_value, tvlv_value_len);
+-              tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
+       } else {
+               if (!src)
+                       return NET_RX_SUCCESS;
+@@ -413,6 +412,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+       return NET_RX_SUCCESS;
+ }
++/**
++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
++ * @tvlv_value: tvlv content
++ * @tvlv_value_len: tvlv content length
++ * @type: tvlv container type to look for
++ * @version: tvlv container version to look for
++ *
++ * Return: true if a container of the given type and version is present in the
++ * tvlv buffer, false otherwise.
++ */
++static bool batadv_tvlv_containers_contain(void *tvlv_value,
++                                         u16 tvlv_value_len, u8 type,
++                                         u8 version)
++{
++      struct batadv_tvlv_hdr *tvlv_hdr;
++      u16 tvlv_value_cont_len;
++
++      while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
++              tvlv_hdr = tvlv_value;
++              tvlv_value_cont_len = ntohs(tvlv_hdr->len);
++              tvlv_value = tvlv_hdr + 1;
++              tvlv_value_len -= sizeof(*tvlv_hdr);
++
++              if (tvlv_value_cont_len > tvlv_value_len)
++                      break;
++
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
++              if (tvlv_hdr->type == type && tvlv_hdr->version == version)
++                      return true;
++
++              tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
++              tvlv_value_len -= tvlv_value_cont_len;
++      }
++
++      return false;
++}
++
+ /**
+  * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the
+  *  appropriate handlers
+@@ -433,7 +474,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+                                  u8 *src, u8 *dst,
+                                  void *tvlv_value, u16 tvlv_value_len)
+ {
++      u16 tvlv_value_start_len = tvlv_value_len;
+       struct batadv_tvlv_handler *tvlv_handler;
++      void *tvlv_value_start = tvlv_value;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       u16 tvlv_value_cont_len;
+       u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
+@@ -474,12 +517,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(tvlv_handler,
+                                &bat_priv->tvlv.handler_list, list) {
+-              if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
+-                  !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
+-                      tvlv_handler->ogm_handler(bat_priv, orig_node,
+-                                                cifnotfound, NULL, 0);
++              if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND))
++                      continue;
+-              tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
++              /* if the corresponding container was present then the handler
++               * was already called from the loop above
++               */
++              if (batadv_tvlv_containers_contain(tvlv_value_start,
++                                                 tvlv_value_start_len,
++                                                 tvlv_handler->type,
++                                                 tvlv_handler->version))
++                      continue;
++
++              tvlv_handler->ogm_handler(bat_priv, orig_node,
++                                        cifnotfound, NULL, 0);
+       }
+       rcu_read_unlock();
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 88831374e81747..79ffd25fd4eba2 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -2492,13 +2492,6 @@ enum batadv_tvlv_handler_flags {
+        *  will call this handler even if its type was not found (with no data)
+        */
+       BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
+-
+-      /**
+-       * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the
+-       *  API marks a handler as being called, so it won't be called if the
+-       *  BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
+-       */
+-      BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
+ };
+ /**
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-5.10/batman-adv-tvlv-enforce-2-byte-alignment.patch
new file mode 100644 (file)
index 0000000..dcd18c7
--- /dev/null
@@ -0,0 +1,106 @@
+From 617eab49d1ecb5560d3742c791fc337ffcf694df Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:51 +0200
+Subject: batman-adv: tvlv: enforce 2-byte alignment
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream.
+
+The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte
+alignment, so a following OGM must start at an even offset. As the header
+length is even, an odd tvlv_len would misalign it and trigger unaligned
+accesses on strict-alignment architectures.
+
+Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in
+the mesh. Therefore, reject such malformed packets.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+[ Drop change for non-existing mcast handling ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 11 ++++++++++-
+ net/batman-adv/bat_v_ogm.c  | 11 ++++++++++-
+ net/batman-adv/tvlv.c       |  6 ++++++
+ 3 files changed, 26 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index 93c2c5f6facc0d..cc4fbfd0b75530 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -312,14 +312,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
+                         const struct batadv_ogm_packet *ogm_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm_packet->tvlv_len);
++
++      /* the fields of an aggregated OGM are accessed assuming (at least)
++       * 2-byte alignment, so a following OGM must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 12de9b44064dd4..2a62ab13bf87bb 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -864,14 +864,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len,
+                        const struct batadv_ogm2_packet *ogm2_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm2_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm2_packet->tvlv_len);
++
++      /* the fields of an aggregated OGMv2 are accessed assuming (at least)
++       * 2-byte alignment, so a following OGMv2 must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm2_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 8da8184a2ebdfc..84f0fb175b3313 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -448,6 +448,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+               if (tvlv_value_cont_len > tvlv_value_len)
+                       break;
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
+               tvlv_handler = batadv_tvlv_handler_get(bat_priv,
+                                                      tvlv_hdr->type,
+                                                      tvlv_hdr->version);
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-5.10/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
new file mode 100644 (file)
index 0000000..da62e25
--- /dev/null
@@ -0,0 +1,106 @@
+From cefdcf236b82687578ad4d6ed69410651ac18441 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:09:43 +0200
+Subject: batman-adv: v: prevent OGM aggregation on disabled hardif
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream.
+
+When an interface gets disabled, the worker is correctly disabled by
+batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable().
+In this process, the skb aggr_list is also freed.
+
+But batadv_v_ogm_send_meshif() can still queue new skbs (via
+batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all
+cores can no longer find the RCU protected list of hard interfaces. These
+queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work.
+
+The batadv_v_ogm_iface_disable() function must block
+batadv_v_ogm_queue_on_if() to avoid leak of skbs.
+
+Cc: stable@kernel.org
+Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_v.c     |  1 +
+ net/batman-adv/bat_v_ogm.c | 12 ++++++++++++
+ net/batman-adv/types.h     |  6 ++++++
+ 3 files changed, 19 insertions(+)
+
+diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c
+index 6dc39fc0350e6a..7b9bc8a4bd48f5 100644
+--- a/net/batman-adv/bat_v.c
++++ b/net/batman-adv/bat_v.c
+@@ -1084,6 +1084,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
+       hard_iface->bat_v.aggr_len = 0;
+       skb_queue_head_init(&hard_iface->bat_v.aggr_list);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq,
+                         batadv_v_ogm_aggr_work);
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 939aa4b303ad98..12de9b44064dd4 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -255,11 +255,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
+       }
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      if (!hard_iface->bat_v.aggr_list_enabled) {
++              kfree_skb(skb);
++              goto unlock;
++      }
++
+       if (!batadv_v_ogm_queue_left(skb, hard_iface))
+               batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+       __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
++
++unlock:
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+@@ -422,6 +429,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
++      spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = true;
++      spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
++
+       batadv_v_ogm_start_queue_timer(hard_iface);
+       batadv_v_ogm_start_timer(bat_priv);
+@@ -437,6 +448,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
+       cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq);
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       batadv_v_ogm_aggr_list_free(hard_iface);
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 238d9824c2d62f..28f239421f74a3 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -131,6 +131,12 @@ struct batadv_hard_iface_bat_v {
+       /** @aggr_list: queue for to be aggregated OGM packets */
+       struct sk_buff_head aggr_list;
++      /**
++       * @aggr_list_enabled: aggr_list is active and new skbs can be
++       * enqueued. Protected by aggr_list.lock after initialization
++       */
++      bool aggr_list_enabled:1;
++
+       /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */
+       unsigned int aggr_len;
+-- 
+2.53.0
+
index e48b2b93a924d7a1ca81f20ff29bee65ae410c53..70b846084f3e2e0ce199c97fb9e71e1395a064fe 100644 (file)
@@ -31,3 +31,26 @@ crypto-qat-replace-kzalloc-copy_from_user-with-memdu.patch
 crypto-qat-return-pointer-directly-in-adf_ctl_alloc_.patch
 crypto-qat-remove-unused-character-device-and-ioctls.patch
 net-sched-act_pedit-fix-action-bind-logic.patch
+batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
+batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
+batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
+batman-adv-tp_meter-avoid-window-underflow.patch
+batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
+batman-adv-tp_meter-fix-fast-recovery-precondition.patch
+batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
+batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
+batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
+batman-adv-prevent-elp-transmission-interval-underfl.patch
+batman-adv-tp_meter-initialize-last_recv_time-during.patch
+batman-adv-frag-ensure-fragment-is-writable-before-m.patch
+batman-adv-frag-avoid-underflow-of-ttl.patch
+batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
+batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
+batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
+batman-adv-tp_meter-prevent-parallel-modifications-o.patch
+batman-adv-tp_meter-handle-overlapping-packets.patch
+batman-adv-tt-don-t-merge-change-entries-with-differ.patch
+batman-adv-tt-track-roam-count-per-vid.patch
+batman-adv-dat-prevent-false-sharing-between-vlans.patch
+batman-adv-tvlv-enforce-2-byte-alignment.patch
+batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
diff --git a/queue-5.15/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-5.15/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
new file mode 100644 (file)
index 0000000..de052d0
--- /dev/null
@@ -0,0 +1,156 @@
+From 2906041178585aacd76f532e70461277492b14b2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:48 +0200
+Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream.
+
+The lasttime field for claim, backbone_gw, and loopdetect tracks the
+jiffies value of the most recent activity and is used to detect timeouts.
+These accesses are not consistently protected by a lock, so
+READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler
+optimizations.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++-------------
+ 1 file changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index 452e78fd70c039..0b5222ac4f59a1 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -511,7 +511,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig,
+               return NULL;
+       entry->vid = vid;
+-      entry->lasttime = jiffies;
++      WRITE_ONCE(entry->lasttime, jiffies);
+       entry->crc = BATADV_BLA_CRC_INIT;
+       entry->bat_priv = bat_priv;
+       spin_lock_init(&entry->crc_lock);
+@@ -579,7 +579,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv,
+       if (unlikely(!backbone_gw))
+               return;
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       batadv_backbone_gw_put(backbone_gw);
+ }
+@@ -713,7 +713,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+               ether_addr_copy(claim->addr, mac);
+               spin_lock_init(&claim->backbone_lock);
+               claim->vid = vid;
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               kref_get(&backbone_gw->refcount);
+               claim->backbone_gw = backbone_gw;
+               kref_init(&claim->refcount);
+@@ -735,7 +735,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+                       return;
+               }
+       } else {
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               if (claim->backbone_gw == backbone_gw)
+                       /* no need to register a new backbone */
+                       goto claim_free_ref;
+@@ -768,7 +768,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+       spin_lock_bh(&backbone_gw->crc_lock);
+       backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+ claim_free_ref:
+       batadv_claim_put(claim);
+@@ -857,7 +857,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
+               return true;
+       /* handle as ANNOUNCE frame */
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       crc = ntohs(*((__force __be16 *)(&an_addr[4])));
+       batadv_dbg(BATADV_DBG_BLA, bat_priv,
+@@ -1252,7 +1252,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
+                                                 head, hash_entry) {
+                               if (now)
+                                       goto purge_now;
+-                              if (!batadv_has_timed_out(backbone_gw->lasttime,
++                              if (!batadv_has_timed_out(READ_ONCE(backbone_gw->lasttime),
+                                                         BATADV_BLA_BACKBONE_TIMEOUT))
+                                       continue;
+@@ -1333,7 +1333,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
+                                               primary_if->net_dev->dev_addr))
+                               goto skip;
+-                      if (!batadv_has_timed_out(claim->lasttime,
++                      if (!batadv_has_timed_out(READ_ONCE(claim->lasttime),
+                                                 BATADV_BLA_CLAIM_TIMEOUT))
+                               goto skip;
+@@ -1493,7 +1493,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+               eth_random_addr(bat_priv->bla.loopdetect_addr);
+               bat_priv->bla.loopdetect_addr[0] = 0xba;
+               bat_priv->bla.loopdetect_addr[1] = 0xbe;
+-              bat_priv->bla.loopdetect_lasttime = jiffies;
++              WRITE_ONCE(bat_priv->bla.loopdetect_lasttime, jiffies);
+               atomic_set(&bat_priv->bla.loopdetect_next,
+                          BATADV_BLA_LOOPDETECT_PERIODS);
+@@ -1514,7 +1514,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                                               primary_if->net_dev->dev_addr))
+                               continue;
+-                      backbone_gw->lasttime = jiffies;
++                      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+                       batadv_bla_send_announce(bat_priv, backbone_gw);
+                       if (send_loopdetect)
+@@ -1899,7 +1899,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       /* If the packet came too late, don't forward it on the mesh
+        * but don't consider that as loop. It might be a coincidence.
+        */
+-      if (batadv_has_timed_out(bat_priv->bla.loopdetect_lasttime,
++      if (batadv_has_timed_out(READ_ONCE(bat_priv->bla.loopdetect_lasttime),
+                                BATADV_BLA_LOOPDETECT_TIMEOUT))
+               return true;
+@@ -2015,7 +2015,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       if (own_claim) {
+               /* ... allow it in any case */
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               goto allow;
+       }
+@@ -2117,7 +2117,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+               /* if yes, the client has roamed and we have
+                * to unclaim it.
+                */
+-              if (batadv_has_timed_out(claim->lasttime, 100)) {
++              if (batadv_has_timed_out(READ_ONCE(claim->lasttime), 100)) {
+                       /* only unclaim if the last claim entry is
+                        * older than 100 ms to make sure we really
+                        * have a roaming client here.
+@@ -2371,7 +2371,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid,
+       backbone_crc = backbone_gw->crc;
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime);
++      msecs = jiffies_to_msecs(jiffies - READ_ONCE(backbone_gw->lasttime));
+       if (is_own)
+               if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) {
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-5.15/batman-adv-dat-prevent-false-sharing-between-vlans.patch
new file mode 100644 (file)
index 0000000..be40708
--- /dev/null
@@ -0,0 +1,57 @@
+From eb233b47819142a2b5c2d28e31bf8fd28ac14c6f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:02 +0200
+Subject: batman-adv: dat: prevent false sharing between VLANs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream.
+
+The local hash of DAT entries is supposed to be VLAN (VID) aware. But
+the adding to the hash and the search in the hash were not checking the VID
+information of the hash entries. The entries would therefore only be
+correctly separated when batadv_hash_dat() didn't select the same buckets
+for different VIDs.
+
+Cc: stable@kernel.org
+Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/distributed-arp-table.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
+index 9f561ea95f730d..b8d471dae2888c 100644
+--- a/net/batman-adv/distributed-arp-table.c
++++ b/net/batman-adv/distributed-arp-table.c
+@@ -215,10 +215,13 @@ static void batadv_dat_purge(struct work_struct *work)
+  */
+ static bool batadv_compare_dat(const struct hlist_node *node, const void *data2)
+ {
+-      const void *data1 = container_of(node, struct batadv_dat_entry,
+-                                       hash_entry);
++      const struct batadv_dat_entry *entry1;
++      const struct batadv_dat_entry *entry2;
+-      return memcmp(data1, data2, sizeof(__be32)) == 0;
++      entry1 = container_of(node, struct batadv_dat_entry, hash_entry);
++      entry2 = data2;
++
++      return entry1->ip == entry2->ip && entry1->vid == entry2->vid;
+ }
+ /**
+@@ -345,6 +348,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
+               if (dat_entry->ip != ip)
+                       continue;
++              if (dat_entry->vid != vid)
++                      continue;
++
+               if (!kref_get_unless_zero(&dat_entry->refcount))
+                       continue;
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-ensure-bcast-is-writable-before-modifying.patch b/queue-5.15/batman-adv-ensure-bcast-is-writable-before-modifying.patch
new file mode 100644 (file)
index 0000000..cac8771
--- /dev/null
@@ -0,0 +1,50 @@
+From a417ea7a6294239dbbcf64e4eca9d2d22dae1b7c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:51 +0200
+Subject: batman-adv: ensure bcast is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or used skb_cow() to ensure that the data part is not
+shared.
+
+The old implementation used a shared queue and created copies before
+attempting to write to it. But with the new implementation, the broadcast
+packet is already modified when it gets received. Potentially writing to
+shared buffers in this process.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to rebroadcast the
+packet.
+
+Cc: stable@kernel.org
+Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/routing.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 970d0d7ccc981a..503c8c9381ebe9 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1198,6 +1198,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
+       if (batadv_is_my_mac(bat_priv, bcast_packet->orig))
+               goto free_skb;
++      /* create a copy of the skb, if needed, to modify it. */
++      if (skb_cow(skb, ETH_HLEN) < 0)
++              goto free_skb;
++
++      bcast_packet = (struct batadv_bcast_packet *)skb->data;
++
+       if (bcast_packet->ttl-- < 2)
+               goto free_skb;
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch b/queue-5.15/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch
new file mode 100644 (file)
index 0000000..4831926
--- /dev/null
@@ -0,0 +1,118 @@
+From 82593888262b84a5910c07b4546692e42980d3e0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:52 +0200
+Subject: batman-adv: fix (m|b)cast csum after decrementing TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream.
+
+The broadcast and multicast packets can be received at the same time by the
+local system and forwarded to other nodes. Both are simply decrementing the
+TTL at the beginning of the receive path - independent of chosen paths
+(receive/forward). But such a modification of the data conflicts with the
+hw csum. This is not a problem when the packet is directly forwarded but
+can cause errors in the local receive path.
+
+Such a problem can then trigger a "hw csum failure". The receiver path must
+therefore ensure that the csum is fixed for each modification of the
+payload before batadv_interface_rx() is reached.
+
+Since all batman-adv packet types with a ttl have it as u8 at offset 2, a
+helper can be used for all of them. But it is only used at the moment for
+batadv_bcast_packet and batadv_mcast_packet because they are the only ones
+which deliver the packet locally but unconditionally modify the TTL.
+
+Cc: stable@kernel.org
+Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed")
+Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding")
+[ Context, Drop change for non-existing mcast handling ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/routing.c | 55 +++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 54 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 503c8c9381ebe9..0fb37993fd4604 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -8,6 +8,7 @@
+ #include "main.h"
+ #include <linux/atomic.h>
++#include <linux/build_bug.h>
+ #include <linux/byteorder/generic.h>
+ #include <linux/compiler.h>
+ #include <linux/errno.h>
+@@ -205,6 +206,58 @@ bool batadv_check_management_packet(struct sk_buff *skb,
+       return true;
+ }
++/**
++ * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe
++ * @skb: the received packet with @skb->data pointing to the batman-adv header
++ *
++ * Supports the following packet types, all of which carry the TTL at offset 2:
++ *
++ * - batadv_ogm_packet
++ * - batadv_ogm2_packet
++ * - batadv_icmp_header
++ * - batadv_icmp_packet
++ * - batadv_icmp_tp_packet
++ * - batadv_icmp_packet_rr
++ * - batadv_unicast_packet
++ * - batadv_frag_packet
++ * - batadv_bcast_packet
++ * - batadv_mcast_packet
++ * - batadv_coded_packet
++ * - batadv_unicast_tvlv_packet
++ *
++ * Return: true if the packet may be forwarded (ttl decremented),
++ *  false if it must be dropped (ttl would expire)
++ */
++static bool batadv_skb_decrement_ttl(struct sk_buff *skb)
++{
++      static const size_t ttl_offset = 2;
++      u8 *ttl_pos;
++
++      BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset);
++
++      ttl_pos = skb->data + ttl_offset;
++
++      /* would expire on this hop -> drop, leave header + csum untouched */
++      if (*ttl_pos < 2)
++              return false;
++
++      skb_postpull_rcsum(skb, ttl_pos, 1);
++      (*ttl_pos)--;
++      skb_postpush_rcsum(skb, ttl_pos, 1);
++
++      return true;
++}
++
+ /**
+  * batadv_recv_my_icmp_packet() - receive an icmp packet locally
+  * @bat_priv: the bat priv with all the soft interface information
+@@ -1204,7 +1257,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
+       bcast_packet = (struct batadv_bcast_packet *)skb->data;
+-      if (bcast_packet->ttl-- < 2)
++      if (!batadv_skb_decrement_ttl(skb))
+               goto free_skb;
+       orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig);
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-5.15/batman-adv-frag-avoid-underflow-of-ttl.patch
new file mode 100644 (file)
index 0000000..7fadc37
--- /dev/null
@@ -0,0 +1,45 @@
+From f54f596f8a17b4218aa235f494ef561c23fdbb44 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:54 +0200
+Subject: batman-adv: frag: avoid underflow of TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream.
+
+Packets with a TTL are using it to limit the amount of time this packet can
+be forwarded. But for batadv_frag_packet, the TTL was always only reduced
+but it was never evaluated. It could even underflow without any effect.
+
+Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for
+forwarding. This keeps it in sync with the not fragmented unicast packet.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index fbf030c57ac04d..0138b0953e10b5 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -420,6 +420,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (packet->ttl < 2) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
+               if (skb_cow(skb, ETH_HLEN) < 0) {
+                       kfree_skb(skb);
+                       *rx_result = NET_RX_DROP;
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-5.15/batman-adv-frag-ensure-fragment-is-writable-before-m.patch
new file mode 100644 (file)
index 0000000..de322ff
--- /dev/null
@@ -0,0 +1,107 @@
+From ce3d7d051bd7a1bec2e392eb92fca7b5cb7a7938 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:53 +0200
+Subject: batman-adv: frag: ensure fragment is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or use skb_cow() to ensure that the data part is not
+shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to forward the
+fragment.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 15 ++++++++++++++-
+ net/batman-adv/fragmentation.h |  3 ++-
+ net/batman-adv/routing.c       |  3 +--
+ 3 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index 4c193194034155..fbf030c57ac04d 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -385,6 +385,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  * @skb: skb to forward
+  * @recv_if: interface that the skb is received on
+  * @orig_node_src: originator that the skb is received from
++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and
++ *  NET_RX_DROP when it was dropped; only valid when true is returned
+  *
+  * Look up the next-hop of the fragments payload and check if the merged packet
+  * will exceed the MTU towards the next-hop. If so, the fragment is forwarded
+@@ -394,7 +396,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  */
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src)
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
+       struct batadv_orig_node *orig_node_dst;
+@@ -417,12 +420,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (skb_cow(skb, ETH_HLEN) < 0) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
++              packet = (struct batadv_frag_packet *)skb->data;
++
+               batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
+               batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
+                                  skb->len + ETH_HLEN);
+               packet->ttl--;
+               batadv_send_unicast_skb(skb, neigh_node);
++              *rx_result = NET_RX_SUCCESS;
+               ret = true;
+       }
+diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h
+index dbf0871f870303..51e281027ab630 100644
+--- a/net/batman-adv/fragmentation.h
++++ b/net/batman-adv/fragmentation.h
+@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig,
+                           bool (*check_cb)(struct batadv_frag_table_entry *));
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src);
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result);
+ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+                           struct batadv_orig_node *orig_node);
+ int batadv_frag_send_packet(struct sk_buff *skb,
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 0fb37993fd4604..596e4cc47046a2 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1174,10 +1174,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb,
+       /* Route the fragment if it is not for us and too big to be merged. */
+       if (!batadv_is_my_mac(bat_priv, frag_packet->dest) &&
+-          batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) {
++          batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) {
+               /* skb was consumed */
+               skb = NULL;
+-              ret = NET_RX_SUCCESS;
+               goto put_orig_node;
+       }
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-5.15/batman-adv-prevent-elp-transmission-interval-underfl.patch
new file mode 100644 (file)
index 0000000..60ca806
--- /dev/null
@@ -0,0 +1,52 @@
+From d1211e333781f3bc9b976568f3edeaf888a94325 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:49 +0200
+Subject: batman-adv: prevent ELP transmission interval underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream.
+
+batadv_v_elp_start_timer() enqeues a delayed work. The time when it starts
+is randomly chosen between (elp_interval - BATADV_JITTER) and
+(elp_interval + BATADV_JITTER). The configured elp_interval must therefore
+be larger or equal to BATADV_JITTER to avoid that it causes an underflow of
+the unsigned integer. If this would happen, then a "fast" ELP interval
+would turn into a "day long" delay.
+
+At the same time, it must not be larger than the maximum value the variable
+can store.
+
+Cc: stable@kernel.org
+Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/netlink.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
+index bbd6ecf1678c97..cb50b46cb8d30d 100644
+--- a/net/batman-adv/netlink.c
++++ b/net/batman-adv/netlink.c
+@@ -941,9 +941,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb,
+ #ifdef CONFIG_BATMAN_ADV_BATMAN_V
+       if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) {
++              u32 elp_interval;
++
+               attr = info->attrs[BATADV_ATTR_ELP_INTERVAL];
++              elp_interval = nla_get_u32(attr);
++
++              elp_interval = min_t(u32, elp_interval, INT_MAX);
++              elp_interval = max_t(u32, elp_interval, BATADV_JITTER);
+-              atomic_set(&hard_iface->bat_v.elp_interval, nla_get_u32(attr));
++              atomic_set(&hard_iface->bat_v.elp_interval, elp_interval);
+       }
+       if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) {
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-5.15/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
new file mode 100644 (file)
index 0000000..c72cf0f
--- /dev/null
@@ -0,0 +1,74 @@
+From ee45ff9e66206c819dbe2d926138fdae02a0a9b0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:47 +0200
+Subject: batman-adv: tp_meter: add only finished tp_vars to lists
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream.
+
+When the receiver variables (aka "session") are initialized, then they are
+added to the list of sessions before the timer is set up. A RCU protected
+reader could therefore find the entry and run mod_setup before
+batadv_tp_init_recv() finished the timer initialization.
+
+The same is true for batadv_tp_start(), which must first initialize the
+finish_work and the test_length to avoid a similar problem.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 7385cd02abf9f0..257f8d042d158e 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1096,21 +1096,21 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->prerandom_offset = 0;
+       spin_lock_init(&tp_vars->prerandom_lock);
+-      kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+-      spin_unlock_bh(&bat_priv->tp_list_lock);
+-
+       tp_vars->test_length = test_length;
+       if (!tp_vars->test_length)
+               tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH;
++      /* init work item for finished tp tests */
++      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
++
++      kref_get(&tp_vars->refcount);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      spin_unlock_bh(&bat_priv->tp_list_lock);
++
+       batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                  "Meter: starting throughput meter towards %pM (length=%ums)\n",
+                  dst, test_length);
+-      /* init work item for finished tp tests */
+-      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
+-
+       /* start tp kthread. This way the write() call issued from userspace can
+        * happily return and avoid to block
+        */
+@@ -1430,10 +1430,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
+       kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+       kref_get(&tp_vars->refcount);
+-      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+       batadv_tp_reset_receiver_timer(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-5.15/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
new file mode 100644 (file)
index 0000000..5d2d473
--- /dev/null
@@ -0,0 +1,66 @@
+From 584a72aa30797ddbe2e61b73e0d668c7012222c4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:57 +0200
+Subject: batman-adv: tp_meter: annotate last_recv_time access with
+ READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream.
+
+The last_recv_time field for batadv_tp_receiver tracks the jiffies value of
+the most recent activity and is used to detect timeouts. These accesses are
+not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used
+to prevent data races caused by compiler optimizations.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 105cda5b0b2cda..503819e9839c62 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       bat_priv = tp_vars->bat_priv;
+       /* if there is recent activity rearm the timer */
+-      if (!batadv_has_timed_out(tp_vars->last_recv_time,
++      if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time),
+                                 BATADV_TP_RECV_TIMEOUT)) {
+               /* reset the receiver shutdown timer */
+               batadv_tp_reset_receiver_timer(tp_vars);
+@@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+       if (tp_vars) {
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+               goto out_unlock;
+       }
+@@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+-      tp_vars->last_recv_time = jiffies;
++      WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                       goto out;
+               }
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-5.15/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
new file mode 100644 (file)
index 0000000..320cb41
--- /dev/null
@@ -0,0 +1,64 @@
+From ca58056f2de5dcf4ab6996cdf7d6860e34aac5da Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:44 +0200
+Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream.
+
+The cwnd is always MSS <= cwnd <= 0x20000000. But the calculation in
+batadv_tp_update_cwnd() assumes unsigned 32 bit arithmetics.
+
+    ((mss * 8) ** 2) / (cwnd * 8)
+
+In case cwnd is actually 0x20000000, it will be shifted by 3 bit to the
+left end up at 0x100000000 or U32_MAX + 1. It will therefore wrap around
+and be 0 - resulting in:
+
+    ((mss * 8) ** 2) / 0
+
+This is of course invalid and cannot be calculated. The calculation should
+must be simplified to avoid this overflow:
+
+   (mss ** 2) * 8 / cwnd
+
+It will keep the precision enhancement from the scaling (by 8) but avoid
+the overflow in the divisor.
+
+In theory, there could still be an overflow in the dividend. It is at the
+moment fixed to BATADV_TP_PLEN in batadv_tp_recv_ack() - so it is not an
+imminent problem. But allowing it to use the whole u32 bit range, would
+mean that it can still use up to 67 bits. To keep this calculation safe for
+32 bit arithmetic, mss must never use more than floor((32 - 3) / 2) bits -
+or in other words: must never be larger than 16383.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 3fd46999f36745..f961beac12282e 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -154,9 +154,12 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss)
+               return;
+       }
++      /* prevent overflow in (mss * mss) << 3 */
++      mss = min_t(u32, mss, (1U << 14) - 1);
++
+       /* increment CWND at least of 1 (section 3.1 of RFC5681) */
+       tp_vars->dec_cwnd += max_t(u32, 1U << 3,
+-                                 ((mss * mss) << 6) / (tp_vars->cwnd << 3));
++                                 ((mss * mss) << 3) / tp_vars->cwnd);
+       if (tp_vars->dec_cwnd < (mss << 3)) {
+               spin_unlock_bh(&tp_vars->cwnd_lock);
+               return;
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-5.15/batman-adv-tp_meter-avoid-window-underflow.patch
new file mode 100644 (file)
index 0000000..ba4fb6d
--- /dev/null
@@ -0,0 +1,56 @@
+From 049569def25e4a9f5821e48e92e2a69812e37942 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:43 +0200
+Subject: batman-adv: tp_meter: avoid window underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream.
+
+In batadv_tp_avail(), win_left is calculated with 32-bit unsigned
+arithmetic: win_left = win_limit - tp_vars->last_sent;
+
+During Fast Recovery, cwnd is inflated and last_sent advances rapidly. When
+Fast Recovery ends, cwnd drops abruptly back to ss_threshold. If the newly
+shrunk win_limit is less than last_sent, the unsigned subtraction will
+underflow, wrapping to a massive positive value. Instead of returning that
+the window is full (unavailable), it returns that the sender can continue
+sending.
+
+To handle this situation, it must be checked whether the windows end
+sequence number (win_limit) has to be compared with the last sent sequence
+number. If it would be before the last sent sequence number, then more acks
+are needed before the transmission can be started again.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index f7b0ce3f8e2aef..3fd46999f36745 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+ static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars,
+                           size_t payload_len)
+ {
++      u32 last_sent = READ_ONCE(tp_vars->last_sent);
+       u32 win_left, win_limit;
+       win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd;
+-      win_left = win_limit - tp_vars->last_sent;
++
++      if (batadv_seq_before(last_sent, win_limit))
++              win_left = win_limit - last_sent;
++      else
++              win_left = 0;
+       return win_left >= payload_len;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-5.15/batman-adv-tp_meter-fix-fast-recovery-precondition.patch
new file mode 100644 (file)
index 0000000..fd5a7e6
--- /dev/null
@@ -0,0 +1,48 @@
+From 1e20d87ae2704e3b2d4bf5892f1ee18421ca42d4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:45 +0200
+Subject: batman-adv: tp_meter: fix fast recovery precondition
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream.
+
+The fast recovery precondition checks if the recover (initialized to
+BATADV_TP_FIRST_SEQ) is bigger than the received ack. But since recover is
+only updated when this check is successful, it will never enter the fast
+recovery mode.
+
+According to RFC6582 Section 3.2 step 2, the check should actually be
+different:
+
+> When the third duplicate ACK is received, the TCP sender first
+> checks the value of recover to see if the Cumulative
+> Acknowledgment field covers more than recover
+
+The precondition must therefore check if recover is smaller than the
+received ack - basically swapping the operands of the current check.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index f961beac12282e..1fc8d6e6f99b30 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (recv_ack >= tp_vars->recover)
++              if (tp_vars->recover >= recv_ack)
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-5.15/batman-adv-tp_meter-handle-overlapping-packets.patch
new file mode 100644 (file)
index 0000000..411a279
--- /dev/null
@@ -0,0 +1,109 @@
+From 18d62c85f98b28e7de10db80f1857980c7d159f2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:59 +0200
+Subject: batman-adv: tp_meter: handle overlapping packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit cbde75c38b21f022891525078622587ad557b7c1 upstream.
+
+If the size of the packets would change during the transmission, it could
+happen that some retries of packets are overlapping. In this case, precise
+comparisons of sequence numbers by the receiver would be wrong. It is then
+necessary to check if the start sequence number to the end sequence number
+("seqno + length") would contain a new range.
+
+If this is the case then this is enough to accept this packet. In all other
+cases, the packet still has to be dropped (and not acked).
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 25 +++++++++++--------------
+ 1 file changed, 11 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 83749d7b4fd112..5514fef54b0a9a 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+ /**
+  * batadv_tp_handle_out_of_order() - store an out of order packet
+  * @tp_vars: the private data of the current TP meter session
+- * @skb: the buffer containing the received packet
++ * @seqno: sequence number of new received packet
++ * @payload_len: length of the received packet
+  *
+  * Store the out of order packet in the unacked list for late processing. This
+  * packets are kept in this list so that they can be ACKed at once as soon as
+@@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  * Return: true if the packed has been successfully processed, false otherwise
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+-                                        const struct sk_buff *skb)
++                                        u32 seqno, u32 payload_len)
+       __must_hold(&tp_vars->unacked_lock)
+ {
+-      const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+-      u32 payload_len;
+       bool added = false;
+       new = kmalloc(sizeof(*new), GFP_ATOMIC);
+       if (unlikely(!new))
+               return false;
+-      icmp = (struct batadv_icmp_tp_packet *)skb->data;
+-
+-      new->seqno = ntohl(icmp->seqno);
+-      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      new->seqno = seqno;
+       new->len = payload_len;
+       /* if the list is empty immediately attach this new object */
+@@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+-      size_t packet_size;
++      u32 payload_len;
+       u32 to_ack;
+       u32 seqno;
+@@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+-      if (batadv_seq_before(seqno, tp_vars->last_recv))
++      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      to_ack = seqno + payload_len;
++      if (batadv_seq_before(to_ack, tp_vars->last_recv))
+               goto send_ack;
+       /* if the packet is out of order enqueue it */
+-      if (ntohl(icmp->seqno) != tp_vars->last_recv) {
++      if (batadv_seq_before(tp_vars->last_recv, seqno)) {
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++              if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
+                       spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
+               }
+@@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       }
+       /* if everything was fine count the ACKed bytes */
+-      packet_size = skb->len - sizeof(struct batadv_unicast_packet);
+-      tp_vars->last_recv += packet_size;
++      tp_vars->last_recv = to_ack;
+       /* check if this ordered message filled a gap.... */
+       batadv_tp_ack_unordered(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-5.15/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
new file mode 100644 (file)
index 0000000..079f48d
--- /dev/null
@@ -0,0 +1,40 @@
+From 5fdc6dd0ccb9afc5fe090618e841aba968c13545 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:46 +0200
+Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery
+ detection
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream.
+
+The recover variable and the last_sent sequence number are initialized on
+purpose as a really high value which will wrap-around after the first 2000
+bytes. The fast recovery precondition must therefore not use simple integer
+comparisons but use helpers which are aware of the sequence number
+wrap-arounds.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 1fc8d6e6f99b30..7385cd02abf9f0 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (tp_vars->recover >= recv_ack)
++              if (!batadv_seq_before(tp_vars->recover, recv_ack))
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-5.15/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
new file mode 100644 (file)
index 0000000..8ed5559
--- /dev/null
@@ -0,0 +1,40 @@
+From 0d496b785dde951ccba9bef8260da9ffa0c1ddfe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:42 +0200
+Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit febfb1b86224489535312296ecfa3d4bf467f339 upstream.
+
+When batadv_tp_update_cwnd() is called, dec_cwnd is increased. But dec_cwnd
+is only initialixed (to 0) when a duplicate Ack was received or when cwnd
+is below the ss_threshold.
+
+Just initialize the cwnd during the initialization to avoid any potential
+access of uninitialized data.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 2b68eacd7bf51d..f7b0ce3f8e2aef 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1055,6 +1055,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+        * soft_interface, hence its MTU
+        */
+       tp_vars->cwnd = BATADV_TP_PLEN * 3;
++      tp_vars->dec_cwnd = 0;
++
+       /* at the beginning initialise the SS threshold to the biggest possible
+        * window size, hence the AWND size
+        */
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-5.15/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
new file mode 100644 (file)
index 0000000..48df58d
--- /dev/null
@@ -0,0 +1,42 @@
+From 26d78a9abbff74958875494c37408f37f111b6b0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:41 +0200
+Subject: batman-adv: tp_meter: initialize dup_acks explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b2b68b32a715e0328662801576974aa37b942b00 upstream.
+
+When an ack with a sequence number equal to the last_acked is received, the
+dup_acks counter is increased to decide whether fast retransmit should be
+performed. Only when the sequence numbers are not equal, the dup_acks is
+set to the initial value (0).
+
+But if the initial packet would have the sequence number
+BATADV_TP_FIRST_SEQ, dup_acks would not be initialized and atomic_inc would
+operate on an undefined starting value. It is therefore required to have it
+explicitly initialized during the start of the sender session.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index e8792e1e4eedfa..2b68eacd7bf51d 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1045,6 +1045,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->icmp_uid = icmp_uid;
+       tp_vars->last_sent = BATADV_TP_FIRST_SEQ;
++      atomic_set(&tp_vars->dup_acks, 0);
+       atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ);
+       tp_vars->fast_recovery = false;
+       tp_vars->recover = BATADV_TP_FIRST_SEQ;
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-5.15/batman-adv-tp_meter-initialize-last_recv_time-during.patch
new file mode 100644 (file)
index 0000000..bc624c7
--- /dev/null
@@ -0,0 +1,68 @@
+From 2f594743543b43d26878981f25380c6758124313 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:50 +0200
+Subject: batman-adv: tp_meter: initialize last_recv_time during init
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream.
+
+The last_recv_time is the most important indicator for a receiver session
+to figure out whether a session timed out or not. But this information was
+only initialized after the session was added to the tp_receiver_list and
+after the timer was started.
+
+In the worst case, the timer (function) could have tried to access this
+information before the actual initialization was reached. Like rest of the
+variables of the tp_meter receiver session, this field has to be filled out
+before any other (parallel running) context has the chance to access it.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 257f8d042d158e..9e31d86c4b87f5 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1403,8 +1403,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+-      if (tp_vars)
++      if (tp_vars) {
++              tp_vars->last_recv_time = jiffies;
+               goto out_unlock;
++      }
+       if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) {
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+@@ -1432,6 +1434,8 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      tp_vars->last_recv_time = jiffies;
++
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1480,9 +1484,9 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                                  icmp->orig);
+                       goto out;
+               }
+-      }
+-      tp_vars->last_recv_time = jiffies;
++              tp_vars->last_recv_time = jiffies;
++      }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-5.15/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
new file mode 100644 (file)
index 0000000..e5a1d3c
--- /dev/null
@@ -0,0 +1,44 @@
+From bbdabf1821e04892e99790640f6eacf39c8c8ff5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:40 +0200
+Subject: batman-adv: tp_meter: keep unacked list in ascending ordered
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream.
+
+When batadv_tp_handle_out_of_order inserts a new entry in the list of
+unacked (out of order) packets, it searches from the entry with the newest
+sequence number towards oldest sequence number. If an entry is found which
+is older than the newly entry, the new entry has to be added after the
+found one to keep the ascending order.
+
+But for this operation list_add_tail() was used. But this function adds an
+entry _before_ another one. As result, the list would contain a lot of
+swapped sequence numbers. The consumer of this list
+(batadv_tp_ack_unordered()) would then fail to correctly ack packets.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index c7b48d51e2aed2..e8792e1e4eedfa 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1325,7 +1325,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                * one is attached _after_ it. In this way the list is kept in
+                * ascending order
+                */
+-              list_add_tail(&new->list, &un->list);
++              list_add(&new->list, &un->list);
+               added = true;
+               break;
+       }
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-5.15/batman-adv-tp_meter-prevent-parallel-modifications-o.patch
new file mode 100644 (file)
index 0000000..158c259
--- /dev/null
@@ -0,0 +1,157 @@
+From 8284d148758d64bf934574dd56ee614a1fdf2198 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:58 +0200
+Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream.
+
+When last_recv is updated to store the last receive sequence number, it is
+assuming that nothing is modifying in parallel while:
+
+* check for outdated packets is done
+* out of order check is performed (and packets are stored in out-of-order
+  queue)
+* the out-of-order queue was searched for closed gaps
+* sequence number for next ack is calculated
+
+Nothing of that was actually protected. It could therefore happen that the
+last_recv was updated multiple times in parallel and the final sequence
+number was calculated with deltas which had no connection to the sequence
+number they were added to.
+
+Lock this whole region with the same lock which was already used to protect
+the unacked (out-of-order) list.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 22 +++++++++++++---------
+ net/batman-adv/types.h    |  2 +-
+ 2 files changed, 14 insertions(+), 10 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 503819e9839c62..83749d7b4fd112 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                                         const struct sk_buff *skb)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+@@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       payload_len = skb->len - sizeof(struct batadv_unicast_packet);
+       new->len = payload_len;
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
+               tp_vars->unacked_count++;
+-              goto out;
++              return true;
+       }
+       /* otherwise loop over the list and either drop the packet because this
+@@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+               tp_vars->unacked_count--;
+       }
+-out:
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+-
+       return true;
+ }
+@@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+  * @tp_vars: the private data of the current TP meter session
+  */
+ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       struct batadv_tp_unacked *un, *safe;
+       u32 to_ack;
+@@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+       /* go through the unacked packet list and possibly ACK them as
+        * well
+        */
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               /* the list is ordered, therefore it is possible to stop as soon
+                * there is a gap between the last acked seqno and the seqno of
+@@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               kfree(un);
+               tp_vars->unacked_count--;
+       }
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+ /**
+@@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+       size_t packet_size;
++      u32 to_ack;
+       u32 seqno;
+       icmp = (struct batadv_icmp_tp_packet *)skb->data;
+@@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
++      spin_lock_bh(&tp_vars->unacked_lock);
++
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+@@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb))
++              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++                      spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
++              }
+               /* send a duplicate ACK */
+               goto send_ack;
+@@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       batadv_tp_ack_unordered(tp_vars);
+ send_ack:
++      to_ack = tp_vars->last_recv;
++      spin_unlock_bh(&tp_vars->unacked_lock);
++
+       /* send the ACK. If the received packet was out of order, the ACK that
+        * is going to be sent is a duplicate (the sender will count them and
+        * possibly enter Fast Retransmit as soon as it has reached 3)
+        */
+-      batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv,
++      batadv_tp_send_ack(bat_priv, icmp->orig, to_ack,
+                          icmp->timestamp, icmp->session, icmp->uid);
+ out:
+       batadv_tp_vars_put(tp_vars);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index d7fb3046d04412..e1ca1359415bc7 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1491,7 +1491,7 @@ struct batadv_tp_vars {
+       /** @unacked_list: list of unacked packets (meta-info only) */
+       struct list_head unacked_list;
+-      /** @unacked_lock: protect unacked_list */
++      /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
+       spinlock_t unacked_lock;
+       /** @unacked_count: number of unacked entries */
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-5.15/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
new file mode 100644 (file)
index 0000000..35b5b1b
--- /dev/null
@@ -0,0 +1,124 @@
+From df7081f69f2061e11ca745b2452ca6c98c96f3f7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:56 +0200
+Subject: batman-adv: tp_meter: restrict number of unacked list entries
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream.
+
+When the unacked_list is unbound, an attacker could send messages with
+small lengths and appropriated seqno + gaps to force the receiver to
+allocate more and more unacked_list entries. And the end either causing an
+out-of-memory situation or increase the management overhead for the (large)
+list that significant portions of CPU cycles are wasted in searching
+through the list.
+
+When limiting the list to a specific number, it is important to still
+correctly add a new entry to the list. But if the list became larger than
+the limit, the last entry of the list (with the highest seqno) must be
+dropped to still allow the earlier seqnos to finish and therefore to
+continue the process. Otherwise, the process might get stuck with too high
+seqnos which are not handled by batadv_tp_ack_unordered().
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++-
+ net/batman-adv/types.h    |  3 +++
+ 2 files changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 9e31d86c4b87f5..105cda5b0b2cda 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -87,6 +87,11 @@
+ #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \
+                       sizeof(struct batadv_unicast_packet))
++/**
++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack
++ */
++#define BATADV_TP_MAX_UNACKED 100
++
+ static u8 batadv_tp_prerandom[4096] __read_mostly;
+ /**
+@@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
+               goto out;
+       }
+@@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                */
+               list_add(&new->list, &un->list);
+               added = true;
++              tp_vars->unacked_count++;
+               break;
+       }
+       /* received packet with smallest seqno out of order; add it to front */
+-      if (!added)
++      if (!added) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
++      }
++
++      /* remove the last (biggest) unacked seqno when list is too large */
++      if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) {
++              un = list_last_entry(&tp_vars->unacked_list,
++                                   struct batadv_tp_unacked, list);
++              list_del(&un->list);
++              kfree(un);
++              tp_vars->unacked_count--;
++      }
+ out:
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+@@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       spin_lock_init(&tp_vars->unacked_lock);
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
++      tp_vars->unacked_count = 0;
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index d298a3983fab97..d7fb3046d04412 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1494,6 +1494,9 @@ struct batadv_tp_vars {
+       /** @unacked_lock: protect unacked_list */
+       spinlock_t unacked_lock;
++      /** @unacked_count: number of unacked entries */
++      size_t unacked_count;
++
+       /** @last_recv_time: time (jiffies) a msg was received */
+       unsigned long last_recv_time;
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-5.15/batman-adv-tt-don-t-merge-change-entries-with-differ.patch
new file mode 100644 (file)
index 0000000..a227db4
--- /dev/null
@@ -0,0 +1,46 @@
+From a17c210562764a37e97b52b5137dbf30b08c25e4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:00 +0200
+Subject: batman-adv: tt: don't merge change entries with different VIDs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream.
+
+batadv_tt_local_event() merges/cancels events for the same client which
+would conflict or be duplicates. The matching of the queued events only
+compares the MAC address - the VLAN ID stored in each event is ignored.
+
+If a MAC would now appear on multiple VID, the two ADD change events (for
+VID 1 and VID 2) would be merged to a single vid event. The remote can
+therefore not calculate the correct TT table and desync. A full translation
+table exchange is required to recover from this state.
+
+A check of VID is therefore necessary to avoid such wrong merges/cancels.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 8ab1257bade048..4bc73aceeb89f6 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -485,6 +485,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
+               if (!batadv_compare_eth(entry->change.addr, common->addr))
+                       continue;
++              if (entry->change.vid != tt_change_node->change.vid)
++                      continue;
++
+               /* DEL+ADD in the same orig interval have no effect and can be
+                * removed to avoid silly behaviour on the receiver side. The
+                * other way around (ADD+DEL) can happen in case of roaming of
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tt-track-roam-count-per-vid.patch b/queue-5.15/batman-adv-tt-track-roam-count-per-vid.patch
new file mode 100644 (file)
index 0000000..7c0d003
--- /dev/null
@@ -0,0 +1,89 @@
+From 674b41f57012ff41b1bd6b64ae87a79ea87fa6f1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:01 +0200
+Subject: batman-adv: tt: track roam count per VID
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream.
+
+batadv_tt_check_roam_count() is supposed to track roaming of a TT entry.
+But TT entries are for a MAC + VID. The VID was completely missed and thus
+leads to incorrect detection of ROAM counts when a client MAC exists in
+multiple VLANs.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 9 +++++++--
+ net/batman-adv/types.h             | 3 +++
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 4bc73aceeb89f6..53ac49ca2c2f1d 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -3517,6 +3517,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  * batadv_tt_check_roam_count() - check if a client has roamed too frequently
+  * @bat_priv: the bat priv with all the soft interface information
+  * @client: mac address of the roaming client
++ * @vid: VLAN identifier
+  *
+  * This function checks whether the client already reached the
+  * maximum number of possible roaming phases. In this case the ROAMING_ADV
+@@ -3524,7 +3525,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  *
+  * Return: true if the ROAMING_ADV can be sent, false otherwise
+  */
+-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid)
+ {
+       struct batadv_tt_roam_node *tt_roam_node;
+       bool ret = false;
+@@ -3537,6 +3538,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               if (!batadv_compare_eth(tt_roam_node->addr, client))
+                       continue;
++              if (tt_roam_node->vid != vid)
++                      continue;
++
+               if (batadv_has_timed_out(tt_roam_node->first_time,
+                                        BATADV_ROAMING_MAX_TIME))
+                       continue;
+@@ -3558,6 +3562,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               atomic_set(&tt_roam_node->counter,
+                          BATADV_ROAMING_MAX_COUNT - 1);
+               ether_addr_copy(tt_roam_node->addr, client);
++              tt_roam_node->vid = vid;
+               list_add(&tt_roam_node->list, &bat_priv->tt.roam_list);
+               ret = true;
+@@ -3594,7 +3599,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
+       /* before going on we have to check whether the client has
+        * already roamed to us too many times
+        */
+-      if (!batadv_tt_check_roam_count(bat_priv, client))
++      if (!batadv_tt_check_roam_count(bat_priv, client, vid))
+               goto out;
+       batadv_dbg(BATADV_DBG_TT, bat_priv,
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index e1ca1359415bc7..f0d5701265ef48 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -2034,6 +2034,9 @@ struct batadv_tt_roam_node {
+       /** @addr: mac address of the client in the roaming phase */
+       u8 addr[ETH_ALEN];
++      /** @vid: VLAN identifier */
++      u16 vid;
++
+       /**
+        * @counter: number of allowed roaming events per client within a single
+        * OGM interval (changes are committed with each OGM)
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-5.15/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
new file mode 100644 (file)
index 0000000..7a6bfb1
--- /dev/null
@@ -0,0 +1,155 @@
+From 792d8b5b518a080dc39a800ee7ce47f591dfcf58 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:04 +0200
+Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream.
+
+TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to
+signal that the OGM handler should be called (with NULL for data) when the
+specific TVLV container was not found in the OGM. This is used by:
+
+* DAT
+* GW
+* Multicast (OGM + Tracker)
+
+The state whether the handler was executed was stored in the struct
+batadv_tvlv_handler. But the TVLV processing is started without any lock.
+Multiple parallel contexts processing TVLVs would therefore overwrite each
+others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared
+batadv_tvlv_handler.
+
+Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine,
+per TVLV buffer, whether a matching container was present by scanning the
+packet's buffer.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tvlv.c  | 63 ++++++++++++++++++++++++++++++++++++++----
+ net/batman-adv/types.h |  7 -----
+ 2 files changed, 57 insertions(+), 13 deletions(-)
+
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 66c2043dc1df91..6eb95bd7a4bc36 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -394,7 +394,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+               tvlv_handler->ogm_handler(bat_priv, orig_node,
+                                         BATADV_NO_FLAGS,
+                                         tvlv_value, tvlv_value_len);
+-              tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
+       } else {
+               if (!src)
+                       return NET_RX_SUCCESS;
+@@ -413,6 +412,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+       return NET_RX_SUCCESS;
+ }
++/**
++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
++ * @tvlv_value: tvlv content
++ * @tvlv_value_len: tvlv content length
++ * @type: tvlv container type to look for
++ * @version: tvlv container version to look for
++ *
++ * Return: true if a container of the given type and version is present in the
++ * tvlv buffer, false otherwise.
++ */
++static bool batadv_tvlv_containers_contain(void *tvlv_value,
++                                         u16 tvlv_value_len, u8 type,
++                                         u8 version)
++{
++      struct batadv_tvlv_hdr *tvlv_hdr;
++      u16 tvlv_value_cont_len;
++
++      while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
++              tvlv_hdr = tvlv_value;
++              tvlv_value_cont_len = ntohs(tvlv_hdr->len);
++              tvlv_value = tvlv_hdr + 1;
++              tvlv_value_len -= sizeof(*tvlv_hdr);
++
++              if (tvlv_value_cont_len > tvlv_value_len)
++                      break;
++
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
++              if (tvlv_hdr->type == type && tvlv_hdr->version == version)
++                      return true;
++
++              tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
++              tvlv_value_len -= tvlv_value_cont_len;
++      }
++
++      return false;
++}
++
+ /**
+  * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the
+  *  appropriate handlers
+@@ -433,7 +474,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+                                  u8 *src, u8 *dst,
+                                  void *tvlv_value, u16 tvlv_value_len)
+ {
++      u16 tvlv_value_start_len = tvlv_value_len;
+       struct batadv_tvlv_handler *tvlv_handler;
++      void *tvlv_value_start = tvlv_value;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       u16 tvlv_value_cont_len;
+       u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
+@@ -473,12 +516,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(tvlv_handler,
+                                &bat_priv->tvlv.handler_list, list) {
+-              if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
+-                  !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
+-                      tvlv_handler->ogm_handler(bat_priv, orig_node,
+-                                                cifnotfound, NULL, 0);
++              if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND))
++                      continue;
+-              tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
++              /* if the corresponding container was present then the handler
++               * was already called from the loop above
++               */
++              if (batadv_tvlv_containers_contain(tvlv_value_start,
++                                                 tvlv_value_start_len,
++                                                 tvlv_handler->type,
++                                                 tvlv_handler->version))
++                      continue;
++
++              tvlv_handler->ogm_handler(bat_priv, orig_node,
++                                        cifnotfound, NULL, 0);
+       }
+       rcu_read_unlock();
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index f0d5701265ef48..586679ad574f72 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -2447,13 +2447,6 @@ enum batadv_tvlv_handler_flags {
+        *  will call this handler even if its type was not found (with no data)
+        */
+       BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
+-
+-      /**
+-       * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the
+-       *  API marks a handler as being called, so it won't be called if the
+-       *  BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
+-       */
+-      BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
+ };
+ #endif /* _NET_BATMAN_ADV_TYPES_H_ */
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-5.15/batman-adv-tvlv-enforce-2-byte-alignment.patch
new file mode 100644 (file)
index 0000000..18a9d9c
--- /dev/null
@@ -0,0 +1,106 @@
+From 289176ea3b5875d64153e062f8e313f3b7317877 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:03 +0200
+Subject: batman-adv: tvlv: enforce 2-byte alignment
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream.
+
+The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte
+alignment, so a following OGM must start at an even offset. As the header
+length is even, an odd tvlv_len would misalign it and trigger unaligned
+accesses on strict-alignment architectures.
+
+Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in
+the mesh. Therefore, reject such malformed packets.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+[ Drop change for non-existing mcast handling ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 11 ++++++++++-
+ net/batman-adv/bat_v_ogm.c  | 11 ++++++++++-
+ net/batman-adv/tvlv.c       |  6 ++++++
+ 3 files changed, 26 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index 669f77eed073a0..a424c5b7a2462c 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -311,14 +311,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
+                         const struct batadv_ogm_packet *ogm_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm_packet->tvlv_len);
++
++      /* the fields of an aggregated OGM are accessed assuming (at least)
++       * 2-byte alignment, so a following OGM must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index c7d15887c47c8e..641f3dbde1bde2 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -854,14 +854,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len,
+                        const struct batadv_ogm2_packet *ogm2_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm2_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm2_packet->tvlv_len);
++
++      /* the fields of an aggregated OGMv2 are accessed assuming (at least)
++       * 2-byte alignment, so a following OGMv2 must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm2_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index de0c139426839c..66c2043dc1df91 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -448,6 +448,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+               if (tvlv_value_cont_len > tvlv_value_len)
+                       break;
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
+               tvlv_handler = batadv_tvlv_handler_get(bat_priv,
+                                                      tvlv_hdr->type,
+                                                      tvlv_hdr->version);
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-5.15/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
new file mode 100644 (file)
index 0000000..4654b9a
--- /dev/null
@@ -0,0 +1,106 @@
+From 6fcda727f2744048d3e2bcb36b04d68ae6c7e5cc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:55 +0200
+Subject: batman-adv: v: prevent OGM aggregation on disabled hardif
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream.
+
+When an interface gets disabled, the worker is correctly disabled by
+batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable().
+In this process, the skb aggr_list is also freed.
+
+But batadv_v_ogm_send_meshif() can still queue new skbs (via
+batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all
+cores can no longer find the RCU protected list of hard interfaces. These
+queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work.
+
+The batadv_v_ogm_iface_disable() function must block
+batadv_v_ogm_queue_on_if() to avoid leak of skbs.
+
+Cc: stable@kernel.org
+Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_v.c     |  1 +
+ net/batman-adv/bat_v_ogm.c | 12 ++++++++++++
+ net/batman-adv/types.h     |  6 ++++++
+ 3 files changed, 19 insertions(+)
+
+diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c
+index 651e01b86141e3..34874942ae8d03 100644
+--- a/net/batman-adv/bat_v.c
++++ b/net/batman-adv/bat_v.c
+@@ -838,6 +838,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
+       hard_iface->bat_v.aggr_len = 0;
+       skb_queue_head_init(&hard_iface->bat_v.aggr_list);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq,
+                         batadv_v_ogm_aggr_work);
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 63337e02cf2f11..c7d15887c47c8e 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -255,11 +255,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
+       }
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      if (!hard_iface->bat_v.aggr_list_enabled) {
++              kfree_skb(skb);
++              goto unlock;
++      }
++
+       if (!batadv_v_ogm_queue_left(skb, hard_iface))
+               batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+       __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
++
++unlock:
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+@@ -422,6 +429,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
++      spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = true;
++      spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
++
+       batadv_v_ogm_start_queue_timer(hard_iface);
+       batadv_v_ogm_start_timer(bat_priv);
+@@ -437,6 +448,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
+       cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq);
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       batadv_v_ogm_aggr_list_free(hard_iface);
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index f1a835edd115c7..d298a3983fab97 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v {
+       /** @aggr_list: queue for to be aggregated OGM packets */
+       struct sk_buff_head aggr_list;
++      /**
++       * @aggr_list_enabled: aggr_list is active and new skbs can be
++       * enqueued. Protected by aggr_list.lock after initialization
++       */
++      bool aggr_list_enabled:1;
++
+       /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */
+       unsigned int aggr_len;
+-- 
+2.53.0
+
index 0f0db55aee359032066822e91f185c9c75d72f98..0a9cc78a3f876bc1d728e9b606935c3b84fd4ad2 100644 (file)
@@ -32,3 +32,28 @@ kvm-x86-fix-shadow-paging-use-after-free-due-to-unex.patch
 kvm-x86-fix-shadow-paging-use-after-free-due-to-unex.patch-23529
 kvm-x86-mmu-ensure-hugepage-is-in-by-slot-before-che.patch
 kselftest-arm64-signal-skip-sve-signal-test-if-not-e.patch
+batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
+batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
+batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
+batman-adv-tp_meter-avoid-window-underflow.patch
+batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
+batman-adv-tp_meter-fix-fast-recovery-precondition.patch
+batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
+batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
+batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
+batman-adv-prevent-elp-transmission-interval-underfl.patch
+batman-adv-tp_meter-initialize-last_recv_time-during.patch
+batman-adv-ensure-bcast-is-writable-before-modifying.patch
+batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch
+batman-adv-frag-ensure-fragment-is-writable-before-m.patch
+batman-adv-frag-avoid-underflow-of-ttl.patch
+batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
+batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
+batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
+batman-adv-tp_meter-prevent-parallel-modifications-o.patch
+batman-adv-tp_meter-handle-overlapping-packets.patch
+batman-adv-tt-don-t-merge-change-entries-with-differ.patch
+batman-adv-tt-track-roam-count-per-vid.patch
+batman-adv-dat-prevent-false-sharing-between-vlans.patch
+batman-adv-tvlv-enforce-2-byte-alignment.patch
+batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
diff --git a/queue-6.1/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-6.1/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
new file mode 100644 (file)
index 0000000..e923424
--- /dev/null
@@ -0,0 +1,156 @@
+From 5a52ae127a87d497c511fc702390e9affad4a1bf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:07 +0200
+Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream.
+
+The lasttime field for claim, backbone_gw, and loopdetect tracks the
+jiffies value of the most recent activity and is used to detect timeouts.
+These accesses are not consistently protected by a lock, so
+READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler
+optimizations.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++-------------
+ 1 file changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index cfb1eb25c6ac4d..ac12c06f11b89e 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -512,7 +512,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
+               return NULL;
+       entry->vid = vid;
+-      entry->lasttime = jiffies;
++      WRITE_ONCE(entry->lasttime, jiffies);
+       entry->crc = BATADV_BLA_CRC_INIT;
+       entry->bat_priv = bat_priv;
+       spin_lock_init(&entry->crc_lock);
+@@ -580,7 +580,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv,
+       if (unlikely(!backbone_gw))
+               return;
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       batadv_backbone_gw_put(backbone_gw);
+ }
+@@ -714,7 +714,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+               ether_addr_copy(claim->addr, mac);
+               spin_lock_init(&claim->backbone_lock);
+               claim->vid = vid;
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               kref_get(&backbone_gw->refcount);
+               claim->backbone_gw = backbone_gw;
+               kref_init(&claim->refcount);
+@@ -736,7 +736,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+                       return;
+               }
+       } else {
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               if (claim->backbone_gw == backbone_gw)
+                       /* no need to register a new backbone */
+                       goto claim_free_ref;
+@@ -769,7 +769,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+       spin_lock_bh(&backbone_gw->crc_lock);
+       backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+ claim_free_ref:
+       batadv_claim_put(claim);
+@@ -858,7 +858,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
+               return true;
+       /* handle as ANNOUNCE frame */
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       crc = ntohs(*((__force __be16 *)(&an_addr[4])));
+       batadv_dbg(BATADV_DBG_BLA, bat_priv,
+@@ -1253,7 +1253,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
+                                                 head, hash_entry) {
+                               if (now)
+                                       goto purge_now;
+-                              if (!batadv_has_timed_out(backbone_gw->lasttime,
++                              if (!batadv_has_timed_out(READ_ONCE(backbone_gw->lasttime),
+                                                         BATADV_BLA_BACKBONE_TIMEOUT))
+                                       continue;
+@@ -1334,7 +1334,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
+                                               primary_if->net_dev->dev_addr))
+                               goto skip;
+-                      if (!batadv_has_timed_out(claim->lasttime,
++                      if (!batadv_has_timed_out(READ_ONCE(claim->lasttime),
+                                                 BATADV_BLA_CLAIM_TIMEOUT))
+                               goto skip;
+@@ -1494,7 +1494,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+               eth_random_addr(bat_priv->bla.loopdetect_addr);
+               bat_priv->bla.loopdetect_addr[0] = 0xba;
+               bat_priv->bla.loopdetect_addr[1] = 0xbe;
+-              bat_priv->bla.loopdetect_lasttime = jiffies;
++              WRITE_ONCE(bat_priv->bla.loopdetect_lasttime, jiffies);
+               atomic_set(&bat_priv->bla.loopdetect_next,
+                          BATADV_BLA_LOOPDETECT_PERIODS);
+@@ -1515,7 +1515,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                                               primary_if->net_dev->dev_addr))
+                               continue;
+-                      backbone_gw->lasttime = jiffies;
++                      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+                       batadv_bla_send_announce(bat_priv, backbone_gw);
+                       if (send_loopdetect)
+@@ -1900,7 +1900,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       /* If the packet came too late, don't forward it on the mesh
+        * but don't consider that as loop. It might be a coincidence.
+        */
+-      if (batadv_has_timed_out(bat_priv->bla.loopdetect_lasttime,
++      if (batadv_has_timed_out(READ_ONCE(bat_priv->bla.loopdetect_lasttime),
+                                BATADV_BLA_LOOPDETECT_TIMEOUT))
+               return true;
+@@ -2016,7 +2016,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       if (own_claim) {
+               /* ... allow it in any case */
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               goto allow;
+       }
+@@ -2118,7 +2118,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+               /* if yes, the client has roamed and we have
+                * to unclaim it.
+                */
+-              if (batadv_has_timed_out(claim->lasttime, 100)) {
++              if (batadv_has_timed_out(READ_ONCE(claim->lasttime), 100)) {
+                       /* only unclaim if the last claim entry is
+                        * older than 100 ms to make sure we really
+                        * have a roaming client here.
+@@ -2372,7 +2372,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid,
+       backbone_crc = backbone_gw->crc;
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime);
++      msecs = jiffies_to_msecs(jiffies - READ_ONCE(backbone_gw->lasttime));
+       if (is_own)
+               if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) {
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-6.1/batman-adv-dat-prevent-false-sharing-between-vlans.patch
new file mode 100644 (file)
index 0000000..13985e8
--- /dev/null
@@ -0,0 +1,57 @@
+From 27049ec18ed615ee2fbd658140d74246d7a81057 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:21 +0200
+Subject: batman-adv: dat: prevent false sharing between VLANs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream.
+
+The local hash of DAT entries is supposed to be VLAN (VID) aware. But
+the adding to the hash and the search in the hash were not checking the VID
+information of the hash entries. The entries would therefore only be
+correctly separated when batadv_hash_dat() didn't select the same buckets
+for different VIDs.
+
+Cc: stable@kernel.org
+Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/distributed-arp-table.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
+index e8d706d86aa82a..36462d9288c7ee 100644
+--- a/net/batman-adv/distributed-arp-table.c
++++ b/net/batman-adv/distributed-arp-table.c
+@@ -215,10 +215,13 @@ static void batadv_dat_purge(struct work_struct *work)
+  */
+ static bool batadv_compare_dat(const struct hlist_node *node, const void *data2)
+ {
+-      const void *data1 = container_of(node, struct batadv_dat_entry,
+-                                       hash_entry);
++      const struct batadv_dat_entry *entry1;
++      const struct batadv_dat_entry *entry2;
+-      return memcmp(data1, data2, sizeof(__be32)) == 0;
++      entry1 = container_of(node, struct batadv_dat_entry, hash_entry);
++      entry2 = data2;
++
++      return entry1->ip == entry2->ip && entry1->vid == entry2->vid;
+ }
+ /**
+@@ -345,6 +348,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
+               if (dat_entry->ip != ip)
+                       continue;
++              if (dat_entry->vid != vid)
++                      continue;
++
+               if (!kref_get_unless_zero(&dat_entry->refcount))
+                       continue;
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-ensure-bcast-is-writable-before-modifying.patch b/queue-6.1/batman-adv-ensure-bcast-is-writable-before-modifying.patch
new file mode 100644 (file)
index 0000000..e5526f3
--- /dev/null
@@ -0,0 +1,50 @@
+From 6632a50cba14b1852f5517c0df65ee5faddbcd25 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:10 +0200
+Subject: batman-adv: ensure bcast is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or used skb_cow() to ensure that the data part is not
+shared.
+
+The old implementation used a shared queue and created copies before
+attempting to write to it. But with the new implementation, the broadcast
+packet is already modified when it gets received. Potentially writing to
+shared buffers in this process.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to rebroadcast the
+packet.
+
+Cc: stable@kernel.org
+Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/routing.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 83f31494ea4d92..0709f42c54b496 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1199,6 +1199,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
+       if (batadv_is_my_mac(bat_priv, bcast_packet->orig))
+               goto free_skb;
++      /* create a copy of the skb, if needed, to modify it. */
++      if (skb_cow(skb, ETH_HLEN) < 0)
++              goto free_skb;
++
++      bcast_packet = (struct batadv_bcast_packet *)skb->data;
++
+       if (bcast_packet->ttl-- < 2)
+               goto free_skb;
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch b/queue-6.1/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch
new file mode 100644 (file)
index 0000000..3ec5451
--- /dev/null
@@ -0,0 +1,118 @@
+From 97713caef9bd3e682405f26528a5983714af0dad Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:11 +0200
+Subject: batman-adv: fix (m|b)cast csum after decrementing TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream.
+
+The broadcast and multicast packets can be received at the same time by the
+local system and forwarded to other nodes. Both are simply decrementing the
+TTL at the beginning of the receive path - independent of chosen paths
+(receive/forward). But such a modification of the data conflicts with the
+hw csum. This is not a problem when the packet is directly forwarded but
+can cause errors in the local receive path.
+
+Such a problem can then trigger a "hw csum failure". The receiver path must
+therefore ensure that the csum is fixed for each modification of the
+payload before batadv_interface_rx() is reached.
+
+Since all batman-adv packet types with a ttl have it as u8 at offset 2, a
+helper can be used for all of them. But it is only used at the moment for
+batadv_bcast_packet and batadv_mcast_packet because they are the only ones
+which deliver the packet locally but unconditionally modify the TTL.
+
+Cc: stable@kernel.org
+Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed")
+Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding")
+[ Context, Drop change for non-existing mcast handling ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/routing.c | 55 +++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 54 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 0709f42c54b496..e9347b25d2087a 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -8,6 +8,7 @@
+ #include "main.h"
+ #include <linux/atomic.h>
++#include <linux/build_bug.h>
+ #include <linux/byteorder/generic.h>
+ #include <linux/compiler.h>
+ #include <linux/errno.h>
+@@ -205,6 +206,58 @@ bool batadv_check_management_packet(struct sk_buff *skb,
+       return true;
+ }
++/**
++ * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe
++ * @skb: the received packet with @skb->data pointing to the batman-adv header
++ *
++ * Supports the following packet types, all of which carry the TTL at offset 2:
++ *
++ * - batadv_ogm_packet
++ * - batadv_ogm2_packet
++ * - batadv_icmp_header
++ * - batadv_icmp_packet
++ * - batadv_icmp_tp_packet
++ * - batadv_icmp_packet_rr
++ * - batadv_unicast_packet
++ * - batadv_frag_packet
++ * - batadv_bcast_packet
++ * - batadv_mcast_packet
++ * - batadv_coded_packet
++ * - batadv_unicast_tvlv_packet
++ *
++ * Return: true if the packet may be forwarded (ttl decremented),
++ *  false if it must be dropped (ttl would expire)
++ */
++static bool batadv_skb_decrement_ttl(struct sk_buff *skb)
++{
++      static const size_t ttl_offset = 2;
++      u8 *ttl_pos;
++
++      BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset);
++
++      ttl_pos = skb->data + ttl_offset;
++
++      /* would expire on this hop -> drop, leave header + csum untouched */
++      if (*ttl_pos < 2)
++              return false;
++
++      skb_postpull_rcsum(skb, ttl_pos, 1);
++      (*ttl_pos)--;
++      skb_postpush_rcsum(skb, ttl_pos, 1);
++
++      return true;
++}
++
+ /**
+  * batadv_recv_my_icmp_packet() - receive an icmp packet locally
+  * @bat_priv: the bat priv with all the soft interface information
+@@ -1205,7 +1258,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
+       bcast_packet = (struct batadv_bcast_packet *)skb->data;
+-      if (bcast_packet->ttl-- < 2)
++      if (!batadv_skb_decrement_ttl(skb))
+               goto free_skb;
+       orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig);
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-6.1/batman-adv-frag-avoid-underflow-of-ttl.patch
new file mode 100644 (file)
index 0000000..fa86c7b
--- /dev/null
@@ -0,0 +1,45 @@
+From 314a46c28a91be49dc3f83322f498ba03c6906df Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:13 +0200
+Subject: batman-adv: frag: avoid underflow of TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream.
+
+Packets with a TTL are using it to limit the amount of time this packet can
+be forwarded. But for batadv_frag_packet, the TTL was always only reduced
+but it was never evaluated. It could even underflow without any effect.
+
+Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for
+forwarding. This keeps it in sync with the not fragmented unicast packet.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index fbf030c57ac04d..0138b0953e10b5 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -420,6 +420,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (packet->ttl < 2) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
+               if (skb_cow(skb, ETH_HLEN) < 0) {
+                       kfree_skb(skb);
+                       *rx_result = NET_RX_DROP;
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-6.1/batman-adv-frag-ensure-fragment-is-writable-before-m.patch
new file mode 100644 (file)
index 0000000..1b6a65a
--- /dev/null
@@ -0,0 +1,107 @@
+From b22fa2d5eb7111575d51e5b4d3cf58b60d2542ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:12 +0200
+Subject: batman-adv: frag: ensure fragment is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or use skb_cow() to ensure that the data part is not
+shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to forward the
+fragment.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 15 ++++++++++++++-
+ net/batman-adv/fragmentation.h |  3 ++-
+ net/batman-adv/routing.c       |  3 +--
+ 3 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index 4c193194034155..fbf030c57ac04d 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -385,6 +385,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  * @skb: skb to forward
+  * @recv_if: interface that the skb is received on
+  * @orig_node_src: originator that the skb is received from
++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and
++ *  NET_RX_DROP when it was dropped; only valid when true is returned
+  *
+  * Look up the next-hop of the fragments payload and check if the merged packet
+  * will exceed the MTU towards the next-hop. If so, the fragment is forwarded
+@@ -394,7 +396,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  */
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src)
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
+       struct batadv_orig_node *orig_node_dst;
+@@ -417,12 +420,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (skb_cow(skb, ETH_HLEN) < 0) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
++              packet = (struct batadv_frag_packet *)skb->data;
++
+               batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
+               batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
+                                  skb->len + ETH_HLEN);
+               packet->ttl--;
+               batadv_send_unicast_skb(skb, neigh_node);
++              *rx_result = NET_RX_SUCCESS;
+               ret = true;
+       }
+diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h
+index dbf0871f870303..51e281027ab630 100644
+--- a/net/batman-adv/fragmentation.h
++++ b/net/batman-adv/fragmentation.h
+@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig,
+                           bool (*check_cb)(struct batadv_frag_table_entry *));
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src);
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result);
+ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+                           struct batadv_orig_node *orig_node);
+ int batadv_frag_send_packet(struct sk_buff *skb,
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index e9347b25d2087a..49cce0cbdff8b6 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1175,10 +1175,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb,
+       /* Route the fragment if it is not for us and too big to be merged. */
+       if (!batadv_is_my_mac(bat_priv, frag_packet->dest) &&
+-          batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) {
++          batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) {
+               /* skb was consumed */
+               skb = NULL;
+-              ret = NET_RX_SUCCESS;
+               goto put_orig_node;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-6.1/batman-adv-prevent-elp-transmission-interval-underfl.patch
new file mode 100644 (file)
index 0000000..6494bec
--- /dev/null
@@ -0,0 +1,52 @@
+From f5f9cf61085ff7967aaacbf7bc7824f342b2ffca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:08 +0200
+Subject: batman-adv: prevent ELP transmission interval underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream.
+
+batadv_v_elp_start_timer() enqeues a delayed work. The time when it starts
+is randomly chosen between (elp_interval - BATADV_JITTER) and
+(elp_interval + BATADV_JITTER). The configured elp_interval must therefore
+be larger or equal to BATADV_JITTER to avoid that it causes an underflow of
+the unsigned integer. If this would happen, then a "fast" ELP interval
+would turn into a "day long" delay.
+
+At the same time, it must not be larger than the maximum value the variable
+can store.
+
+Cc: stable@kernel.org
+Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/netlink.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
+index 86e0664e0511bb..7ff48c968a13fe 100644
+--- a/net/batman-adv/netlink.c
++++ b/net/batman-adv/netlink.c
+@@ -941,9 +941,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb,
+ #ifdef CONFIG_BATMAN_ADV_BATMAN_V
+       if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) {
++              u32 elp_interval;
++
+               attr = info->attrs[BATADV_ATTR_ELP_INTERVAL];
++              elp_interval = nla_get_u32(attr);
++
++              elp_interval = min_t(u32, elp_interval, INT_MAX);
++              elp_interval = max_t(u32, elp_interval, BATADV_JITTER);
+-              atomic_set(&hard_iface->bat_v.elp_interval, nla_get_u32(attr));
++              atomic_set(&hard_iface->bat_v.elp_interval, elp_interval);
+       }
+       if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) {
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-6.1/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
new file mode 100644 (file)
index 0000000..e61dc45
--- /dev/null
@@ -0,0 +1,74 @@
+From 48b7c9045ff6b24ec88ce85aee844255527a7244 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:06 +0200
+Subject: batman-adv: tp_meter: add only finished tp_vars to lists
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream.
+
+When the receiver variables (aka "session") are initialized, then they are
+added to the list of sessions before the timer is set up. A RCU protected
+reader could therefore find the entry and run mod_setup before
+batadv_tp_init_recv() finished the timer initialization.
+
+The same is true for batadv_tp_start(), which must first initialize the
+finish_work and the test_length to avoid a similar problem.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 089ac7cc9fbebc..2bba53fc6da5c0 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1096,21 +1096,21 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->prerandom_offset = 0;
+       spin_lock_init(&tp_vars->prerandom_lock);
+-      kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+-      spin_unlock_bh(&bat_priv->tp_list_lock);
+-
+       tp_vars->test_length = test_length;
+       if (!tp_vars->test_length)
+               tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH;
++      /* init work item for finished tp tests */
++      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
++
++      kref_get(&tp_vars->refcount);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      spin_unlock_bh(&bat_priv->tp_list_lock);
++
+       batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                  "Meter: starting throughput meter towards %pM (length=%ums)\n",
+                  dst, test_length);
+-      /* init work item for finished tp tests */
+-      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
+-
+       /* start tp kthread. This way the write() call issued from userspace can
+        * happily return and avoid to block
+        */
+@@ -1430,10 +1430,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
+       kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+       kref_get(&tp_vars->refcount);
+-      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+       batadv_tp_reset_receiver_timer(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-6.1/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
new file mode 100644 (file)
index 0000000..7af7868
--- /dev/null
@@ -0,0 +1,66 @@
+From ede0391be39ad21e7d159985fd99dbc756db8db3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:16 +0200
+Subject: batman-adv: tp_meter: annotate last_recv_time access with
+ READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream.
+
+The last_recv_time field for batadv_tp_receiver tracks the jiffies value of
+the most recent activity and is used to detect timeouts. These accesses are
+not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used
+to prevent data races caused by compiler optimizations.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 638dd438a6c052..b882919a868ca0 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       bat_priv = tp_vars->bat_priv;
+       /* if there is recent activity rearm the timer */
+-      if (!batadv_has_timed_out(tp_vars->last_recv_time,
++      if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time),
+                                 BATADV_TP_RECV_TIMEOUT)) {
+               /* reset the receiver shutdown timer */
+               batadv_tp_reset_receiver_timer(tp_vars);
+@@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+       if (tp_vars) {
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+               goto out_unlock;
+       }
+@@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+-      tp_vars->last_recv_time = jiffies;
++      WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                       goto out;
+               }
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-6.1/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
new file mode 100644 (file)
index 0000000..5f80b9a
--- /dev/null
@@ -0,0 +1,64 @@
+From 7749dcd68bed6dc3142962efd15fae37974c5bfd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:03 +0200
+Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream.
+
+The cwnd is always MSS <= cwnd <= 0x20000000. But the calculation in
+batadv_tp_update_cwnd() assumes unsigned 32 bit arithmetics.
+
+    ((mss * 8) ** 2) / (cwnd * 8)
+
+In case cwnd is actually 0x20000000, it will be shifted by 3 bit to the
+left end up at 0x100000000 or U32_MAX + 1. It will therefore wrap around
+and be 0 - resulting in:
+
+    ((mss * 8) ** 2) / 0
+
+This is of course invalid and cannot be calculated. The calculation should
+must be simplified to avoid this overflow:
+
+   (mss ** 2) * 8 / cwnd
+
+It will keep the precision enhancement from the scaling (by 8) but avoid
+the overflow in the divisor.
+
+In theory, there could still be an overflow in the dividend. It is at the
+moment fixed to BATADV_TP_PLEN in batadv_tp_recv_ack() - so it is not an
+imminent problem. But allowing it to use the whole u32 bit range, would
+mean that it can still use up to 67 bits. To keep this calculation safe for
+32 bit arithmetic, mss must never use more than floor((32 - 3) / 2) bits -
+or in other words: must never be larger than 16383.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 0fdcafca3aa02b..4ff80e4214ff0a 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -154,9 +154,12 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss)
+               return;
+       }
++      /* prevent overflow in (mss * mss) << 3 */
++      mss = min_t(u32, mss, (1U << 14) - 1);
++
+       /* increment CWND at least of 1 (section 3.1 of RFC5681) */
+       tp_vars->dec_cwnd += max_t(u32, 1U << 3,
+-                                 ((mss * mss) << 6) / (tp_vars->cwnd << 3));
++                                 ((mss * mss) << 3) / tp_vars->cwnd);
+       if (tp_vars->dec_cwnd < (mss << 3)) {
+               spin_unlock_bh(&tp_vars->cwnd_lock);
+               return;
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-6.1/batman-adv-tp_meter-avoid-window-underflow.patch
new file mode 100644 (file)
index 0000000..770717b
--- /dev/null
@@ -0,0 +1,56 @@
+From 4aca6456212d50fc31e6ff514ef1c61b8a003c8e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:02 +0200
+Subject: batman-adv: tp_meter: avoid window underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream.
+
+In batadv_tp_avail(), win_left is calculated with 32-bit unsigned
+arithmetic: win_left = win_limit - tp_vars->last_sent;
+
+During Fast Recovery, cwnd is inflated and last_sent advances rapidly. When
+Fast Recovery ends, cwnd drops abruptly back to ss_threshold. If the newly
+shrunk win_limit is less than last_sent, the unsigned subtraction will
+underflow, wrapping to a massive positive value. Instead of returning that
+the window is full (unavailable), it returns that the sender can continue
+sending.
+
+To handle this situation, it must be checked whether the windows end
+sequence number (win_limit) has to be compared with the last sent sequence
+number. If it would be before the last sent sequence number, then more acks
+are needed before the transmission can be started again.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index f6ccb639744a2a..0fdcafca3aa02b 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+ static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars,
+                           size_t payload_len)
+ {
++      u32 last_sent = READ_ONCE(tp_vars->last_sent);
+       u32 win_left, win_limit;
+       win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd;
+-      win_left = win_limit - tp_vars->last_sent;
++
++      if (batadv_seq_before(last_sent, win_limit))
++              win_left = win_limit - last_sent;
++      else
++              win_left = 0;
+       return win_left >= payload_len;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-6.1/batman-adv-tp_meter-fix-fast-recovery-precondition.patch
new file mode 100644 (file)
index 0000000..02a7c43
--- /dev/null
@@ -0,0 +1,48 @@
+From dd6c5523b0be5cdaceda49f5f13c067fa275fde6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:04 +0200
+Subject: batman-adv: tp_meter: fix fast recovery precondition
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream.
+
+The fast recovery precondition checks if the recover (initialized to
+BATADV_TP_FIRST_SEQ) is bigger than the received ack. But since recover is
+only updated when this check is successful, it will never enter the fast
+recovery mode.
+
+According to RFC6582 Section 3.2 step 2, the check should actually be
+different:
+
+> When the third duplicate ACK is received, the TCP sender first
+> checks the value of recover to see if the Cumulative
+> Acknowledgment field covers more than recover
+
+The precondition must therefore check if recover is smaller than the
+received ack - basically swapping the operands of the current check.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 4ff80e4214ff0a..c79352cfddc4ae 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (recv_ack >= tp_vars->recover)
++              if (tp_vars->recover >= recv_ack)
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-6.1/batman-adv-tp_meter-handle-overlapping-packets.patch
new file mode 100644 (file)
index 0000000..7703bab
--- /dev/null
@@ -0,0 +1,109 @@
+From ba1e89c0adeb1b5a17ae3c71e1bf020738400886 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:18 +0200
+Subject: batman-adv: tp_meter: handle overlapping packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit cbde75c38b21f022891525078622587ad557b7c1 upstream.
+
+If the size of the packets would change during the transmission, it could
+happen that some retries of packets are overlapping. In this case, precise
+comparisons of sequence numbers by the receiver would be wrong. It is then
+necessary to check if the start sequence number to the end sequence number
+("seqno + length") would contain a new range.
+
+If this is the case then this is enough to accept this packet. In all other
+cases, the packet still has to be dropped (and not acked).
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 25 +++++++++++--------------
+ 1 file changed, 11 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index f745bc09a04963..50b83e0438a07b 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+ /**
+  * batadv_tp_handle_out_of_order() - store an out of order packet
+  * @tp_vars: the private data of the current TP meter session
+- * @skb: the buffer containing the received packet
++ * @seqno: sequence number of new received packet
++ * @payload_len: length of the received packet
+  *
+  * Store the out of order packet in the unacked list for late processing. This
+  * packets are kept in this list so that they can be ACKed at once as soon as
+@@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  * Return: true if the packed has been successfully processed, false otherwise
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+-                                        const struct sk_buff *skb)
++                                        u32 seqno, u32 payload_len)
+       __must_hold(&tp_vars->unacked_lock)
+ {
+-      const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+-      u32 payload_len;
+       bool added = false;
+       new = kmalloc(sizeof(*new), GFP_ATOMIC);
+       if (unlikely(!new))
+               return false;
+-      icmp = (struct batadv_icmp_tp_packet *)skb->data;
+-
+-      new->seqno = ntohl(icmp->seqno);
+-      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      new->seqno = seqno;
+       new->len = payload_len;
+       /* if the list is empty immediately attach this new object */
+@@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+-      size_t packet_size;
++      u32 payload_len;
+       u32 to_ack;
+       u32 seqno;
+@@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+-      if (batadv_seq_before(seqno, tp_vars->last_recv))
++      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      to_ack = seqno + payload_len;
++      if (batadv_seq_before(to_ack, tp_vars->last_recv))
+               goto send_ack;
+       /* if the packet is out of order enqueue it */
+-      if (ntohl(icmp->seqno) != tp_vars->last_recv) {
++      if (batadv_seq_before(tp_vars->last_recv, seqno)) {
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++              if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
+                       spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
+               }
+@@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       }
+       /* if everything was fine count the ACKed bytes */
+-      packet_size = skb->len - sizeof(struct batadv_unicast_packet);
+-      tp_vars->last_recv += packet_size;
++      tp_vars->last_recv = to_ack;
+       /* check if this ordered message filled a gap.... */
+       batadv_tp_ack_unordered(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-6.1/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
new file mode 100644 (file)
index 0000000..33bfff1
--- /dev/null
@@ -0,0 +1,40 @@
+From 38fc401a35eb38b318195cb72df11b66a029ba45 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:05 +0200
+Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery
+ detection
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream.
+
+The recover variable and the last_sent sequence number are initialized on
+purpose as a really high value which will wrap-around after the first 2000
+bytes. The fast recovery precondition must therefore not use simple integer
+comparisons but use helpers which are aware of the sequence number
+wrap-arounds.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index c79352cfddc4ae..089ac7cc9fbebc 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (tp_vars->recover >= recv_ack)
++              if (!batadv_seq_before(tp_vars->recover, recv_ack))
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-6.1/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
new file mode 100644 (file)
index 0000000..72a9f74
--- /dev/null
@@ -0,0 +1,40 @@
+From e873fb927f6012a780059788943b6b6a2ca0509f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:01 +0200
+Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit febfb1b86224489535312296ecfa3d4bf467f339 upstream.
+
+When batadv_tp_update_cwnd() is called, dec_cwnd is increased. But dec_cwnd
+is only initialixed (to 0) when a duplicate Ack was received or when cwnd
+is below the ss_threshold.
+
+Just initialize the cwnd during the initialization to avoid any potential
+access of uninitialized data.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 750e5e9d3dc9d6..f6ccb639744a2a 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1055,6 +1055,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+        * soft_interface, hence its MTU
+        */
+       tp_vars->cwnd = BATADV_TP_PLEN * 3;
++      tp_vars->dec_cwnd = 0;
++
+       /* at the beginning initialise the SS threshold to the biggest possible
+        * window size, hence the AWND size
+        */
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-6.1/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
new file mode 100644 (file)
index 0000000..759ff62
--- /dev/null
@@ -0,0 +1,42 @@
+From 32e6439ebb546a129578459ed5168feddaed52d7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:00 +0200
+Subject: batman-adv: tp_meter: initialize dup_acks explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b2b68b32a715e0328662801576974aa37b942b00 upstream.
+
+When an ack with a sequence number equal to the last_acked is received, the
+dup_acks counter is increased to decide whether fast retransmit should be
+performed. Only when the sequence numbers are not equal, the dup_acks is
+set to the initial value (0).
+
+But if the initial packet would have the sequence number
+BATADV_TP_FIRST_SEQ, dup_acks would not be initialized and atomic_inc would
+operate on an undefined starting value. It is therefore required to have it
+explicitly initialized during the start of the sender session.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index f350a60e6c76b9..750e5e9d3dc9d6 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1045,6 +1045,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->icmp_uid = icmp_uid;
+       tp_vars->last_sent = BATADV_TP_FIRST_SEQ;
++      atomic_set(&tp_vars->dup_acks, 0);
+       atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ);
+       tp_vars->fast_recovery = false;
+       tp_vars->recover = BATADV_TP_FIRST_SEQ;
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-6.1/batman-adv-tp_meter-initialize-last_recv_time-during.patch
new file mode 100644 (file)
index 0000000..3cdd774
--- /dev/null
@@ -0,0 +1,68 @@
+From 44d8f1adc3b995e0aab69fd66ca19f4eebe6cf47 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:09 +0200
+Subject: batman-adv: tp_meter: initialize last_recv_time during init
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream.
+
+The last_recv_time is the most important indicator for a receiver session
+to figure out whether a session timed out or not. But this information was
+only initialized after the session was added to the tp_receiver_list and
+after the timer was started.
+
+In the worst case, the timer (function) could have tried to access this
+information before the actual initialization was reached. Like rest of the
+variables of the tp_meter receiver session, this field has to be filled out
+before any other (parallel running) context has the chance to access it.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 2bba53fc6da5c0..133eed2fa9507f 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1403,8 +1403,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+-      if (tp_vars)
++      if (tp_vars) {
++              tp_vars->last_recv_time = jiffies;
+               goto out_unlock;
++      }
+       if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) {
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+@@ -1432,6 +1434,8 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      tp_vars->last_recv_time = jiffies;
++
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1480,9 +1484,9 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                                  icmp->orig);
+                       goto out;
+               }
+-      }
+-      tp_vars->last_recv_time = jiffies;
++              tp_vars->last_recv_time = jiffies;
++      }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-6.1/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
new file mode 100644 (file)
index 0000000..33f60b2
--- /dev/null
@@ -0,0 +1,44 @@
+From cc1388bbbf10cbfce38716bebd2fec199c425152 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:10:59 +0200
+Subject: batman-adv: tp_meter: keep unacked list in ascending ordered
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream.
+
+When batadv_tp_handle_out_of_order inserts a new entry in the list of
+unacked (out of order) packets, it searches from the entry with the newest
+sequence number towards oldest sequence number. If an entry is found which
+is older than the newly entry, the new entry has to be added after the
+found one to keep the ascending order.
+
+But for this operation list_add_tail() was used. But this function adds an
+entry _before_ another one. As result, the list would contain a lot of
+swapped sequence numbers. The consumer of this list
+(batadv_tp_ack_unordered()) would then fail to correctly ack packets.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index dfc3374549921f..f350a60e6c76b9 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1325,7 +1325,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                * one is attached _after_ it. In this way the list is kept in
+                * ascending order
+                */
+-              list_add_tail(&new->list, &un->list);
++              list_add(&new->list, &un->list);
+               added = true;
+               break;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-6.1/batman-adv-tp_meter-prevent-parallel-modifications-o.patch
new file mode 100644 (file)
index 0000000..6a05f49
--- /dev/null
@@ -0,0 +1,157 @@
+From e5656e4d6f83c88da8e02e42e93e0538668507f7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:17 +0200
+Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream.
+
+When last_recv is updated to store the last receive sequence number, it is
+assuming that nothing is modifying in parallel while:
+
+* check for outdated packets is done
+* out of order check is performed (and packets are stored in out-of-order
+  queue)
+* the out-of-order queue was searched for closed gaps
+* sequence number for next ack is calculated
+
+Nothing of that was actually protected. It could therefore happen that the
+last_recv was updated multiple times in parallel and the final sequence
+number was calculated with deltas which had no connection to the sequence
+number they were added to.
+
+Lock this whole region with the same lock which was already used to protect
+the unacked (out-of-order) list.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 22 +++++++++++++---------
+ net/batman-adv/types.h    |  2 +-
+ 2 files changed, 14 insertions(+), 10 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index b882919a868ca0..f745bc09a04963 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                                         const struct sk_buff *skb)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+@@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       payload_len = skb->len - sizeof(struct batadv_unicast_packet);
+       new->len = payload_len;
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
+               tp_vars->unacked_count++;
+-              goto out;
++              return true;
+       }
+       /* otherwise loop over the list and either drop the packet because this
+@@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+               tp_vars->unacked_count--;
+       }
+-out:
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+-
+       return true;
+ }
+@@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+  * @tp_vars: the private data of the current TP meter session
+  */
+ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       struct batadv_tp_unacked *un, *safe;
+       u32 to_ack;
+@@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+       /* go through the unacked packet list and possibly ACK them as
+        * well
+        */
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               /* the list is ordered, therefore it is possible to stop as soon
+                * there is a gap between the last acked seqno and the seqno of
+@@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               kfree(un);
+               tp_vars->unacked_count--;
+       }
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+ /**
+@@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+       size_t packet_size;
++      u32 to_ack;
+       u32 seqno;
+       icmp = (struct batadv_icmp_tp_packet *)skb->data;
+@@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
++      spin_lock_bh(&tp_vars->unacked_lock);
++
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+@@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb))
++              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++                      spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
++              }
+               /* send a duplicate ACK */
+               goto send_ack;
+@@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       batadv_tp_ack_unordered(tp_vars);
+ send_ack:
++      to_ack = tp_vars->last_recv;
++      spin_unlock_bh(&tp_vars->unacked_lock);
++
+       /* send the ACK. If the received packet was out of order, the ACK that
+        * is going to be sent is a duplicate (the sender will count them and
+        * possibly enter Fast Retransmit as soon as it has reached 3)
+        */
+-      batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv,
++      batadv_tp_send_ack(bat_priv, icmp->orig, to_ack,
+                          icmp->timestamp, icmp->session, icmp->uid);
+ out:
+       batadv_tp_vars_put(tp_vars);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index d66726d0ea8112..0a6d0303a616c5 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1491,7 +1491,7 @@ struct batadv_tp_vars {
+       /** @unacked_list: list of unacked packets (meta-info only) */
+       struct list_head unacked_list;
+-      /** @unacked_lock: protect unacked_list */
++      /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
+       spinlock_t unacked_lock;
+       /** @unacked_count: number of unacked entries */
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-6.1/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
new file mode 100644 (file)
index 0000000..3c8d0ce
--- /dev/null
@@ -0,0 +1,124 @@
+From ab5601dbe335fdfb45f756d336418edb2f780630 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:15 +0200
+Subject: batman-adv: tp_meter: restrict number of unacked list entries
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream.
+
+When the unacked_list is unbound, an attacker could send messages with
+small lengths and appropriated seqno + gaps to force the receiver to
+allocate more and more unacked_list entries. And the end either causing an
+out-of-memory situation or increase the management overhead for the (large)
+list that significant portions of CPU cycles are wasted in searching
+through the list.
+
+When limiting the list to a specific number, it is important to still
+correctly add a new entry to the list. But if the list became larger than
+the limit, the last entry of the list (with the highest seqno) must be
+dropped to still allow the earlier seqnos to finish and therefore to
+continue the process. Otherwise, the process might get stuck with too high
+seqnos which are not handled by batadv_tp_ack_unordered().
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++-
+ net/batman-adv/types.h    |  3 +++
+ 2 files changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 133eed2fa9507f..638dd438a6c052 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -87,6 +87,11 @@
+ #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \
+                       sizeof(struct batadv_unicast_packet))
++/**
++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack
++ */
++#define BATADV_TP_MAX_UNACKED 100
++
+ static u8 batadv_tp_prerandom[4096] __read_mostly;
+ /**
+@@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
+               goto out;
+       }
+@@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                */
+               list_add(&new->list, &un->list);
+               added = true;
++              tp_vars->unacked_count++;
+               break;
+       }
+       /* received packet with smallest seqno out of order; add it to front */
+-      if (!added)
++      if (!added) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
++      }
++
++      /* remove the last (biggest) unacked seqno when list is too large */
++      if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) {
++              un = list_last_entry(&tp_vars->unacked_list,
++                                   struct batadv_tp_unacked, list);
++              list_del(&un->list);
++              kfree(un);
++              tp_vars->unacked_count--;
++      }
+ out:
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+@@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       spin_lock_init(&tp_vars->unacked_lock);
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
++      tp_vars->unacked_count = 0;
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index a61e319e4db0e9..d66726d0ea8112 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1494,6 +1494,9 @@ struct batadv_tp_vars {
+       /** @unacked_lock: protect unacked_list */
+       spinlock_t unacked_lock;
++      /** @unacked_count: number of unacked entries */
++      size_t unacked_count;
++
+       /** @last_recv_time: time (jiffies) a msg was received */
+       unsigned long last_recv_time;
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-6.1/batman-adv-tt-don-t-merge-change-entries-with-differ.patch
new file mode 100644 (file)
index 0000000..147ba9f
--- /dev/null
@@ -0,0 +1,46 @@
+From 22a90a94c2949165086caa3a6ad1dff0285b06ba Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:19 +0200
+Subject: batman-adv: tt: don't merge change entries with different VIDs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream.
+
+batadv_tt_local_event() merges/cancels events for the same client which
+would conflict or be duplicates. The matching of the queued events only
+compares the MAC address - the VLAN ID stored in each event is ignored.
+
+If a MAC would now appear on multiple VID, the two ADD change events (for
+VID 1 and VID 2) would be merged to a single vid event. The remote can
+therefore not calculate the correct TT table and desync. A full translation
+table exchange is required to recover from this state.
+
+A check of VID is therefore necessary to avoid such wrong merges/cancels.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 53d2aadcafa154..c3d62df9e2b87d 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -485,6 +485,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
+               if (!batadv_compare_eth(entry->change.addr, common->addr))
+                       continue;
++              if (entry->change.vid != tt_change_node->change.vid)
++                      continue;
++
+               /* DEL+ADD in the same orig interval have no effect and can be
+                * removed to avoid silly behaviour on the receiver side. The
+                * other way around (ADD+DEL) can happen in case of roaming of
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tt-track-roam-count-per-vid.patch b/queue-6.1/batman-adv-tt-track-roam-count-per-vid.patch
new file mode 100644 (file)
index 0000000..ee0d8b3
--- /dev/null
@@ -0,0 +1,89 @@
+From f2e2e77009d59b556ec40f518a2b48e021027b17 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:20 +0200
+Subject: batman-adv: tt: track roam count per VID
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream.
+
+batadv_tt_check_roam_count() is supposed to track roaming of a TT entry.
+But TT entries are for a MAC + VID. The VID was completely missed and thus
+leads to incorrect detection of ROAM counts when a client MAC exists in
+multiple VLANs.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 9 +++++++--
+ net/batman-adv/types.h             | 3 +++
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index c3d62df9e2b87d..5161ddf1c788e0 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -3517,6 +3517,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  * batadv_tt_check_roam_count() - check if a client has roamed too frequently
+  * @bat_priv: the bat priv with all the soft interface information
+  * @client: mac address of the roaming client
++ * @vid: VLAN identifier
+  *
+  * This function checks whether the client already reached the
+  * maximum number of possible roaming phases. In this case the ROAMING_ADV
+@@ -3524,7 +3525,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  *
+  * Return: true if the ROAMING_ADV can be sent, false otherwise
+  */
+-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid)
+ {
+       struct batadv_tt_roam_node *tt_roam_node;
+       bool ret = false;
+@@ -3537,6 +3538,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               if (!batadv_compare_eth(tt_roam_node->addr, client))
+                       continue;
++              if (tt_roam_node->vid != vid)
++                      continue;
++
+               if (batadv_has_timed_out(tt_roam_node->first_time,
+                                        BATADV_ROAMING_MAX_TIME))
+                       continue;
+@@ -3558,6 +3562,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               atomic_set(&tt_roam_node->counter,
+                          BATADV_ROAMING_MAX_COUNT - 1);
+               ether_addr_copy(tt_roam_node->addr, client);
++              tt_roam_node->vid = vid;
+               list_add(&tt_roam_node->list, &bat_priv->tt.roam_list);
+               ret = true;
+@@ -3594,7 +3599,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
+       /* before going on we have to check whether the client has
+        * already roamed to us too many times
+        */
+-      if (!batadv_tt_check_roam_count(bat_priv, client))
++      if (!batadv_tt_check_roam_count(bat_priv, client, vid))
+               goto out;
+       batadv_dbg(BATADV_DBG_TT, bat_priv,
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 0a6d0303a616c5..673b04dd71b043 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1995,6 +1995,9 @@ struct batadv_tt_roam_node {
+       /** @addr: mac address of the client in the roaming phase */
+       u8 addr[ETH_ALEN];
++      /** @vid: VLAN identifier */
++      u16 vid;
++
+       /**
+        * @counter: number of allowed roaming events per client within a single
+        * OGM interval (changes are committed with each OGM)
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-6.1/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
new file mode 100644 (file)
index 0000000..32f461a
--- /dev/null
@@ -0,0 +1,155 @@
+From c052a0b7183276a112af8373678e639cf36a50fb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:23 +0200
+Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream.
+
+TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to
+signal that the OGM handler should be called (with NULL for data) when the
+specific TVLV container was not found in the OGM. This is used by:
+
+* DAT
+* GW
+* Multicast (OGM + Tracker)
+
+The state whether the handler was executed was stored in the struct
+batadv_tvlv_handler. But the TVLV processing is started without any lock.
+Multiple parallel contexts processing TVLVs would therefore overwrite each
+others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared
+batadv_tvlv_handler.
+
+Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine,
+per TVLV buffer, whether a matching container was present by scanning the
+packet's buffer.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tvlv.c  | 63 ++++++++++++++++++++++++++++++++++++++----
+ net/batman-adv/types.h |  7 -----
+ 2 files changed, 57 insertions(+), 13 deletions(-)
+
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index e25430da5afb1b..61a1e5e5f0d913 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -394,7 +394,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+               tvlv_handler->ogm_handler(bat_priv, orig_node,
+                                         BATADV_NO_FLAGS,
+                                         tvlv_value, tvlv_value_len);
+-              tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
+       } else {
+               if (!src)
+                       return NET_RX_SUCCESS;
+@@ -413,6 +412,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+       return NET_RX_SUCCESS;
+ }
++/**
++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
++ * @tvlv_value: tvlv content
++ * @tvlv_value_len: tvlv content length
++ * @type: tvlv container type to look for
++ * @version: tvlv container version to look for
++ *
++ * Return: true if a container of the given type and version is present in the
++ * tvlv buffer, false otherwise.
++ */
++static bool batadv_tvlv_containers_contain(void *tvlv_value,
++                                         u16 tvlv_value_len, u8 type,
++                                         u8 version)
++{
++      struct batadv_tvlv_hdr *tvlv_hdr;
++      u16 tvlv_value_cont_len;
++
++      while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
++              tvlv_hdr = tvlv_value;
++              tvlv_value_cont_len = ntohs(tvlv_hdr->len);
++              tvlv_value = tvlv_hdr + 1;
++              tvlv_value_len -= sizeof(*tvlv_hdr);
++
++              if (tvlv_value_cont_len > tvlv_value_len)
++                      break;
++
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
++              if (tvlv_hdr->type == type && tvlv_hdr->version == version)
++                      return true;
++
++              tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
++              tvlv_value_len -= tvlv_value_cont_len;
++      }
++
++      return false;
++}
++
+ /**
+  * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the
+  *  appropriate handlers
+@@ -433,7 +474,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+                                  u8 *src, u8 *dst,
+                                  void *tvlv_value, u16 tvlv_value_len)
+ {
++      u16 tvlv_value_start_len = tvlv_value_len;
+       struct batadv_tvlv_handler *tvlv_handler;
++      void *tvlv_value_start = tvlv_value;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       u16 tvlv_value_cont_len;
+       u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
+@@ -473,12 +516,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(tvlv_handler,
+                                &bat_priv->tvlv.handler_list, list) {
+-              if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
+-                  !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
+-                      tvlv_handler->ogm_handler(bat_priv, orig_node,
+-                                                cifnotfound, NULL, 0);
++              if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND))
++                      continue;
+-              tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
++              /* if the corresponding container was present then the handler
++               * was already called from the loop above
++               */
++              if (batadv_tvlv_containers_contain(tvlv_value_start,
++                                                 tvlv_value_start_len,
++                                                 tvlv_handler->type,
++                                                 tvlv_handler->version))
++                      continue;
++
++              tvlv_handler->ogm_handler(bat_priv, orig_node,
++                                        cifnotfound, NULL, 0);
+       }
+       rcu_read_unlock();
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 673b04dd71b043..f009b2a381a468 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -2408,13 +2408,6 @@ enum batadv_tvlv_handler_flags {
+        *  will call this handler even if its type was not found (with no data)
+        */
+       BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
+-
+-      /**
+-       * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the
+-       *  API marks a handler as being called, so it won't be called if the
+-       *  BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
+-       */
+-      BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
+ };
+ #endif /* _NET_BATMAN_ADV_TYPES_H_ */
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-6.1/batman-adv-tvlv-enforce-2-byte-alignment.patch
new file mode 100644 (file)
index 0000000..98146ef
--- /dev/null
@@ -0,0 +1,106 @@
+From 70e7b2220fad294bafebc8640a223ac08d825f01 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:22 +0200
+Subject: batman-adv: tvlv: enforce 2-byte alignment
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream.
+
+The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte
+alignment, so a following OGM must start at an even offset. As the header
+length is even, an odd tvlv_len would misalign it and trigger unaligned
+accesses on strict-alignment architectures.
+
+Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in
+the mesh. Therefore, reject such malformed packets.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+[ Drop change for non-existing mcast handling ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 11 ++++++++++-
+ net/batman-adv/bat_v_ogm.c  | 11 ++++++++++-
+ net/batman-adv/tvlv.c       |  6 ++++++
+ 3 files changed, 26 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index c52e3b82889868..7fd692b642e392 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -311,14 +311,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
+                         const struct batadv_ogm_packet *ogm_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm_packet->tvlv_len);
++
++      /* the fields of an aggregated OGM are accessed assuming (at least)
++       * 2-byte alignment, so a following OGM must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index f87c2b80e4291e..e8ad4817d20018 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -854,14 +854,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len,
+                        const struct batadv_ogm2_packet *ogm2_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm2_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm2_packet->tvlv_len);
++
++      /* the fields of an aggregated OGMv2 are accessed assuming (at least)
++       * 2-byte alignment, so a following OGMv2 must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm2_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 99e5e8518dcc9a..e25430da5afb1b 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -448,6 +448,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+               if (tvlv_value_cont_len > tvlv_value_len)
+                       break;
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
+               tvlv_handler = batadv_tvlv_handler_get(bat_priv,
+                                                      tvlv_hdr->type,
+                                                      tvlv_hdr->version);
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-6.1/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
new file mode 100644 (file)
index 0000000..a5ce23c
--- /dev/null
@@ -0,0 +1,106 @@
+From f349fccf6e64eb281a526e38664a966944e11974 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:14 +0200
+Subject: batman-adv: v: prevent OGM aggregation on disabled hardif
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream.
+
+When an interface gets disabled, the worker is correctly disabled by
+batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable().
+In this process, the skb aggr_list is also freed.
+
+But batadv_v_ogm_send_meshif() can still queue new skbs (via
+batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all
+cores can no longer find the RCU protected list of hard interfaces. These
+queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work.
+
+The batadv_v_ogm_iface_disable() function must block
+batadv_v_ogm_queue_on_if() to avoid leak of skbs.
+
+Cc: stable@kernel.org
+Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_v.c     |  1 +
+ net/batman-adv/bat_v_ogm.c | 12 ++++++++++++
+ net/batman-adv/types.h     |  6 ++++++
+ 3 files changed, 19 insertions(+)
+
+diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c
+index 651e01b86141e3..34874942ae8d03 100644
+--- a/net/batman-adv/bat_v.c
++++ b/net/batman-adv/bat_v.c
+@@ -838,6 +838,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
+       hard_iface->bat_v.aggr_len = 0;
+       skb_queue_head_init(&hard_iface->bat_v.aggr_list);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq,
+                         batadv_v_ogm_aggr_work);
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 57d0c6862f449c..f87c2b80e4291e 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -255,11 +255,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
+       }
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      if (!hard_iface->bat_v.aggr_list_enabled) {
++              kfree_skb(skb);
++              goto unlock;
++      }
++
+       if (!batadv_v_ogm_queue_left(skb, hard_iface))
+               batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+       __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
++
++unlock:
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+@@ -422,6 +429,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
++      spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = true;
++      spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
++
+       batadv_v_ogm_start_queue_timer(hard_iface);
+       batadv_v_ogm_start_timer(bat_priv);
+@@ -437,6 +448,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
+       cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq);
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       batadv_v_ogm_aggr_list_free(hard_iface);
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index ca1c258faa095f..a61e319e4db0e9 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v {
+       /** @aggr_list: queue for to be aggregated OGM packets */
+       struct sk_buff_head aggr_list;
++      /**
++       * @aggr_list_enabled: aggr_list is active and new skbs can be
++       * enqueued. Protected by aggr_list.lock after initialization
++       */
++      bool aggr_list_enabled:1;
++
+       /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */
+       unsigned int aggr_len;
+-- 
+2.53.0
+
index c7c870b56faa6181ffba1013480e403c4c8c1a69..8b6c0b06e8327873959a0f3d3a16c8b46dcd8c6f 100644 (file)
@@ -47,3 +47,28 @@ kvm-x86-fix-shadow-paging-use-after-free-due-to-unex.patch
 kvm-x86-mmu-ensure-hugepage-is-in-by-slot-before-che.patch
 revert-ptp-add-testptp-mask-test.patch
 mm-mglru-skip-special-vmas-in-lru_gen_look_around.patch
+batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
+batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
+batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
+batman-adv-tp_meter-avoid-window-underflow.patch
+batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
+batman-adv-tp_meter-fix-fast-recovery-precondition.patch
+batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
+batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
+batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
+batman-adv-prevent-elp-transmission-interval-underfl.patch
+batman-adv-tp_meter-initialize-last_recv_time-during.patch
+batman-adv-ensure-bcast-is-writable-before-modifying.patch
+batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch
+batman-adv-frag-ensure-fragment-is-writable-before-m.patch
+batman-adv-frag-avoid-underflow-of-ttl.patch
+batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
+batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
+batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
+batman-adv-tp_meter-prevent-parallel-modifications-o.patch
+batman-adv-tp_meter-handle-overlapping-packets.patch
+batman-adv-tt-don-t-merge-change-entries-with-differ.patch
+batman-adv-tt-track-roam-count-per-vid.patch
+batman-adv-dat-prevent-false-sharing-between-vlans.patch
+batman-adv-tvlv-enforce-2-byte-alignment.patch
+batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
diff --git a/queue-6.12/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-6.12/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
new file mode 100644 (file)
index 0000000..83c9ee7
--- /dev/null
@@ -0,0 +1,156 @@
+From 8f7ad6fa32ea7f5f6fa95ab7c016d02973aba88d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:38 +0200
+Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream.
+
+The lasttime field for claim, backbone_gw, and loopdetect tracks the
+jiffies value of the most recent activity and is used to detect timeouts.
+These accesses are not consistently protected by a lock, so
+READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler
+optimizations.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++-------------
+ 1 file changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index 15aeb07285e61f..cc6c14cac06282 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -512,7 +512,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
+               return NULL;
+       entry->vid = vid;
+-      entry->lasttime = jiffies;
++      WRITE_ONCE(entry->lasttime, jiffies);
+       entry->crc = BATADV_BLA_CRC_INIT;
+       entry->bat_priv = bat_priv;
+       spin_lock_init(&entry->crc_lock);
+@@ -580,7 +580,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv,
+       if (unlikely(!backbone_gw))
+               return;
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       batadv_backbone_gw_put(backbone_gw);
+ }
+@@ -714,7 +714,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+               ether_addr_copy(claim->addr, mac);
+               spin_lock_init(&claim->backbone_lock);
+               claim->vid = vid;
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               kref_get(&backbone_gw->refcount);
+               claim->backbone_gw = backbone_gw;
+               kref_init(&claim->refcount);
+@@ -736,7 +736,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+                       return;
+               }
+       } else {
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               if (claim->backbone_gw == backbone_gw)
+                       /* no need to register a new backbone */
+                       goto claim_free_ref;
+@@ -769,7 +769,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+       spin_lock_bh(&backbone_gw->crc_lock);
+       backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+ claim_free_ref:
+       batadv_claim_put(claim);
+@@ -858,7 +858,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
+               return true;
+       /* handle as ANNOUNCE frame */
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       crc = ntohs(*((__force __be16 *)(&an_addr[4])));
+       batadv_dbg(BATADV_DBG_BLA, bat_priv,
+@@ -1253,7 +1253,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
+                                                 head, hash_entry) {
+                               if (now)
+                                       goto purge_now;
+-                              if (!batadv_has_timed_out(backbone_gw->lasttime,
++                              if (!batadv_has_timed_out(READ_ONCE(backbone_gw->lasttime),
+                                                         BATADV_BLA_BACKBONE_TIMEOUT))
+                                       continue;
+@@ -1334,7 +1334,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
+                                               primary_if->net_dev->dev_addr))
+                               goto skip;
+-                      if (!batadv_has_timed_out(claim->lasttime,
++                      if (!batadv_has_timed_out(READ_ONCE(claim->lasttime),
+                                                 BATADV_BLA_CLAIM_TIMEOUT))
+                               goto skip;
+@@ -1494,7 +1494,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+               eth_random_addr(bat_priv->bla.loopdetect_addr);
+               bat_priv->bla.loopdetect_addr[0] = 0xba;
+               bat_priv->bla.loopdetect_addr[1] = 0xbe;
+-              bat_priv->bla.loopdetect_lasttime = jiffies;
++              WRITE_ONCE(bat_priv->bla.loopdetect_lasttime, jiffies);
+               atomic_set(&bat_priv->bla.loopdetect_next,
+                          BATADV_BLA_LOOPDETECT_PERIODS);
+@@ -1515,7 +1515,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                                               primary_if->net_dev->dev_addr))
+                               continue;
+-                      backbone_gw->lasttime = jiffies;
++                      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+                       batadv_bla_send_announce(bat_priv, backbone_gw);
+                       if (send_loopdetect)
+@@ -1900,7 +1900,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       /* If the packet came too late, don't forward it on the mesh
+        * but don't consider that as loop. It might be a coincidence.
+        */
+-      if (batadv_has_timed_out(bat_priv->bla.loopdetect_lasttime,
++      if (batadv_has_timed_out(READ_ONCE(bat_priv->bla.loopdetect_lasttime),
+                                BATADV_BLA_LOOPDETECT_TIMEOUT))
+               return true;
+@@ -2016,7 +2016,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       if (own_claim) {
+               /* ... allow it in any case */
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               goto allow;
+       }
+@@ -2118,7 +2118,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+               /* if yes, the client has roamed and we have
+                * to unclaim it.
+                */
+-              if (batadv_has_timed_out(claim->lasttime, 100)) {
++              if (batadv_has_timed_out(READ_ONCE(claim->lasttime), 100)) {
+                       /* only unclaim if the last claim entry is
+                        * older than 100 ms to make sure we really
+                        * have a roaming client here.
+@@ -2372,7 +2372,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid,
+       backbone_crc = backbone_gw->crc;
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime);
++      msecs = jiffies_to_msecs(jiffies - READ_ONCE(backbone_gw->lasttime));
+       if (is_own)
+               if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) {
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-6.12/batman-adv-dat-prevent-false-sharing-between-vlans.patch
new file mode 100644 (file)
index 0000000..f92a19d
--- /dev/null
@@ -0,0 +1,57 @@
+From ccbf0785c5e840d366ff4c4978fea86733a0ae66 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:52 +0200
+Subject: batman-adv: dat: prevent false sharing between VLANs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream.
+
+The local hash of DAT entries is supposed to be VLAN (VID) aware. But
+the adding to the hash and the search in the hash were not checking the VID
+information of the hash entries. The entries would therefore only be
+correctly separated when batadv_hash_dat() didn't select the same buckets
+for different VIDs.
+
+Cc: stable@kernel.org
+Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/distributed-arp-table.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
+index 4d53e7c3ea5463..bd5477263ecd7c 100644
+--- a/net/batman-adv/distributed-arp-table.c
++++ b/net/batman-adv/distributed-arp-table.c
+@@ -215,10 +215,13 @@ static void batadv_dat_purge(struct work_struct *work)
+  */
+ static bool batadv_compare_dat(const struct hlist_node *node, const void *data2)
+ {
+-      const void *data1 = container_of(node, struct batadv_dat_entry,
+-                                       hash_entry);
++      const struct batadv_dat_entry *entry1;
++      const struct batadv_dat_entry *entry2;
+-      return memcmp(data1, data2, sizeof(__be32)) == 0;
++      entry1 = container_of(node, struct batadv_dat_entry, hash_entry);
++      entry2 = data2;
++
++      return entry1->ip == entry2->ip && entry1->vid == entry2->vid;
+ }
+ /**
+@@ -345,6 +348,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
+               if (dat_entry->ip != ip)
+                       continue;
++              if (dat_entry->vid != vid)
++                      continue;
++
+               if (!kref_get_unless_zero(&dat_entry->refcount))
+                       continue;
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-ensure-bcast-is-writable-before-modifying.patch b/queue-6.12/batman-adv-ensure-bcast-is-writable-before-modifying.patch
new file mode 100644 (file)
index 0000000..13b6f9d
--- /dev/null
@@ -0,0 +1,50 @@
+From 9459d407a92bb0848d0407197ecd0a5361c558c1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:41 +0200
+Subject: batman-adv: ensure bcast is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or used skb_cow() to ensure that the data part is not
+shared.
+
+The old implementation used a shared queue and created copies before
+attempting to write to it. But with the new implementation, the broadcast
+packet is already modified when it gets received. Potentially writing to
+shared buffers in this process.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to rebroadcast the
+packet.
+
+Cc: stable@kernel.org
+Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/routing.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index f1061985149fc5..32a40c1c115c6a 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1198,6 +1198,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
+       if (batadv_is_my_mac(bat_priv, bcast_packet->orig))
+               goto free_skb;
++      /* create a copy of the skb, if needed, to modify it. */
++      if (skb_cow(skb, ETH_HLEN) < 0)
++              goto free_skb;
++
++      bcast_packet = (struct batadv_bcast_packet *)skb->data;
++
+       if (bcast_packet->ttl-- < 2)
+               goto free_skb;
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch b/queue-6.12/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch
new file mode 100644 (file)
index 0000000..c4e33bd
--- /dev/null
@@ -0,0 +1,128 @@
+From 7a4ead9dab9c3e66c277a9f85fdcdedb553e4517 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:42 +0200
+Subject: batman-adv: fix (m|b)cast csum after decrementing TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream.
+
+The broadcast and multicast packets can be received at the same time by the
+local system and forwarded to other nodes. Both are simply decrementing the
+TTL at the beginning of the receive path - independent of chosen paths
+(receive/forward). But such a modification of the data conflicts with the
+hw csum. This is not a problem when the packet is directly forwarded but
+can cause errors in the local receive path.
+
+Such a problem can then trigger a "hw csum failure". The receiver path must
+therefore ensure that the csum is fixed for each modification of the
+payload before batadv_interface_rx() is reached.
+
+Since all batman-adv packet types with a ttl have it as u8 at offset 2, a
+helper can be used for all of them. But it is only used at the moment for
+batadv_bcast_packet and batadv_mcast_packet because they are the only ones
+which deliver the packet locally but unconditionally modify the TTL.
+
+Cc: stable@kernel.org
+Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed")
+Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/routing.c | 58 ++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 56 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 32a40c1c115c6a..85bc2d284a7798 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -8,6 +8,7 @@
+ #include "main.h"
+ #include <linux/atomic.h>
++#include <linux/build_bug.h>
+ #include <linux/byteorder/generic.h>
+ #include <linux/compiler.h>
+ #include <linux/errno.h>
+@@ -205,6 +206,59 @@ bool batadv_check_management_packet(struct sk_buff *skb,
+       return true;
+ }
++/**
++ * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe
++ * @skb: the received packet with @skb->data pointing to the batman-adv header
++ *
++ * Supports the following packet types, all of which carry the TTL at offset 2:
++ *
++ * - batadv_ogm_packet
++ * - batadv_ogm2_packet
++ * - batadv_icmp_header
++ * - batadv_icmp_packet
++ * - batadv_icmp_tp_packet
++ * - batadv_icmp_packet_rr
++ * - batadv_unicast_packet
++ * - batadv_frag_packet
++ * - batadv_bcast_packet
++ * - batadv_mcast_packet
++ * - batadv_coded_packet
++ * - batadv_unicast_tvlv_packet
++ *
++ * Return: true if the packet may be forwarded (ttl decremented),
++ *  false if it must be dropped (ttl would expire)
++ */
++static bool batadv_skb_decrement_ttl(struct sk_buff *skb)
++{
++      static const size_t ttl_offset = 2;
++      u8 *ttl_pos;
++
++      BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_mcast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset);
++
++      ttl_pos = skb->data + ttl_offset;
++
++      /* would expire on this hop -> drop, leave header + csum untouched */
++      if (*ttl_pos < 2)
++              return false;
++
++      skb_postpull_rcsum(skb, ttl_pos, 1);
++      (*ttl_pos)--;
++      skb_postpush_rcsum(skb, ttl_pos, 1);
++
++      return true;
++}
++
+ /**
+  * batadv_recv_my_icmp_packet() - receive an icmp packet locally
+  * @bat_priv: the bat priv with all the soft interface information
+@@ -1204,7 +1258,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
+       bcast_packet = (struct batadv_bcast_packet *)skb->data;
+-      if (bcast_packet->ttl-- < 2)
++      if (!batadv_skb_decrement_ttl(skb))
+               goto free_skb;
+       orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig);
+@@ -1311,7 +1365,7 @@ int batadv_recv_mcast_packet(struct sk_buff *skb,
+               goto free_skb;
+       mcast_packet = (struct batadv_mcast_packet *)skb->data;
+-      if (mcast_packet->ttl-- < 2)
++      if (!batadv_skb_decrement_ttl(skb))
+               goto free_skb;
+       tvlv_buff = (unsigned char *)(skb->data + hdr_size);
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-6.12/batman-adv-frag-avoid-underflow-of-ttl.patch
new file mode 100644 (file)
index 0000000..75ceccc
--- /dev/null
@@ -0,0 +1,45 @@
+From a621528e2c42e781f48ebba1caedf6528469e82e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:44 +0200
+Subject: batman-adv: frag: avoid underflow of TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream.
+
+Packets with a TTL are using it to limit the amount of time this packet can
+be forwarded. But for batadv_frag_packet, the TTL was always only reduced
+but it was never evaluated. It could even underflow without any effect.
+
+Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for
+forwarding. This keeps it in sync with the not fragmented unicast packet.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index f6714405b0f948..375c9499121346 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -415,6 +415,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (packet->ttl < 2) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
+               if (skb_cow(skb, ETH_HLEN) < 0) {
+                       kfree_skb(skb);
+                       *rx_result = NET_RX_DROP;
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-6.12/batman-adv-frag-ensure-fragment-is-writable-before-m.patch
new file mode 100644 (file)
index 0000000..f472e79
--- /dev/null
@@ -0,0 +1,107 @@
+From 17b0bd7b43267127ce0725a2cd18fb7e51cac111 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:43 +0200
+Subject: batman-adv: frag: ensure fragment is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or use skb_cow() to ensure that the data part is not
+shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to forward the
+fragment.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 15 ++++++++++++++-
+ net/batman-adv/fragmentation.h |  3 ++-
+ net/batman-adv/routing.c       |  3 +--
+ 3 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index fd7cb789ae9a6a..f6714405b0f948 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -384,6 +384,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  * @skb: skb to forward
+  * @recv_if: interface that the skb is received on
+  * @orig_node_src: originator that the skb is received from
++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and
++ *  NET_RX_DROP when it was dropped; only valid when true is returned
+  *
+  * Look up the next-hop of the fragments payload and check if the merged packet
+  * will exceed the MTU towards the next-hop. If so, the fragment is forwarded
+@@ -393,7 +395,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  */
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src)
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
+       struct batadv_neigh_node *neigh_node = NULL;
+@@ -412,12 +415,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (skb_cow(skb, ETH_HLEN) < 0) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
++              packet = (struct batadv_frag_packet *)skb->data;
++
+               batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
+               batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
+                                  skb->len + ETH_HLEN);
+               packet->ttl--;
+               batadv_send_unicast_skb(skb, neigh_node);
++              *rx_result = NET_RX_SUCCESS;
+               ret = true;
+       }
+diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h
+index dbf0871f870303..51e281027ab630 100644
+--- a/net/batman-adv/fragmentation.h
++++ b/net/batman-adv/fragmentation.h
+@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig,
+                           bool (*check_cb)(struct batadv_frag_table_entry *));
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src);
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result);
+ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+                           struct batadv_orig_node *orig_node);
+ int batadv_frag_send_packet(struct sk_buff *skb,
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 85bc2d284a7798..6fd18b69e5f6be 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1175,10 +1175,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb,
+       /* Route the fragment if it is not for us and too big to be merged. */
+       if (!batadv_is_my_mac(bat_priv, frag_packet->dest) &&
+-          batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) {
++          batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) {
+               /* skb was consumed */
+               skb = NULL;
+-              ret = NET_RX_SUCCESS;
+               goto put_orig_node;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-6.12/batman-adv-prevent-elp-transmission-interval-underfl.patch
new file mode 100644 (file)
index 0000000..ecf0c7b
--- /dev/null
@@ -0,0 +1,52 @@
+From ea963a22dabe874b6f0347ab10ad82c46b268c99 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:39 +0200
+Subject: batman-adv: prevent ELP transmission interval underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream.
+
+batadv_v_elp_start_timer() enqeues a delayed work. The time when it starts
+is randomly chosen between (elp_interval - BATADV_JITTER) and
+(elp_interval + BATADV_JITTER). The configured elp_interval must therefore
+be larger or equal to BATADV_JITTER to avoid that it causes an underflow of
+the unsigned integer. If this would happen, then a "fast" ELP interval
+would turn into a "day long" delay.
+
+At the same time, it must not be larger than the maximum value the variable
+can store.
+
+Cc: stable@kernel.org
+Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/netlink.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
+index 9362cd9d6f3d33..a7522015224a5b 100644
+--- a/net/batman-adv/netlink.c
++++ b/net/batman-adv/netlink.c
+@@ -936,9 +936,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb,
+ #ifdef CONFIG_BATMAN_ADV_BATMAN_V
+       if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) {
++              u32 elp_interval;
++
+               attr = info->attrs[BATADV_ATTR_ELP_INTERVAL];
++              elp_interval = nla_get_u32(attr);
++
++              elp_interval = min_t(u32, elp_interval, INT_MAX);
++              elp_interval = max_t(u32, elp_interval, BATADV_JITTER);
+-              atomic_set(&hard_iface->bat_v.elp_interval, nla_get_u32(attr));
++              atomic_set(&hard_iface->bat_v.elp_interval, elp_interval);
+       }
+       if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) {
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-6.12/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
new file mode 100644 (file)
index 0000000..76293d6
--- /dev/null
@@ -0,0 +1,74 @@
+From 9f57d0cb63c0c1c960650d4a1538c34f8a3fc452 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:37 +0200
+Subject: batman-adv: tp_meter: add only finished tp_vars to lists
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream.
+
+When the receiver variables (aka "session") are initialized, then they are
+added to the list of sessions before the timer is set up. A RCU protected
+reader could therefore find the entry and run mod_setup before
+batadv_tp_init_recv() finished the timer initialization.
+
+The same is true for batadv_tp_start(), which must first initialize the
+finish_work and the test_length to avoid a similar problem.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 089ac7cc9fbebc..2bba53fc6da5c0 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1096,21 +1096,21 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->prerandom_offset = 0;
+       spin_lock_init(&tp_vars->prerandom_lock);
+-      kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+-      spin_unlock_bh(&bat_priv->tp_list_lock);
+-
+       tp_vars->test_length = test_length;
+       if (!tp_vars->test_length)
+               tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH;
++      /* init work item for finished tp tests */
++      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
++
++      kref_get(&tp_vars->refcount);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      spin_unlock_bh(&bat_priv->tp_list_lock);
++
+       batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                  "Meter: starting throughput meter towards %pM (length=%ums)\n",
+                  dst, test_length);
+-      /* init work item for finished tp tests */
+-      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
+-
+       /* start tp kthread. This way the write() call issued from userspace can
+        * happily return and avoid to block
+        */
+@@ -1430,10 +1430,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
+       kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+       kref_get(&tp_vars->refcount);
+-      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+       batadv_tp_reset_receiver_timer(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-6.12/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
new file mode 100644 (file)
index 0000000..6ffc386
--- /dev/null
@@ -0,0 +1,66 @@
+From ed1e61b7338d93653bfc166aeabde99ef7d2fe7d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:47 +0200
+Subject: batman-adv: tp_meter: annotate last_recv_time access with
+ READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream.
+
+The last_recv_time field for batadv_tp_receiver tracks the jiffies value of
+the most recent activity and is used to detect timeouts. These accesses are
+not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used
+to prevent data races caused by compiler optimizations.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 638dd438a6c052..b882919a868ca0 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       bat_priv = tp_vars->bat_priv;
+       /* if there is recent activity rearm the timer */
+-      if (!batadv_has_timed_out(tp_vars->last_recv_time,
++      if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time),
+                                 BATADV_TP_RECV_TIMEOUT)) {
+               /* reset the receiver shutdown timer */
+               batadv_tp_reset_receiver_timer(tp_vars);
+@@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+       if (tp_vars) {
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+               goto out_unlock;
+       }
+@@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+-      tp_vars->last_recv_time = jiffies;
++      WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                       goto out;
+               }
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-6.12/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
new file mode 100644 (file)
index 0000000..8d0c09b
--- /dev/null
@@ -0,0 +1,64 @@
+From bb014b18d3d2438fd59d930951ce55b123498369 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:34 +0200
+Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream.
+
+The cwnd is always MSS <= cwnd <= 0x20000000. But the calculation in
+batadv_tp_update_cwnd() assumes unsigned 32 bit arithmetics.
+
+    ((mss * 8) ** 2) / (cwnd * 8)
+
+In case cwnd is actually 0x20000000, it will be shifted by 3 bit to the
+left end up at 0x100000000 or U32_MAX + 1. It will therefore wrap around
+and be 0 - resulting in:
+
+    ((mss * 8) ** 2) / 0
+
+This is of course invalid and cannot be calculated. The calculation should
+must be simplified to avoid this overflow:
+
+   (mss ** 2) * 8 / cwnd
+
+It will keep the precision enhancement from the scaling (by 8) but avoid
+the overflow in the divisor.
+
+In theory, there could still be an overflow in the dividend. It is at the
+moment fixed to BATADV_TP_PLEN in batadv_tp_recv_ack() - so it is not an
+imminent problem. But allowing it to use the whole u32 bit range, would
+mean that it can still use up to 67 bits. To keep this calculation safe for
+32 bit arithmetic, mss must never use more than floor((32 - 3) / 2) bits -
+or in other words: must never be larger than 16383.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 0fdcafca3aa02b..4ff80e4214ff0a 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -154,9 +154,12 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss)
+               return;
+       }
++      /* prevent overflow in (mss * mss) << 3 */
++      mss = min_t(u32, mss, (1U << 14) - 1);
++
+       /* increment CWND at least of 1 (section 3.1 of RFC5681) */
+       tp_vars->dec_cwnd += max_t(u32, 1U << 3,
+-                                 ((mss * mss) << 6) / (tp_vars->cwnd << 3));
++                                 ((mss * mss) << 3) / tp_vars->cwnd);
+       if (tp_vars->dec_cwnd < (mss << 3)) {
+               spin_unlock_bh(&tp_vars->cwnd_lock);
+               return;
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-6.12/batman-adv-tp_meter-avoid-window-underflow.patch
new file mode 100644 (file)
index 0000000..711e71a
--- /dev/null
@@ -0,0 +1,56 @@
+From 995911928b4f6cd2b8f7d8c67f8cba8857aaa9c4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:33 +0200
+Subject: batman-adv: tp_meter: avoid window underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream.
+
+In batadv_tp_avail(), win_left is calculated with 32-bit unsigned
+arithmetic: win_left = win_limit - tp_vars->last_sent;
+
+During Fast Recovery, cwnd is inflated and last_sent advances rapidly. When
+Fast Recovery ends, cwnd drops abruptly back to ss_threshold. If the newly
+shrunk win_limit is less than last_sent, the unsigned subtraction will
+underflow, wrapping to a massive positive value. Instead of returning that
+the window is full (unavailable), it returns that the sender can continue
+sending.
+
+To handle this situation, it must be checked whether the windows end
+sequence number (win_limit) has to be compared with the last sent sequence
+number. If it would be before the last sent sequence number, then more acks
+are needed before the transmission can be started again.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index f6ccb639744a2a..0fdcafca3aa02b 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+ static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars,
+                           size_t payload_len)
+ {
++      u32 last_sent = READ_ONCE(tp_vars->last_sent);
+       u32 win_left, win_limit;
+       win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd;
+-      win_left = win_limit - tp_vars->last_sent;
++
++      if (batadv_seq_before(last_sent, win_limit))
++              win_left = win_limit - last_sent;
++      else
++              win_left = 0;
+       return win_left >= payload_len;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-6.12/batman-adv-tp_meter-fix-fast-recovery-precondition.patch
new file mode 100644 (file)
index 0000000..c019bb4
--- /dev/null
@@ -0,0 +1,48 @@
+From 8bfc320374cf39bb95d3791382e1a563ffe41994 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:35 +0200
+Subject: batman-adv: tp_meter: fix fast recovery precondition
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream.
+
+The fast recovery precondition checks if the recover (initialized to
+BATADV_TP_FIRST_SEQ) is bigger than the received ack. But since recover is
+only updated when this check is successful, it will never enter the fast
+recovery mode.
+
+According to RFC6582 Section 3.2 step 2, the check should actually be
+different:
+
+> When the third duplicate ACK is received, the TCP sender first
+> checks the value of recover to see if the Cumulative
+> Acknowledgment field covers more than recover
+
+The precondition must therefore check if recover is smaller than the
+received ack - basically swapping the operands of the current check.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 4ff80e4214ff0a..c79352cfddc4ae 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (recv_ack >= tp_vars->recover)
++              if (tp_vars->recover >= recv_ack)
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-6.12/batman-adv-tp_meter-handle-overlapping-packets.patch
new file mode 100644 (file)
index 0000000..3c468ce
--- /dev/null
@@ -0,0 +1,109 @@
+From 7fb49577e3a6185990d92896981361aafe9a8093 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:49 +0200
+Subject: batman-adv: tp_meter: handle overlapping packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit cbde75c38b21f022891525078622587ad557b7c1 upstream.
+
+If the size of the packets would change during the transmission, it could
+happen that some retries of packets are overlapping. In this case, precise
+comparisons of sequence numbers by the receiver would be wrong. It is then
+necessary to check if the start sequence number to the end sequence number
+("seqno + length") would contain a new range.
+
+If this is the case then this is enough to accept this packet. In all other
+cases, the packet still has to be dropped (and not acked).
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 25 +++++++++++--------------
+ 1 file changed, 11 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index f745bc09a04963..50b83e0438a07b 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+ /**
+  * batadv_tp_handle_out_of_order() - store an out of order packet
+  * @tp_vars: the private data of the current TP meter session
+- * @skb: the buffer containing the received packet
++ * @seqno: sequence number of new received packet
++ * @payload_len: length of the received packet
+  *
+  * Store the out of order packet in the unacked list for late processing. This
+  * packets are kept in this list so that they can be ACKed at once as soon as
+@@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  * Return: true if the packed has been successfully processed, false otherwise
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+-                                        const struct sk_buff *skb)
++                                        u32 seqno, u32 payload_len)
+       __must_hold(&tp_vars->unacked_lock)
+ {
+-      const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+-      u32 payload_len;
+       bool added = false;
+       new = kmalloc(sizeof(*new), GFP_ATOMIC);
+       if (unlikely(!new))
+               return false;
+-      icmp = (struct batadv_icmp_tp_packet *)skb->data;
+-
+-      new->seqno = ntohl(icmp->seqno);
+-      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      new->seqno = seqno;
+       new->len = payload_len;
+       /* if the list is empty immediately attach this new object */
+@@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+-      size_t packet_size;
++      u32 payload_len;
+       u32 to_ack;
+       u32 seqno;
+@@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+-      if (batadv_seq_before(seqno, tp_vars->last_recv))
++      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      to_ack = seqno + payload_len;
++      if (batadv_seq_before(to_ack, tp_vars->last_recv))
+               goto send_ack;
+       /* if the packet is out of order enqueue it */
+-      if (ntohl(icmp->seqno) != tp_vars->last_recv) {
++      if (batadv_seq_before(tp_vars->last_recv, seqno)) {
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++              if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
+                       spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
+               }
+@@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       }
+       /* if everything was fine count the ACKed bytes */
+-      packet_size = skb->len - sizeof(struct batadv_unicast_packet);
+-      tp_vars->last_recv += packet_size;
++      tp_vars->last_recv = to_ack;
+       /* check if this ordered message filled a gap.... */
+       batadv_tp_ack_unordered(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-6.12/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
new file mode 100644 (file)
index 0000000..209c759
--- /dev/null
@@ -0,0 +1,40 @@
+From d8d12e03670e6d4f0faebea9b5e67a5b0f156489 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:36 +0200
+Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery
+ detection
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream.
+
+The recover variable and the last_sent sequence number are initialized on
+purpose as a really high value which will wrap-around after the first 2000
+bytes. The fast recovery precondition must therefore not use simple integer
+comparisons but use helpers which are aware of the sequence number
+wrap-arounds.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index c79352cfddc4ae..089ac7cc9fbebc 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (tp_vars->recover >= recv_ack)
++              if (!batadv_seq_before(tp_vars->recover, recv_ack))
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-6.12/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
new file mode 100644 (file)
index 0000000..f736342
--- /dev/null
@@ -0,0 +1,40 @@
+From 0c673441894cc4d6eb4e8abefd5f3d8e967fe8f1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:32 +0200
+Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit febfb1b86224489535312296ecfa3d4bf467f339 upstream.
+
+When batadv_tp_update_cwnd() is called, dec_cwnd is increased. But dec_cwnd
+is only initialixed (to 0) when a duplicate Ack was received or when cwnd
+is below the ss_threshold.
+
+Just initialize the cwnd during the initialization to avoid any potential
+access of uninitialized data.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 750e5e9d3dc9d6..f6ccb639744a2a 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1055,6 +1055,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+        * soft_interface, hence its MTU
+        */
+       tp_vars->cwnd = BATADV_TP_PLEN * 3;
++      tp_vars->dec_cwnd = 0;
++
+       /* at the beginning initialise the SS threshold to the biggest possible
+        * window size, hence the AWND size
+        */
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-6.12/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
new file mode 100644 (file)
index 0000000..7badcbd
--- /dev/null
@@ -0,0 +1,42 @@
+From 81f357d34f1f1b6bfa9fc4890a9e6810c16253d6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:31 +0200
+Subject: batman-adv: tp_meter: initialize dup_acks explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b2b68b32a715e0328662801576974aa37b942b00 upstream.
+
+When an ack with a sequence number equal to the last_acked is received, the
+dup_acks counter is increased to decide whether fast retransmit should be
+performed. Only when the sequence numbers are not equal, the dup_acks is
+set to the initial value (0).
+
+But if the initial packet would have the sequence number
+BATADV_TP_FIRST_SEQ, dup_acks would not be initialized and atomic_inc would
+operate on an undefined starting value. It is therefore required to have it
+explicitly initialized during the start of the sender session.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index f350a60e6c76b9..750e5e9d3dc9d6 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1045,6 +1045,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->icmp_uid = icmp_uid;
+       tp_vars->last_sent = BATADV_TP_FIRST_SEQ;
++      atomic_set(&tp_vars->dup_acks, 0);
+       atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ);
+       tp_vars->fast_recovery = false;
+       tp_vars->recover = BATADV_TP_FIRST_SEQ;
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-6.12/batman-adv-tp_meter-initialize-last_recv_time-during.patch
new file mode 100644 (file)
index 0000000..04c54aa
--- /dev/null
@@ -0,0 +1,68 @@
+From 90d4994e77eb12756cf63db7a0e1f36a55b6b4b8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:40 +0200
+Subject: batman-adv: tp_meter: initialize last_recv_time during init
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream.
+
+The last_recv_time is the most important indicator for a receiver session
+to figure out whether a session timed out or not. But this information was
+only initialized after the session was added to the tp_receiver_list and
+after the timer was started.
+
+In the worst case, the timer (function) could have tried to access this
+information before the actual initialization was reached. Like rest of the
+variables of the tp_meter receiver session, this field has to be filled out
+before any other (parallel running) context has the chance to access it.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 2bba53fc6da5c0..133eed2fa9507f 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1403,8 +1403,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+-      if (tp_vars)
++      if (tp_vars) {
++              tp_vars->last_recv_time = jiffies;
+               goto out_unlock;
++      }
+       if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) {
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+@@ -1432,6 +1434,8 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      tp_vars->last_recv_time = jiffies;
++
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1480,9 +1484,9 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                                  icmp->orig);
+                       goto out;
+               }
+-      }
+-      tp_vars->last_recv_time = jiffies;
++              tp_vars->last_recv_time = jiffies;
++      }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-6.12/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
new file mode 100644 (file)
index 0000000..fc0462b
--- /dev/null
@@ -0,0 +1,44 @@
+From edebcce561dfc2f9b4a9612e33aad29c5357c0c0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:30 +0200
+Subject: batman-adv: tp_meter: keep unacked list in ascending ordered
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream.
+
+When batadv_tp_handle_out_of_order inserts a new entry in the list of
+unacked (out of order) packets, it searches from the entry with the newest
+sequence number towards oldest sequence number. If an entry is found which
+is older than the newly entry, the new entry has to be added after the
+found one to keep the ascending order.
+
+But for this operation list_add_tail() was used. But this function adds an
+entry _before_ another one. As result, the list would contain a lot of
+swapped sequence numbers. The consumer of this list
+(batadv_tp_ack_unordered()) would then fail to correctly ack packets.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index dfc3374549921f..f350a60e6c76b9 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1325,7 +1325,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                * one is attached _after_ it. In this way the list is kept in
+                * ascending order
+                */
+-              list_add_tail(&new->list, &un->list);
++              list_add(&new->list, &un->list);
+               added = true;
+               break;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-6.12/batman-adv-tp_meter-prevent-parallel-modifications-o.patch
new file mode 100644 (file)
index 0000000..e3f3af3
--- /dev/null
@@ -0,0 +1,157 @@
+From 3c8832b3656c8a73dce73dfab0e0efca46919922 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:48 +0200
+Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream.
+
+When last_recv is updated to store the last receive sequence number, it is
+assuming that nothing is modifying in parallel while:
+
+* check for outdated packets is done
+* out of order check is performed (and packets are stored in out-of-order
+  queue)
+* the out-of-order queue was searched for closed gaps
+* sequence number for next ack is calculated
+
+Nothing of that was actually protected. It could therefore happen that the
+last_recv was updated multiple times in parallel and the final sequence
+number was calculated with deltas which had no connection to the sequence
+number they were added to.
+
+Lock this whole region with the same lock which was already used to protect
+the unacked (out-of-order) list.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 22 +++++++++++++---------
+ net/batman-adv/types.h    |  2 +-
+ 2 files changed, 14 insertions(+), 10 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index b882919a868ca0..f745bc09a04963 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                                         const struct sk_buff *skb)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+@@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       payload_len = skb->len - sizeof(struct batadv_unicast_packet);
+       new->len = payload_len;
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
+               tp_vars->unacked_count++;
+-              goto out;
++              return true;
+       }
+       /* otherwise loop over the list and either drop the packet because this
+@@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+               tp_vars->unacked_count--;
+       }
+-out:
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+-
+       return true;
+ }
+@@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+  * @tp_vars: the private data of the current TP meter session
+  */
+ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       struct batadv_tp_unacked *un, *safe;
+       u32 to_ack;
+@@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+       /* go through the unacked packet list and possibly ACK them as
+        * well
+        */
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               /* the list is ordered, therefore it is possible to stop as soon
+                * there is a gap between the last acked seqno and the seqno of
+@@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               kfree(un);
+               tp_vars->unacked_count--;
+       }
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+ /**
+@@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+       size_t packet_size;
++      u32 to_ack;
+       u32 seqno;
+       icmp = (struct batadv_icmp_tp_packet *)skb->data;
+@@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
++      spin_lock_bh(&tp_vars->unacked_lock);
++
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+@@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb))
++              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++                      spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
++              }
+               /* send a duplicate ACK */
+               goto send_ack;
+@@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       batadv_tp_ack_unordered(tp_vars);
+ send_ack:
++      to_ack = tp_vars->last_recv;
++      spin_unlock_bh(&tp_vars->unacked_lock);
++
+       /* send the ACK. If the received packet was out of order, the ACK that
+        * is going to be sent is a duplicate (the sender will count them and
+        * possibly enter Fast Retransmit as soon as it has reached 3)
+        */
+-      batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv,
++      batadv_tp_send_ack(bat_priv, icmp->orig, to_ack,
+                          icmp->timestamp, icmp->session, icmp->uid);
+ out:
+       batadv_tp_vars_put(tp_vars);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index e8679a4cd0413b..01c8fef027b419 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1561,7 +1561,7 @@ struct batadv_tp_vars {
+       /** @unacked_list: list of unacked packets (meta-info only) */
+       struct list_head unacked_list;
+-      /** @unacked_lock: protect unacked_list */
++      /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
+       spinlock_t unacked_lock;
+       /** @unacked_count: number of unacked entries */
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-6.12/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
new file mode 100644 (file)
index 0000000..251ae89
--- /dev/null
@@ -0,0 +1,124 @@
+From 6e797b12224f160f8c7c12efdbd3496da814d51a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:46 +0200
+Subject: batman-adv: tp_meter: restrict number of unacked list entries
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream.
+
+When the unacked_list is unbound, an attacker could send messages with
+small lengths and appropriated seqno + gaps to force the receiver to
+allocate more and more unacked_list entries. And the end either causing an
+out-of-memory situation or increase the management overhead for the (large)
+list that significant portions of CPU cycles are wasted in searching
+through the list.
+
+When limiting the list to a specific number, it is important to still
+correctly add a new entry to the list. But if the list became larger than
+the limit, the last entry of the list (with the highest seqno) must be
+dropped to still allow the earlier seqnos to finish and therefore to
+continue the process. Otherwise, the process might get stuck with too high
+seqnos which are not handled by batadv_tp_ack_unordered().
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++-
+ net/batman-adv/types.h    |  3 +++
+ 2 files changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 133eed2fa9507f..638dd438a6c052 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -87,6 +87,11 @@
+ #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \
+                       sizeof(struct batadv_unicast_packet))
++/**
++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack
++ */
++#define BATADV_TP_MAX_UNACKED 100
++
+ static u8 batadv_tp_prerandom[4096] __read_mostly;
+ /**
+@@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
+               goto out;
+       }
+@@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                */
+               list_add(&new->list, &un->list);
+               added = true;
++              tp_vars->unacked_count++;
+               break;
+       }
+       /* received packet with smallest seqno out of order; add it to front */
+-      if (!added)
++      if (!added) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
++      }
++
++      /* remove the last (biggest) unacked seqno when list is too large */
++      if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) {
++              un = list_last_entry(&tp_vars->unacked_list,
++                                   struct batadv_tp_unacked, list);
++              list_del(&un->list);
++              kfree(un);
++              tp_vars->unacked_count--;
++      }
+ out:
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+@@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       spin_lock_init(&tp_vars->unacked_lock);
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
++      tp_vars->unacked_count = 0;
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 3b2a170b1cf18b..e8679a4cd0413b 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1564,6 +1564,9 @@ struct batadv_tp_vars {
+       /** @unacked_lock: protect unacked_list */
+       spinlock_t unacked_lock;
++      /** @unacked_count: number of unacked entries */
++      size_t unacked_count;
++
+       /** @last_recv_time: time (jiffies) a msg was received */
+       unsigned long last_recv_time;
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-6.12/batman-adv-tt-don-t-merge-change-entries-with-differ.patch
new file mode 100644 (file)
index 0000000..51976b0
--- /dev/null
@@ -0,0 +1,46 @@
+From 2519bd1f1bfc3eaabb26731f985da62aa288fbd6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:50 +0200
+Subject: batman-adv: tt: don't merge change entries with different VIDs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream.
+
+batadv_tt_local_event() merges/cancels events for the same client which
+would conflict or be duplicates. The matching of the queued events only
+compares the MAC address - the VLAN ID stored in each event is ignored.
+
+If a MAC would now appear on multiple VID, the two ADD change events (for
+VID 1 and VID 2) would be merged to a single vid event. The remote can
+therefore not calculate the correct TT table and desync. A full translation
+table exchange is required to recover from this state.
+
+A check of VID is therefore necessary to avoid such wrong merges/cancels.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 7041cd69e20070..69a42bc3fa02e6 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -485,6 +485,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
+               if (!batadv_compare_eth(entry->change.addr, common->addr))
+                       continue;
++              if (entry->change.vid != tt_change_node->change.vid)
++                      continue;
++
+               /* DEL+ADD in the same orig interval have no effect and can be
+                * removed to avoid silly behaviour on the receiver side. The
+                * other way around (ADD+DEL) can happen in case of roaming of
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tt-track-roam-count-per-vid.patch b/queue-6.12/batman-adv-tt-track-roam-count-per-vid.patch
new file mode 100644 (file)
index 0000000..1686c68
--- /dev/null
@@ -0,0 +1,89 @@
+From 7035db422b280d1700bcc15770570cfab1299765 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:51 +0200
+Subject: batman-adv: tt: track roam count per VID
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream.
+
+batadv_tt_check_roam_count() is supposed to track roaming of a TT entry.
+But TT entries are for a MAC + VID. The VID was completely missed and thus
+leads to incorrect detection of ROAM counts when a client MAC exists in
+multiple VLANs.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 9 +++++++--
+ net/batman-adv/types.h             | 3 +++
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 69a42bc3fa02e6..2a7caab5c197ea 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -3517,6 +3517,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  * batadv_tt_check_roam_count() - check if a client has roamed too frequently
+  * @bat_priv: the bat priv with all the soft interface information
+  * @client: mac address of the roaming client
++ * @vid: VLAN identifier
+  *
+  * This function checks whether the client already reached the
+  * maximum number of possible roaming phases. In this case the ROAMING_ADV
+@@ -3524,7 +3525,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  *
+  * Return: true if the ROAMING_ADV can be sent, false otherwise
+  */
+-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid)
+ {
+       struct batadv_tt_roam_node *tt_roam_node;
+       bool ret = false;
+@@ -3537,6 +3538,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               if (!batadv_compare_eth(tt_roam_node->addr, client))
+                       continue;
++              if (tt_roam_node->vid != vid)
++                      continue;
++
+               if (batadv_has_timed_out(tt_roam_node->first_time,
+                                        BATADV_ROAMING_MAX_TIME))
+                       continue;
+@@ -3558,6 +3562,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               atomic_set(&tt_roam_node->counter,
+                          BATADV_ROAMING_MAX_COUNT - 1);
+               ether_addr_copy(tt_roam_node->addr, client);
++              tt_roam_node->vid = vid;
+               list_add(&tt_roam_node->list, &bat_priv->tt.roam_list);
+               ret = true;
+@@ -3594,7 +3599,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
+       /* before going on we have to check whether the client has
+        * already roamed to us too many times
+        */
+-      if (!batadv_tt_check_roam_count(bat_priv, client))
++      if (!batadv_tt_check_roam_count(bat_priv, client, vid))
+               goto out;
+       batadv_dbg(BATADV_DBG_TT, bat_priv,
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 01c8fef027b419..9bde0469e748ce 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -2065,6 +2065,9 @@ struct batadv_tt_roam_node {
+       /** @addr: mac address of the client in the roaming phase */
+       u8 addr[ETH_ALEN];
++      /** @vid: VLAN identifier */
++      u16 vid;
++
+       /**
+        * @counter: number of allowed roaming events per client within a single
+        * OGM interval (changes are committed with each OGM)
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-6.12/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
new file mode 100644 (file)
index 0000000..e36c527
--- /dev/null
@@ -0,0 +1,154 @@
+From 4c23e693dd8c390dad2284158f8665baf46e51a1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:54 +0200
+Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream.
+
+TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to
+signal that the OGM handler should be called (with NULL for data) when the
+specific TVLV container was not found in the OGM. This is used by:
+
+* DAT
+* GW
+* Multicast (OGM + Tracker)
+
+The state whether the handler was executed was stored in the struct
+batadv_tvlv_handler. But the TVLV processing is started without any lock.
+Multiple parallel contexts processing TVLVs would therefore overwrite each
+others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared
+batadv_tvlv_handler.
+
+Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine,
+per TVLV buffer, whether a matching container was present by scanning the
+packet's buffer.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tvlv.c  | 63 ++++++++++++++++++++++++++++++++++++++----
+ net/batman-adv/types.h |  7 -----
+ 2 files changed, 57 insertions(+), 13 deletions(-)
+
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index e1cd27b99bd119..cc66e231ccb8a0 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -398,7 +398,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+               tvlv_handler->ogm_handler(bat_priv, orig_node,
+                                         BATADV_NO_FLAGS,
+                                         tvlv_value, tvlv_value_len);
+-              tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
+               break;
+       case BATADV_UNICAST_TVLV:
+               if (!skb)
+@@ -430,6 +429,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+       return NET_RX_SUCCESS;
+ }
++/**
++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
++ * @tvlv_value: tvlv content
++ * @tvlv_value_len: tvlv content length
++ * @type: tvlv container type to look for
++ * @version: tvlv container version to look for
++ *
++ * Return: true if a container of the given type and version is present in the
++ * tvlv buffer, false otherwise.
++ */
++static bool batadv_tvlv_containers_contain(void *tvlv_value,
++                                         u16 tvlv_value_len, u8 type,
++                                         u8 version)
++{
++      struct batadv_tvlv_hdr *tvlv_hdr;
++      u16 tvlv_value_cont_len;
++
++      while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
++              tvlv_hdr = tvlv_value;
++              tvlv_value_cont_len = ntohs(tvlv_hdr->len);
++              tvlv_value = tvlv_hdr + 1;
++              tvlv_value_len -= sizeof(*tvlv_hdr);
++
++              if (tvlv_value_cont_len > tvlv_value_len)
++                      break;
++
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
++              if (tvlv_hdr->type == type && tvlv_hdr->version == version)
++                      return true;
++
++              tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
++              tvlv_value_len -= tvlv_value_cont_len;
++      }
++
++      return false;
++}
++
+ /**
+  * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the
+  *  appropriate handlers
+@@ -449,7 +490,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+                                  struct sk_buff *skb, void *tvlv_value,
+                                  u16 tvlv_value_len)
+ {
++      u16 tvlv_value_start_len = tvlv_value_len;
+       struct batadv_tvlv_handler *tvlv_handler;
++      void *tvlv_value_start = tvlv_value;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       u16 tvlv_value_cont_len;
+       u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
+@@ -493,12 +536,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+               if (!tvlv_handler->ogm_handler)
+                       continue;
+-              if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
+-                  !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
+-                      tvlv_handler->ogm_handler(bat_priv, orig_node,
+-                                                cifnotfound, NULL, 0);
++              if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND))
++                      continue;
+-              tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
++              /* if the corresponding container was present then the handler
++               * was already called from the loop above
++               */
++              if (batadv_tvlv_containers_contain(tvlv_value_start,
++                                                 tvlv_value_start_len,
++                                                 tvlv_handler->type,
++                                                 tvlv_handler->version))
++                      continue;
++
++              tvlv_handler->ogm_handler(bat_priv, orig_node,
++                                        cifnotfound, NULL, 0);
+       }
+       rcu_read_unlock();
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 9bde0469e748ce..0022bee14574df 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -2483,13 +2483,6 @@ enum batadv_tvlv_handler_flags {
+        *  will call this handler even if its type was not found (with no data)
+        */
+       BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
+-
+-      /**
+-       * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the
+-       *  API marks a handler as being called, so it won't be called if the
+-       *  BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
+-       */
+-      BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
+ };
+ #endif /* _NET_BATMAN_ADV_TYPES_H_ */
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-6.12/batman-adv-tvlv-enforce-2-byte-alignment.patch
new file mode 100644 (file)
index 0000000..1ceeed0
--- /dev/null
@@ -0,0 +1,123 @@
+From 9ec598250ee85db73f5438bf7996e4a66a21f2ee Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:53 +0200
+Subject: batman-adv: tvlv: enforce 2-byte alignment
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream.
+
+The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte
+alignment, so a following OGM must start at an even offset. As the header
+length is even, an odd tvlv_len would misalign it and trigger unaligned
+accesses on strict-alignment architectures.
+
+Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in
+the mesh. Therefore, reject such malformed packets.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 11 ++++++++++-
+ net/batman-adv/bat_v_ogm.c  | 11 ++++++++++-
+ net/batman-adv/routing.c    |  6 ++++++
+ net/batman-adv/tvlv.c       |  6 ++++++
+ 4 files changed, 32 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index b37c9fb178ae50..5dd3e1f281bab2 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -310,14 +310,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
+                         const struct batadv_ogm_packet *ogm_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm_packet->tvlv_len);
++
++      /* the fields of an aggregated OGM are accessed assuming (at least)
++       * 2-byte alignment, so a following OGM must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 48a67705eba85c..c5c4d33cb19838 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -853,14 +853,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len,
+                        const struct batadv_ogm2_packet *ogm2_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm2_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm2_packet->tvlv_len);
++
++      /* the fields of an aggregated OGMv2 are accessed assuming (at least)
++       * 2-byte alignment, so a following OGMv2 must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm2_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 6fd18b69e5f6be..f828d8143f7526 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1373,6 +1373,12 @@ int batadv_recv_mcast_packet(struct sk_buff *skb,
+       if (tvlv_buff_len > skb->len - hdr_size)
+               goto free_skb;
++      /* the fields of an multicast payload are accessed assuming (at least)
++       * 2-byte alignment, so a following packet must start at an even offset.
++       */
++      if (tvlv_buff_len & 1)
++              goto free_skb;
++
+       ret = batadv_tvlv_containers_process(bat_priv, BATADV_MCAST, NULL, skb,
+                                            tvlv_buff, tvlv_buff_len);
+       if (ret >= 0) {
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 8d6b017c433cc9..e1cd27b99bd119 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -464,6 +464,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+               if (tvlv_value_cont_len > tvlv_value_len)
+                       break;
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
+               tvlv_handler = batadv_tvlv_handler_get(bat_priv,
+                                                      tvlv_hdr->type,
+                                                      tvlv_hdr->version);
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-6.12/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
new file mode 100644 (file)
index 0000000..b79f82a
--- /dev/null
@@ -0,0 +1,106 @@
+From 372ba31c4696617a1a6084e4468b7ef253996048 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:45 +0200
+Subject: batman-adv: v: prevent OGM aggregation on disabled hardif
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream.
+
+When an interface gets disabled, the worker is correctly disabled by
+batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable().
+In this process, the skb aggr_list is also freed.
+
+But batadv_v_ogm_send_meshif() can still queue new skbs (via
+batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all
+cores can no longer find the RCU protected list of hard interfaces. These
+queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work.
+
+The batadv_v_ogm_iface_disable() function must block
+batadv_v_ogm_queue_on_if() to avoid leak of skbs.
+
+Cc: stable@kernel.org
+Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_v.c     |  1 +
+ net/batman-adv/bat_v_ogm.c | 12 ++++++++++++
+ net/batman-adv/types.h     |  6 ++++++
+ 3 files changed, 19 insertions(+)
+
+diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c
+index d35479c465e2c4..e13b39121f2db8 100644
+--- a/net/batman-adv/bat_v.c
++++ b/net/batman-adv/bat_v.c
+@@ -819,6 +819,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
+       hard_iface->bat_v.aggr_len = 0;
+       skb_queue_head_init(&hard_iface->bat_v.aggr_list);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq,
+                         batadv_v_ogm_aggr_work);
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 8cfc3944dcfd52..48a67705eba85c 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -254,11 +254,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
+       }
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      if (!hard_iface->bat_v.aggr_list_enabled) {
++              kfree_skb(skb);
++              goto unlock;
++      }
++
+       if (!batadv_v_ogm_queue_left(skb, hard_iface))
+               batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+       __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
++
++unlock:
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+@@ -421,6 +428,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
++      spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = true;
++      spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
++
+       batadv_v_ogm_start_queue_timer(hard_iface);
+       batadv_v_ogm_start_timer(bat_priv);
+@@ -436,6 +447,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
+       cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq);
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       batadv_v_ogm_aggr_list_free(hard_iface);
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index f703d266780d74..3b2a170b1cf18b 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v {
+       /** @aggr_list: queue for to be aggregated OGM packets */
+       struct sk_buff_head aggr_list;
++      /**
++       * @aggr_list_enabled: aggr_list is active and new skbs can be
++       * enqueued. Protected by aggr_list.lock after initialization
++       */
++      bool aggr_list_enabled:1;
++
+       /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */
+       unsigned int aggr_len;
+-- 
+2.53.0
+
index 9200140766d67dcb4ce0081e5358907bd013ffa3..be988bef514ca53a4a95bcb88cda0afda037c8c1 100644 (file)
@@ -96,3 +96,28 @@ revert-pci-qcom-advertise-hotplug-slot-capability-wi.patch
 kvm-sev-ignore-mmio-requests-of-length-0.patch
 kvm-sev-reject-mmio-requests-larger-than-8-bytes-wit.patch
 kvm-sev-ignore-port-i-o-requests-of-length-0.patch
+batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
+batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
+batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
+batman-adv-tp_meter-avoid-window-underflow.patch
+batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
+batman-adv-tp_meter-fix-fast-recovery-precondition.patch
+batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
+batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
+batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
+batman-adv-prevent-elp-transmission-interval-underfl.patch
+batman-adv-tp_meter-initialize-last_recv_time-during.patch
+batman-adv-ensure-bcast-is-writable-before-modifying.patch
+batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch
+batman-adv-frag-ensure-fragment-is-writable-before-m.patch
+batman-adv-frag-avoid-underflow-of-ttl.patch
+batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
+batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
+batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
+batman-adv-tp_meter-prevent-parallel-modifications-o.patch
+batman-adv-tp_meter-handle-overlapping-packets.patch
+batman-adv-tt-don-t-merge-change-entries-with-differ.patch
+batman-adv-tt-track-roam-count-per-vid.patch
+batman-adv-dat-prevent-false-sharing-between-vlans.patch
+batman-adv-tvlv-enforce-2-byte-alignment.patch
+batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
diff --git a/queue-6.18/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-6.18/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
new file mode 100644 (file)
index 0000000..e478b6c
--- /dev/null
@@ -0,0 +1,156 @@
+From 202878d4663336141898937177592bfccab146f2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:53 +0200
+Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream.
+
+The lasttime field for claim, backbone_gw, and loopdetect tracks the
+jiffies value of the most recent activity and is used to detect timeouts.
+These accesses are not consistently protected by a lock, so
+READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler
+optimizations.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++-------------
+ 1 file changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index 3072f94275ac60..2c6e2b0d1ded48 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -513,7 +513,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
+               return NULL;
+       entry->vid = vid;
+-      entry->lasttime = jiffies;
++      WRITE_ONCE(entry->lasttime, jiffies);
+       entry->crc = BATADV_BLA_CRC_INIT;
+       entry->bat_priv = bat_priv;
+       spin_lock_init(&entry->crc_lock);
+@@ -581,7 +581,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv,
+       if (unlikely(!backbone_gw))
+               return;
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       batadv_backbone_gw_put(backbone_gw);
+ }
+@@ -715,7 +715,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+               ether_addr_copy(claim->addr, mac);
+               spin_lock_init(&claim->backbone_lock);
+               claim->vid = vid;
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               kref_get(&backbone_gw->refcount);
+               claim->backbone_gw = backbone_gw;
+               kref_init(&claim->refcount);
+@@ -737,7 +737,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+                       return;
+               }
+       } else {
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               if (claim->backbone_gw == backbone_gw)
+                       /* no need to register a new backbone */
+                       goto claim_free_ref;
+@@ -770,7 +770,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+       spin_lock_bh(&backbone_gw->crc_lock);
+       backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+ claim_free_ref:
+       batadv_claim_put(claim);
+@@ -859,7 +859,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
+               return true;
+       /* handle as ANNOUNCE frame */
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       crc = ntohs(*((__force __be16 *)(&an_addr[4])));
+       batadv_dbg(BATADV_DBG_BLA, bat_priv,
+@@ -1254,7 +1254,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
+                                                 head, hash_entry) {
+                               if (now)
+                                       goto purge_now;
+-                              if (!batadv_has_timed_out(backbone_gw->lasttime,
++                              if (!batadv_has_timed_out(READ_ONCE(backbone_gw->lasttime),
+                                                         BATADV_BLA_BACKBONE_TIMEOUT))
+                                       continue;
+@@ -1335,7 +1335,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
+                                               primary_if->net_dev->dev_addr))
+                               goto skip;
+-                      if (!batadv_has_timed_out(claim->lasttime,
++                      if (!batadv_has_timed_out(READ_ONCE(claim->lasttime),
+                                                 BATADV_BLA_CLAIM_TIMEOUT))
+                               goto skip;
+@@ -1495,7 +1495,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+               eth_random_addr(bat_priv->bla.loopdetect_addr);
+               bat_priv->bla.loopdetect_addr[0] = 0xba;
+               bat_priv->bla.loopdetect_addr[1] = 0xbe;
+-              bat_priv->bla.loopdetect_lasttime = jiffies;
++              WRITE_ONCE(bat_priv->bla.loopdetect_lasttime, jiffies);
+               atomic_set(&bat_priv->bla.loopdetect_next,
+                          BATADV_BLA_LOOPDETECT_PERIODS);
+@@ -1516,7 +1516,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                                               primary_if->net_dev->dev_addr))
+                               continue;
+-                      backbone_gw->lasttime = jiffies;
++                      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+                       batadv_bla_send_announce(bat_priv, backbone_gw);
+                       if (send_loopdetect)
+@@ -1934,7 +1934,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       /* If the packet came too late, don't forward it on the mesh
+        * but don't consider that as loop. It might be a coincidence.
+        */
+-      if (batadv_has_timed_out(bat_priv->bla.loopdetect_lasttime,
++      if (batadv_has_timed_out(READ_ONCE(bat_priv->bla.loopdetect_lasttime),
+                                BATADV_BLA_LOOPDETECT_TIMEOUT))
+               return true;
+@@ -2049,7 +2049,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       if (own_claim) {
+               /* ... allow it in any case */
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               goto allow;
+       }
+@@ -2151,7 +2151,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+               /* if yes, the client has roamed and we have
+                * to unclaim it.
+                */
+-              if (batadv_has_timed_out(claim->lasttime, 100)) {
++              if (batadv_has_timed_out(READ_ONCE(claim->lasttime), 100)) {
+                       /* only unclaim if the last claim entry is
+                        * older than 100 ms to make sure we really
+                        * have a roaming client here.
+@@ -2396,7 +2396,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid,
+       backbone_crc = backbone_gw->crc;
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime);
++      msecs = jiffies_to_msecs(jiffies - READ_ONCE(backbone_gw->lasttime));
+       if (is_own)
+               if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) {
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-6.18/batman-adv-dat-prevent-false-sharing-between-vlans.patch
new file mode 100644 (file)
index 0000000..a6606b4
--- /dev/null
@@ -0,0 +1,57 @@
+From bca97555cfa96c3eb4e1f9c40fb57634257a8b88 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:08 +0200
+Subject: batman-adv: dat: prevent false sharing between VLANs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream.
+
+The local hash of DAT entries is supposed to be VLAN (VID) aware. But
+the adding to the hash and the search in the hash were not checking the VID
+information of the hash entries. The entries would therefore only be
+correctly separated when batadv_hash_dat() didn't select the same buckets
+for different VIDs.
+
+Cc: stable@kernel.org
+Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/distributed-arp-table.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
+index 031c295fff1b1d..860db505e8697f 100644
+--- a/net/batman-adv/distributed-arp-table.c
++++ b/net/batman-adv/distributed-arp-table.c
+@@ -214,10 +214,13 @@ static void batadv_dat_purge(struct work_struct *work)
+  */
+ static bool batadv_compare_dat(const struct hlist_node *node, const void *data2)
+ {
+-      const void *data1 = container_of(node, struct batadv_dat_entry,
+-                                       hash_entry);
++      const struct batadv_dat_entry *entry1;
++      const struct batadv_dat_entry *entry2;
+-      return memcmp(data1, data2, sizeof(__be32)) == 0;
++      entry1 = container_of(node, struct batadv_dat_entry, hash_entry);
++      entry2 = data2;
++
++      return entry1->ip == entry2->ip && entry1->vid == entry2->vid;
+ }
+ /**
+@@ -344,6 +347,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
+               if (dat_entry->ip != ip)
+                       continue;
++              if (dat_entry->vid != vid)
++                      continue;
++
+               if (!kref_get_unless_zero(&dat_entry->refcount))
+                       continue;
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-ensure-bcast-is-writable-before-modifying.patch b/queue-6.18/batman-adv-ensure-bcast-is-writable-before-modifying.patch
new file mode 100644 (file)
index 0000000..0f33ac2
--- /dev/null
@@ -0,0 +1,50 @@
+From 7f2f2b859d335d0d7c7a07956096ef652b86a5e4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:57 +0200
+Subject: batman-adv: ensure bcast is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or used skb_cow() to ensure that the data part is not
+shared.
+
+The old implementation used a shared queue and created copies before
+attempting to write to it. But with the new implementation, the broadcast
+packet is already modified when it gets received. Potentially writing to
+shared buffers in this process.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to rebroadcast the
+packet.
+
+Cc: stable@kernel.org
+Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/routing.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 12c16f81cc51df..0672dc30bed3b1 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1191,6 +1191,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
+       if (batadv_is_my_mac(bat_priv, bcast_packet->orig))
+               goto free_skb;
++      /* create a copy of the skb, if needed, to modify it. */
++      if (skb_cow(skb, ETH_HLEN) < 0)
++              goto free_skb;
++
++      bcast_packet = (struct batadv_bcast_packet *)skb->data;
++
+       if (bcast_packet->ttl-- < 2)
+               goto free_skb;
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch b/queue-6.18/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch
new file mode 100644 (file)
index 0000000..cf2de0d
--- /dev/null
@@ -0,0 +1,127 @@
+From e9436a3c43d56b7427bcaa3b323e71fabd4587ca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:58 +0200
+Subject: batman-adv: fix (m|b)cast csum after decrementing TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream.
+
+The broadcast and multicast packets can be received at the same time by the
+local system and forwarded to other nodes. Both are simply decrementing the
+TTL at the beginning of the receive path - independent of chosen paths
+(receive/forward). But such a modification of the data conflicts with the
+hw csum. This is not a problem when the packet is directly forwarded but
+can cause errors in the local receive path.
+
+Such a problem can then trigger a "hw csum failure". The receiver path must
+therefore ensure that the csum is fixed for each modification of the
+payload before batadv_interface_rx() is reached.
+
+Since all batman-adv packet types with a ttl have it as u8 at offset 2, a
+helper can be used for all of them. But it is only used at the moment for
+batadv_bcast_packet and batadv_mcast_packet because they are the only ones
+which deliver the packet locally but unconditionally modify the TTL.
+
+Cc: stable@kernel.org
+Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed")
+Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/routing.c | 58 ++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 56 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 0672dc30bed3b1..cdcea90db61235 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -8,6 +8,7 @@
+ #include "main.h"
+ #include <linux/atomic.h>
++#include <linux/build_bug.h>
+ #include <linux/byteorder/generic.h>
+ #include <linux/compiler.h>
+ #include <linux/errno.h>
+@@ -204,6 +205,59 @@ bool batadv_check_management_packet(struct sk_buff *skb,
+       return true;
+ }
++/**
++ * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe
++ * @skb: the received packet with @skb->data pointing to the batman-adv header
++ *
++ * Supports the following packet types, all of which carry the TTL at offset 2:
++ *
++ * - batadv_ogm_packet
++ * - batadv_ogm2_packet
++ * - batadv_icmp_header
++ * - batadv_icmp_packet
++ * - batadv_icmp_tp_packet
++ * - batadv_icmp_packet_rr
++ * - batadv_unicast_packet
++ * - batadv_frag_packet
++ * - batadv_bcast_packet
++ * - batadv_mcast_packet
++ * - batadv_coded_packet
++ * - batadv_unicast_tvlv_packet
++ *
++ * Return: true if the packet may be forwarded (ttl decremented),
++ *  false if it must be dropped (ttl would expire)
++ */
++static bool batadv_skb_decrement_ttl(struct sk_buff *skb)
++{
++      static const size_t ttl_offset = 2;
++      u8 *ttl_pos;
++
++      BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_mcast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset);
++
++      ttl_pos = skb->data + ttl_offset;
++
++      /* would expire on this hop -> drop, leave header + csum untouched */
++      if (*ttl_pos < 2)
++              return false;
++
++      skb_postpull_rcsum(skb, ttl_pos, 1);
++      (*ttl_pos)--;
++      skb_postpush_rcsum(skb, ttl_pos, 1);
++
++      return true;
++}
++
+ /**
+  * batadv_recv_my_icmp_packet() - receive an icmp packet locally
+  * @bat_priv: the bat priv with all the mesh interface information
+@@ -1197,7 +1251,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
+       bcast_packet = (struct batadv_bcast_packet *)skb->data;
+-      if (bcast_packet->ttl-- < 2)
++      if (!batadv_skb_decrement_ttl(skb))
+               goto free_skb;
+       orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig);
+@@ -1304,7 +1358,7 @@ int batadv_recv_mcast_packet(struct sk_buff *skb,
+               goto free_skb;
+       mcast_packet = (struct batadv_mcast_packet *)skb->data;
+-      if (mcast_packet->ttl-- < 2)
++      if (!batadv_skb_decrement_ttl(skb))
+               goto free_skb;
+       tvlv_buff = (unsigned char *)(skb->data + hdr_size);
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-6.18/batman-adv-frag-avoid-underflow-of-ttl.patch
new file mode 100644 (file)
index 0000000..527e9a3
--- /dev/null
@@ -0,0 +1,45 @@
+From 357b073fd79be41a341e5a0e9195e0339daa8812 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:00 +0200
+Subject: batman-adv: frag: avoid underflow of TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream.
+
+Packets with a TTL are using it to limit the amount of time this packet can
+be forwarded. But for batadv_frag_packet, the TTL was always only reduced
+but it was never evaluated. It could even underflow without any effect.
+
+Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for
+forwarding. This keeps it in sync with the not fragmented unicast packet.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index ffa5d4b0740959..4779741e7273ea 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -415,6 +415,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (packet->ttl < 2) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
+               if (skb_cow(skb, ETH_HLEN) < 0) {
+                       kfree_skb(skb);
+                       *rx_result = NET_RX_DROP;
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-6.18/batman-adv-frag-ensure-fragment-is-writable-before-m.patch
new file mode 100644 (file)
index 0000000..488088f
--- /dev/null
@@ -0,0 +1,106 @@
+From 948a175efc11b0e227f9b94a96d3759a83ab3699 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:59 +0200
+Subject: batman-adv: frag: ensure fragment is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or use skb_cow() to ensure that the data part is not
+shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to forward the
+fragment.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 15 ++++++++++++++-
+ net/batman-adv/fragmentation.h |  3 ++-
+ net/batman-adv/routing.c       |  3 +--
+ 3 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index 31395281692cb5..ffa5d4b0740959 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -384,6 +384,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  * @skb: skb to forward
+  * @recv_if: interface that the skb is received on
+  * @orig_node_src: originator that the skb is received from
++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and
++ *  NET_RX_DROP when it was dropped; only valid when true is returned
+  *
+  * Look up the next-hop of the fragments payload and check if the merged packet
+  * will exceed the MTU towards the next-hop. If so, the fragment is forwarded
+@@ -393,7 +395,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  */
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src)
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(recv_if->mesh_iface);
+       struct batadv_neigh_node *neigh_node = NULL;
+@@ -412,12 +415,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (skb_cow(skb, ETH_HLEN) < 0) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
++              packet = (struct batadv_frag_packet *)skb->data;
++
+               batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
+               batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
+                                  skb->len + ETH_HLEN);
+               packet->ttl--;
+               batadv_send_unicast_skb(skb, neigh_node);
++              *rx_result = NET_RX_SUCCESS;
+               ret = true;
+       }
+diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h
+index dbf0871f870303..51e281027ab630 100644
+--- a/net/batman-adv/fragmentation.h
++++ b/net/batman-adv/fragmentation.h
+@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig,
+                           bool (*check_cb)(struct batadv_frag_table_entry *));
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src);
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result);
+ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+                           struct batadv_orig_node *orig_node);
+ int batadv_frag_send_packet(struct sk_buff *skb,
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index cdcea90db61235..4483f8d9c75831 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1168,10 +1168,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb,
+       /* Route the fragment if it is not for us and too big to be merged. */
+       if (!batadv_is_my_mac(bat_priv, frag_packet->dest) &&
+-          batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) {
++          batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) {
+               /* skb was consumed */
+               skb = NULL;
+-              ret = NET_RX_SUCCESS;
+               goto put_orig_node;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-gw-don-t-deselect-gateway-with-active-har.patch b/queue-6.18/batman-adv-gw-don-t-deselect-gateway-with-active-har.patch
new file mode 100644 (file)
index 0000000..b60852f
--- /dev/null
@@ -0,0 +1,84 @@
+From 292e9ada11423a5949113ba88c6c117ba8949d9f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:56 +0200
+Subject: batman-adv: gw: don't deselect gateway with active hardif
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit df97a7107b16375a10a36d7a63e9b4291a8ac680 upstream.
+
+The batadv_hardif_cnt() was previously checking if there is an
+batadv_hard_iface->mesh_iface which is has the same mesh_iface. And since
+batadv_hardif_disable_interface() was resetting the
+batadv_hard_iface->mesh_iface after this check, it had to verify whether
+*1* interface was still part of the mesh_iface before it started the
+gateway deselection.
+
+But after batadv_hardif_cnt() is now checking the lower interfaces of
+mesh_iface and batadv_hardif_disable_interface() already removed the
+interface via netdev_upper_dev_unlink() earlier in this function, the check
+must now make sure that *0* interfaces can be found by batadv_hardif_cnt()
+before selected gateway must be deselected. Otherwise the deselection would
+already happen one batadv_hard_iface too early.
+
+Because a 0 hardif count from batadv_hardif_cnt() is equal to an empty
+list, it is possible to replace the counting with a simple list_empty().
+
+Cc: stable@kernel.org
+Fixes: 7dc284702bcd ("batman-adv: store hard_iface as iflink private data")
+Reviewed-by: Nora Schiffer <neocturne@universe-factory.net>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/hard-interface.c | 28 ++--------------------------
+ 1 file changed, 2 insertions(+), 26 deletions(-)
+
+diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
+index 1c488049d55463..39b1ed813497d2 100644
+--- a/net/batman-adv/hard-interface.c
++++ b/net/batman-adv/hard-interface.c
+@@ -786,30 +786,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
+       return ret;
+ }
+-/**
+- * batadv_hardif_cnt() - get number of interfaces enslaved to mesh interface
+- * @mesh_iface: mesh interface to check
+- *
+- * This function is only using RCU for locking - the result can therefore be
+- * off when another function is modifying the list at the same time. The
+- * caller can use the rtnl_lock to make sure that the count is accurate.
+- *
+- * Return: number of connected/enslaved hard interfaces
+- */
+-static size_t batadv_hardif_cnt(struct net_device *mesh_iface)
+-{
+-      struct batadv_hard_iface *hard_iface;
+-      struct list_head *iter;
+-      size_t count = 0;
+-
+-      rcu_read_lock();
+-      netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter)
+-              count++;
+-      rcu_read_unlock();
+-
+-      return count;
+-}
+-
+ /**
+  * batadv_hardif_disable_interface() - Remove hard interface from mesh interface
+  * @hard_iface: hard interface to be removed
+@@ -850,8 +826,8 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
+       netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface);
+       batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface);
+-      /* nobody uses this interface anymore */
+-      if (batadv_hardif_cnt(hard_iface->mesh_iface) <= 1)
++      /* nobody uses this mesh interface anymore */
++      if (list_empty(&hard_iface->mesh_iface->adj_list.lower))
+               batadv_gw_check_client_stop(bat_priv);
+       hard_iface->mesh_iface = NULL;
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-6.18/batman-adv-prevent-elp-transmission-interval-underfl.patch
new file mode 100644 (file)
index 0000000..98415e1
--- /dev/null
@@ -0,0 +1,52 @@
+From ee52bb15fff196bf2d65dcfcf7f5f58fe70217ad Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:54 +0200
+Subject: batman-adv: prevent ELP transmission interval underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream.
+
+batadv_v_elp_start_timer() enqeues a delayed work. The time when it starts
+is randomly chosen between (elp_interval - BATADV_JITTER) and
+(elp_interval + BATADV_JITTER). The configured elp_interval must therefore
+be larger or equal to BATADV_JITTER to avoid that it causes an underflow of
+the unsigned integer. If this would happen, then a "fast" ELP interval
+would turn into a "day long" delay.
+
+At the same time, it must not be larger than the maximum value the variable
+can store.
+
+Cc: stable@kernel.org
+Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/netlink.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
+index 78c651f634cd39..1d144d8cc0928e 100644
+--- a/net/batman-adv/netlink.c
++++ b/net/batman-adv/netlink.c
+@@ -917,9 +917,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb,
+ #ifdef CONFIG_BATMAN_ADV_BATMAN_V
+       if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) {
++              u32 elp_interval;
++
+               attr = info->attrs[BATADV_ATTR_ELP_INTERVAL];
++              elp_interval = nla_get_u32(attr);
++
++              elp_interval = min_t(u32, elp_interval, INT_MAX);
++              elp_interval = max_t(u32, elp_interval, BATADV_JITTER);
+-              atomic_set(&hard_iface->bat_v.elp_interval, nla_get_u32(attr));
++              atomic_set(&hard_iface->bat_v.elp_interval, elp_interval);
+       }
+       if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) {
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-6.18/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
new file mode 100644 (file)
index 0000000..56b4079
--- /dev/null
@@ -0,0 +1,74 @@
+From 437963dd64218a45c7608812098b45ef81dd4b32 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:52 +0200
+Subject: batman-adv: tp_meter: add only finished tp_vars to lists
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream.
+
+When the receiver variables (aka "session") are initialized, then they are
+added to the list of sessions before the timer is set up. A RCU protected
+reader could therefore find the entry and run mod_setup before
+batadv_tp_init_recv() finished the timer initialization.
+
+The same is true for batadv_tp_start(), which must first initialize the
+finish_work and the test_length to avoid a similar problem.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 0ebfc4462b8f17..0444aa46b95c29 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1096,21 +1096,21 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->prerandom_offset = 0;
+       spin_lock_init(&tp_vars->prerandom_lock);
+-      kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+-      spin_unlock_bh(&bat_priv->tp_list_lock);
+-
+       tp_vars->test_length = test_length;
+       if (!tp_vars->test_length)
+               tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH;
++      /* init work item for finished tp tests */
++      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
++
++      kref_get(&tp_vars->refcount);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      spin_unlock_bh(&bat_priv->tp_list_lock);
++
+       batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                  "Meter: starting throughput meter towards %pM (length=%ums)\n",
+                  dst, test_length);
+-      /* init work item for finished tp tests */
+-      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
+-
+       /* start tp kthread. This way the write() call issued from userspace can
+        * happily return and avoid to block
+        */
+@@ -1430,10 +1430,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
+       kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+       kref_get(&tp_vars->refcount);
+-      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+       batadv_tp_reset_receiver_timer(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-6.18/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
new file mode 100644 (file)
index 0000000..df23e90
--- /dev/null
@@ -0,0 +1,66 @@
+From 4c40ba3c684901303ac8081252f572173bc24043 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:03 +0200
+Subject: batman-adv: tp_meter: annotate last_recv_time access with
+ READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream.
+
+The last_recv_time field for batadv_tp_receiver tracks the jiffies value of
+the most recent activity and is used to detect timeouts. These accesses are
+not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used
+to prevent data races caused by compiler optimizations.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index e5387e8f33244a..e69bf10e66ac33 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       bat_priv = tp_vars->bat_priv;
+       /* if there is recent activity rearm the timer */
+-      if (!batadv_has_timed_out(tp_vars->last_recv_time,
++      if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time),
+                                 BATADV_TP_RECV_TIMEOUT)) {
+               /* reset the receiver shutdown timer */
+               batadv_tp_reset_receiver_timer(tp_vars);
+@@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+       if (tp_vars) {
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+               goto out_unlock;
+       }
+@@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+-      tp_vars->last_recv_time = jiffies;
++      WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                       goto out;
+               }
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-6.18/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
new file mode 100644 (file)
index 0000000..223ab15
--- /dev/null
@@ -0,0 +1,64 @@
+From 9a7182a7e003167b4f09f93a62e903a1c9ed45e3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:49 +0200
+Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream.
+
+The cwnd is always MSS <= cwnd <= 0x20000000. But the calculation in
+batadv_tp_update_cwnd() assumes unsigned 32 bit arithmetics.
+
+    ((mss * 8) ** 2) / (cwnd * 8)
+
+In case cwnd is actually 0x20000000, it will be shifted by 3 bit to the
+left end up at 0x100000000 or U32_MAX + 1. It will therefore wrap around
+and be 0 - resulting in:
+
+    ((mss * 8) ** 2) / 0
+
+This is of course invalid and cannot be calculated. The calculation should
+must be simplified to avoid this overflow:
+
+   (mss ** 2) * 8 / cwnd
+
+It will keep the precision enhancement from the scaling (by 8) but avoid
+the overflow in the divisor.
+
+In theory, there could still be an overflow in the dividend. It is at the
+moment fixed to BATADV_TP_PLEN in batadv_tp_recv_ack() - so it is not an
+imminent problem. But allowing it to use the whole u32 bit range, would
+mean that it can still use up to 67 bits. To keep this calculation safe for
+32 bit arithmetic, mss must never use more than floor((32 - 3) / 2) bits -
+or in other words: must never be larger than 16383.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 71a4352cd78c77..00d8bb01611f99 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -154,9 +154,12 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss)
+               return;
+       }
++      /* prevent overflow in (mss * mss) << 3 */
++      mss = min_t(u32, mss, (1U << 14) - 1);
++
+       /* increment CWND at least of 1 (section 3.1 of RFC5681) */
+       tp_vars->dec_cwnd += max_t(u32, 1U << 3,
+-                                 ((mss * mss) << 6) / (tp_vars->cwnd << 3));
++                                 ((mss * mss) << 3) / tp_vars->cwnd);
+       if (tp_vars->dec_cwnd < (mss << 3)) {
+               spin_unlock_bh(&tp_vars->cwnd_lock);
+               return;
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-6.18/batman-adv-tp_meter-avoid-window-underflow.patch
new file mode 100644 (file)
index 0000000..542b824
--- /dev/null
@@ -0,0 +1,56 @@
+From c141c9d1393829739398c5eeca89c56f4bb621eb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:48 +0200
+Subject: batman-adv: tp_meter: avoid window underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream.
+
+In batadv_tp_avail(), win_left is calculated with 32-bit unsigned
+arithmetic: win_left = win_limit - tp_vars->last_sent;
+
+During Fast Recovery, cwnd is inflated and last_sent advances rapidly. When
+Fast Recovery ends, cwnd drops abruptly back to ss_threshold. If the newly
+shrunk win_limit is less than last_sent, the unsigned subtraction will
+underflow, wrapping to a massive positive value. Instead of returning that
+the window is full (unavailable), it returns that the sender can continue
+sending.
+
+To handle this situation, it must be checked whether the windows end
+sequence number (win_limit) has to be compared with the last sent sequence
+number. If it would be before the last sent sequence number, then more acks
+are needed before the transmission can be started again.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 473641d32dc683..71a4352cd78c77 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+ static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars,
+                           size_t payload_len)
+ {
++      u32 last_sent = READ_ONCE(tp_vars->last_sent);
+       u32 win_left, win_limit;
+       win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd;
+-      win_left = win_limit - tp_vars->last_sent;
++
++      if (batadv_seq_before(last_sent, win_limit))
++              win_left = win_limit - last_sent;
++      else
++              win_left = 0;
+       return win_left >= payload_len;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-6.18/batman-adv-tp_meter-fix-fast-recovery-precondition.patch
new file mode 100644 (file)
index 0000000..4776860
--- /dev/null
@@ -0,0 +1,48 @@
+From c2da02b138d748873fd5d24a5f764545b33a60d7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:50 +0200
+Subject: batman-adv: tp_meter: fix fast recovery precondition
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream.
+
+The fast recovery precondition checks if the recover (initialized to
+BATADV_TP_FIRST_SEQ) is bigger than the received ack. But since recover is
+only updated when this check is successful, it will never enter the fast
+recovery mode.
+
+According to RFC6582 Section 3.2 step 2, the check should actually be
+different:
+
+> When the third duplicate ACK is received, the TCP sender first
+> checks the value of recover to see if the Cumulative
+> Acknowledgment field covers more than recover
+
+The precondition must therefore check if recover is smaller than the
+received ack - basically swapping the operands of the current check.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 00d8bb01611f99..a85622267ba655 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (recv_ack >= tp_vars->recover)
++              if (tp_vars->recover >= recv_ack)
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-6.18/batman-adv-tp_meter-handle-overlapping-packets.patch
new file mode 100644 (file)
index 0000000..7292c98
--- /dev/null
@@ -0,0 +1,109 @@
+From b909cd0adc450ea331944be0afd87f94abf29fa1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:05 +0200
+Subject: batman-adv: tp_meter: handle overlapping packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit cbde75c38b21f022891525078622587ad557b7c1 upstream.
+
+If the size of the packets would change during the transmission, it could
+happen that some retries of packets are overlapping. In this case, precise
+comparisons of sequence numbers by the receiver would be wrong. It is then
+necessary to check if the start sequence number to the end sequence number
+("seqno + length") would contain a new range.
+
+If this is the case then this is enough to accept this packet. In all other
+cases, the packet still has to be dropped (and not acked).
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 25 +++++++++++--------------
+ 1 file changed, 11 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 629831ea9a58e5..02af19aaaff291 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+ /**
+  * batadv_tp_handle_out_of_order() - store an out of order packet
+  * @tp_vars: the private data of the current TP meter session
+- * @skb: the buffer containing the received packet
++ * @seqno: sequence number of new received packet
++ * @payload_len: length of the received packet
+  *
+  * Store the out of order packet in the unacked list for late processing. This
+  * packets are kept in this list so that they can be ACKed at once as soon as
+@@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  * Return: true if the packed has been successfully processed, false otherwise
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+-                                        const struct sk_buff *skb)
++                                        u32 seqno, u32 payload_len)
+       __must_hold(&tp_vars->unacked_lock)
+ {
+-      const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+-      u32 payload_len;
+       bool added = false;
+       new = kmalloc(sizeof(*new), GFP_ATOMIC);
+       if (unlikely(!new))
+               return false;
+-      icmp = (struct batadv_icmp_tp_packet *)skb->data;
+-
+-      new->seqno = ntohl(icmp->seqno);
+-      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      new->seqno = seqno;
+       new->len = payload_len;
+       /* if the list is empty immediately attach this new object */
+@@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+-      size_t packet_size;
++      u32 payload_len;
+       u32 to_ack;
+       u32 seqno;
+@@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+-      if (batadv_seq_before(seqno, tp_vars->last_recv))
++      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      to_ack = seqno + payload_len;
++      if (batadv_seq_before(to_ack, tp_vars->last_recv))
+               goto send_ack;
+       /* if the packet is out of order enqueue it */
+-      if (ntohl(icmp->seqno) != tp_vars->last_recv) {
++      if (batadv_seq_before(tp_vars->last_recv, seqno)) {
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++              if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
+                       spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
+               }
+@@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       }
+       /* if everything was fine count the ACKed bytes */
+-      packet_size = skb->len - sizeof(struct batadv_unicast_packet);
+-      tp_vars->last_recv += packet_size;
++      tp_vars->last_recv = to_ack;
+       /* check if this ordered message filled a gap.... */
+       batadv_tp_ack_unordered(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-6.18/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
new file mode 100644 (file)
index 0000000..fc28acb
--- /dev/null
@@ -0,0 +1,40 @@
+From 1bb42504094a679da4bcfa7936ae212e44c159cd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:51 +0200
+Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery
+ detection
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream.
+
+The recover variable and the last_sent sequence number are initialized on
+purpose as a really high value which will wrap-around after the first 2000
+bytes. The fast recovery precondition must therefore not use simple integer
+comparisons but use helpers which are aware of the sequence number
+wrap-arounds.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index a85622267ba655..0ebfc4462b8f17 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (tp_vars->recover >= recv_ack)
++              if (!batadv_seq_before(tp_vars->recover, recv_ack))
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-6.18/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
new file mode 100644 (file)
index 0000000..674bdea
--- /dev/null
@@ -0,0 +1,40 @@
+From bc6dfc14d9d5b3521dbdf89915c970897a7015ba Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:47 +0200
+Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit febfb1b86224489535312296ecfa3d4bf467f339 upstream.
+
+When batadv_tp_update_cwnd() is called, dec_cwnd is increased. But dec_cwnd
+is only initialixed (to 0) when a duplicate Ack was received or when cwnd
+is below the ss_threshold.
+
+Just initialize the cwnd during the initialization to avoid any potential
+access of uninitialized data.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index fe9a447643074a..473641d32dc683 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1055,6 +1055,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+        * mesh_interface, hence its MTU
+        */
+       tp_vars->cwnd = BATADV_TP_PLEN * 3;
++      tp_vars->dec_cwnd = 0;
++
+       /* at the beginning initialise the SS threshold to the biggest possible
+        * window size, hence the AWND size
+        */
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-6.18/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
new file mode 100644 (file)
index 0000000..05ab68a
--- /dev/null
@@ -0,0 +1,42 @@
+From b6fe66028fae037882ea6e13809d1299ce87a4ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:46 +0200
+Subject: batman-adv: tp_meter: initialize dup_acks explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b2b68b32a715e0328662801576974aa37b942b00 upstream.
+
+When an ack with a sequence number equal to the last_acked is received, the
+dup_acks counter is increased to decide whether fast retransmit should be
+performed. Only when the sequence numbers are not equal, the dup_acks is
+set to the initial value (0).
+
+But if the initial packet would have the sequence number
+BATADV_TP_FIRST_SEQ, dup_acks would not be initialized and atomic_inc would
+operate on an undefined starting value. It is therefore required to have it
+explicitly initialized during the start of the sender session.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index f222c5093b647e..fe9a447643074a 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1045,6 +1045,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->icmp_uid = icmp_uid;
+       tp_vars->last_sent = BATADV_TP_FIRST_SEQ;
++      atomic_set(&tp_vars->dup_acks, 0);
+       atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ);
+       tp_vars->fast_recovery = false;
+       tp_vars->recover = BATADV_TP_FIRST_SEQ;
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-6.18/batman-adv-tp_meter-initialize-last_recv_time-during.patch
new file mode 100644 (file)
index 0000000..962d21c
--- /dev/null
@@ -0,0 +1,68 @@
+From 057e1f7d30c1fa98382c0be169cd55bca7481a0c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:55 +0200
+Subject: batman-adv: tp_meter: initialize last_recv_time during init
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream.
+
+The last_recv_time is the most important indicator for a receiver session
+to figure out whether a session timed out or not. But this information was
+only initialized after the session was added to the tp_receiver_list and
+after the timer was started.
+
+In the worst case, the timer (function) could have tried to access this
+information before the actual initialization was reached. Like rest of the
+variables of the tp_meter receiver session, this field has to be filled out
+before any other (parallel running) context has the chance to access it.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 0444aa46b95c29..10b8daca3a6154 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1403,8 +1403,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+-      if (tp_vars)
++      if (tp_vars) {
++              tp_vars->last_recv_time = jiffies;
+               goto out_unlock;
++      }
+       if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) {
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+@@ -1432,6 +1434,8 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      tp_vars->last_recv_time = jiffies;
++
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1480,9 +1484,9 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                                  icmp->orig);
+                       goto out;
+               }
+-      }
+-      tp_vars->last_recv_time = jiffies;
++              tp_vars->last_recv_time = jiffies;
++      }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-6.18/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
new file mode 100644 (file)
index 0000000..0a6a2ce
--- /dev/null
@@ -0,0 +1,44 @@
+From e8c4b955ee14124851fc8b809868476971e75eef Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:45 +0200
+Subject: batman-adv: tp_meter: keep unacked list in ascending ordered
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream.
+
+When batadv_tp_handle_out_of_order inserts a new entry in the list of
+unacked (out of order) packets, it searches from the entry with the newest
+sequence number towards oldest sequence number. If an entry is found which
+is older than the newly entry, the new entry has to be added after the
+found one to keep the ascending order.
+
+But for this operation list_add_tail() was used. But this function adds an
+entry _before_ another one. As result, the list would contain a lot of
+swapped sequence numbers. The consumer of this list
+(batadv_tp_ack_unordered()) would then fail to correctly ack packets.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index b1629e0ac82683..f222c5093b647e 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1325,7 +1325,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                * one is attached _after_ it. In this way the list is kept in
+                * ascending order
+                */
+-              list_add_tail(&new->list, &un->list);
++              list_add(&new->list, &un->list);
+               added = true;
+               break;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-6.18/batman-adv-tp_meter-prevent-parallel-modifications-o.patch
new file mode 100644 (file)
index 0000000..3069bc1
--- /dev/null
@@ -0,0 +1,157 @@
+From beae79a5118a251f694c211d72e334a05ac7f8df Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:04 +0200
+Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream.
+
+When last_recv is updated to store the last receive sequence number, it is
+assuming that nothing is modifying in parallel while:
+
+* check for outdated packets is done
+* out of order check is performed (and packets are stored in out-of-order
+  queue)
+* the out-of-order queue was searched for closed gaps
+* sequence number for next ack is calculated
+
+Nothing of that was actually protected. It could therefore happen that the
+last_recv was updated multiple times in parallel and the final sequence
+number was calculated with deltas which had no connection to the sequence
+number they were added to.
+
+Lock this whole region with the same lock which was already used to protect
+the unacked (out-of-order) list.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 22 +++++++++++++---------
+ net/batman-adv/types.h    |  2 +-
+ 2 files changed, 14 insertions(+), 10 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index e69bf10e66ac33..629831ea9a58e5 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                                         const struct sk_buff *skb)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+@@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       payload_len = skb->len - sizeof(struct batadv_unicast_packet);
+       new->len = payload_len;
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
+               tp_vars->unacked_count++;
+-              goto out;
++              return true;
+       }
+       /* otherwise loop over the list and either drop the packet because this
+@@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+               tp_vars->unacked_count--;
+       }
+-out:
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+-
+       return true;
+ }
+@@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+  * @tp_vars: the private data of the current TP meter session
+  */
+ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       struct batadv_tp_unacked *un, *safe;
+       u32 to_ack;
+@@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+       /* go through the unacked packet list and possibly ACK them as
+        * well
+        */
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               /* the list is ordered, therefore it is possible to stop as soon
+                * there is a gap between the last acked seqno and the seqno of
+@@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               kfree(un);
+               tp_vars->unacked_count--;
+       }
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+ /**
+@@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+       size_t packet_size;
++      u32 to_ack;
+       u32 seqno;
+       icmp = (struct batadv_icmp_tp_packet *)skb->data;
+@@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
++      spin_lock_bh(&tp_vars->unacked_lock);
++
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+@@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb))
++              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++                      spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
++              }
+               /* send a duplicate ACK */
+               goto send_ack;
+@@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       batadv_tp_ack_unordered(tp_vars);
+ send_ack:
++      to_ack = tp_vars->last_recv;
++      spin_unlock_bh(&tp_vars->unacked_lock);
++
+       /* send the ACK. If the received packet was out of order, the ACK that
+        * is going to be sent is a duplicate (the sender will count them and
+        * possibly enter Fast Retransmit as soon as it has reached 3)
+        */
+-      batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv,
++      batadv_tp_send_ack(bat_priv, icmp->orig, to_ack,
+                          icmp->timestamp, icmp->session, icmp->uid);
+ out:
+       batadv_tp_vars_put(tp_vars);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 8b180d8245b2a6..84de2570eac3ed 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1423,7 +1423,7 @@ struct batadv_tp_vars {
+       /** @unacked_list: list of unacked packets (meta-info only) */
+       struct list_head unacked_list;
+-      /** @unacked_lock: protect unacked_list */
++      /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
+       spinlock_t unacked_lock;
+       /** @unacked_count: number of unacked entries */
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-6.18/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
new file mode 100644 (file)
index 0000000..fc704bc
--- /dev/null
@@ -0,0 +1,124 @@
+From 072ff1e2dee7e78ab8b308f299947774c2b38653 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:02 +0200
+Subject: batman-adv: tp_meter: restrict number of unacked list entries
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream.
+
+When the unacked_list is unbound, an attacker could send messages with
+small lengths and appropriated seqno + gaps to force the receiver to
+allocate more and more unacked_list entries. And the end either causing an
+out-of-memory situation or increase the management overhead for the (large)
+list that significant portions of CPU cycles are wasted in searching
+through the list.
+
+When limiting the list to a specific number, it is important to still
+correctly add a new entry to the list. But if the list became larger than
+the limit, the last entry of the list (with the highest seqno) must be
+dropped to still allow the earlier seqnos to finish and therefore to
+continue the process. Otherwise, the process might get stuck with too high
+seqnos which are not handled by batadv_tp_ack_unordered().
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++-
+ net/batman-adv/types.h    |  3 +++
+ 2 files changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 10b8daca3a6154..e5387e8f33244a 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -87,6 +87,11 @@
+ #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \
+                       sizeof(struct batadv_unicast_packet))
++/**
++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack
++ */
++#define BATADV_TP_MAX_UNACKED 100
++
+ static u8 batadv_tp_prerandom[4096] __read_mostly;
+ /**
+@@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
+               goto out;
+       }
+@@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                */
+               list_add(&new->list, &un->list);
+               added = true;
++              tp_vars->unacked_count++;
+               break;
+       }
+       /* received packet with smallest seqno out of order; add it to front */
+-      if (!added)
++      if (!added) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
++      }
++
++      /* remove the last (biggest) unacked seqno when list is too large */
++      if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) {
++              un = list_last_entry(&tp_vars->unacked_list,
++                                   struct batadv_tp_unacked, list);
++              list_del(&un->list);
++              kfree(un);
++              tp_vars->unacked_count--;
++      }
+ out:
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+@@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       spin_lock_init(&tp_vars->unacked_lock);
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
++      tp_vars->unacked_count = 0;
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 417d653021c7af..8b180d8245b2a6 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1426,6 +1426,9 @@ struct batadv_tp_vars {
+       /** @unacked_lock: protect unacked_list */
+       spinlock_t unacked_lock;
++      /** @unacked_count: number of unacked entries */
++      size_t unacked_count;
++
+       /** @last_recv_time: time (jiffies) a msg was received */
+       unsigned long last_recv_time;
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-6.18/batman-adv-tt-don-t-merge-change-entries-with-differ.patch
new file mode 100644 (file)
index 0000000..f8e7812
--- /dev/null
@@ -0,0 +1,45 @@
+From fde94418383277688ef60900ba712901678eefbe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:06 +0200
+Subject: batman-adv: tt: don't merge change entries with different VIDs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream.
+
+batadv_tt_local_event() merges/cancels events for the same client which
+would conflict or be duplicates. The matching of the queued events only
+compares the MAC address - the VLAN ID stored in each event is ignored.
+
+If a MAC would now appear on multiple VID, the two ADD change events (for
+VID 1 and VID 2) would be merged to a single vid event. The remote can
+therefore not calculate the correct TT table and desync. A full translation
+table exchange is required to recover from this state.
+
+A check of VID is therefore necessary to avoid such wrong merges/cancels.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 9f6e67771ffa80..acd8af4446671f 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -446,6 +446,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
+               if (!batadv_compare_eth(entry->change.addr, common->addr))
+                       continue;
++              if (entry->change.vid != tt_change_node->change.vid)
++                      continue;
++
+               del_op_entry = entry->change.flags & BATADV_TT_CLIENT_DEL;
+               if (del_op_requested != del_op_entry) {
+                       /* DEL+ADD in the same orig interval have no effect and
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tt-track-roam-count-per-vid.patch b/queue-6.18/batman-adv-tt-track-roam-count-per-vid.patch
new file mode 100644 (file)
index 0000000..2e1b020
--- /dev/null
@@ -0,0 +1,88 @@
+From 6f20158d28ae2ff9127aed59c0d5211b4455b63a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:07 +0200
+Subject: batman-adv: tt: track roam count per VID
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream.
+
+batadv_tt_check_roam_count() is supposed to track roaming of a TT entry.
+But TT entries are for a MAC + VID. The VID was completely missed and thus
+leads to incorrect detection of ROAM counts when a client MAC exists in
+multiple VLANs.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 9 +++++++--
+ net/batman-adv/types.h             | 3 +++
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index acd8af4446671f..83dfd804a143ab 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -3442,6 +3442,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  * batadv_tt_check_roam_count() - check if a client has roamed too frequently
+  * @bat_priv: the bat priv with all the mesh interface information
+  * @client: mac address of the roaming client
++ * @vid: VLAN identifier
+  *
+  * This function checks whether the client already reached the
+  * maximum number of possible roaming phases. In this case the ROAMING_ADV
+@@ -3449,7 +3450,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  *
+  * Return: true if the ROAMING_ADV can be sent, false otherwise
+  */
+-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid)
+ {
+       struct batadv_tt_roam_node *tt_roam_node;
+       bool ret = false;
+@@ -3462,6 +3463,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               if (!batadv_compare_eth(tt_roam_node->addr, client))
+                       continue;
++              if (tt_roam_node->vid != vid)
++                      continue;
++
+               if (batadv_has_timed_out(tt_roam_node->first_time,
+                                        BATADV_ROAMING_MAX_TIME))
+                       continue;
+@@ -3483,6 +3487,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               atomic_set(&tt_roam_node->counter,
+                          BATADV_ROAMING_MAX_COUNT - 1);
+               ether_addr_copy(tt_roam_node->addr, client);
++              tt_roam_node->vid = vid;
+               list_add(&tt_roam_node->list, &bat_priv->tt.roam_list);
+               ret = true;
+@@ -3519,7 +3524,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
+       /* before going on we have to check whether the client has
+        * already roamed to us too many times
+        */
+-      if (!batadv_tt_check_roam_count(bat_priv, client))
++      if (!batadv_tt_check_roam_count(bat_priv, client, vid))
+               goto out;
+       batadv_dbg(BATADV_DBG_TT, bat_priv,
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 84de2570eac3ed..ef712ba4fff2cb 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1912,6 +1912,9 @@ struct batadv_tt_roam_node {
+       /** @addr: mac address of the client in the roaming phase */
+       u8 addr[ETH_ALEN];
++      /** @vid: VLAN identifier */
++      u16 vid;
++
+       /**
+        * @counter: number of allowed roaming events per client within a single
+        * OGM interval (changes are committed with each OGM)
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-6.18/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
new file mode 100644 (file)
index 0000000..165a30e
--- /dev/null
@@ -0,0 +1,154 @@
+From 79d578f38da153522da846e36f128a4a786c7524 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:10 +0200
+Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream.
+
+TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to
+signal that the OGM handler should be called (with NULL for data) when the
+specific TVLV container was not found in the OGM. This is used by:
+
+* DAT
+* GW
+* Multicast (OGM + Tracker)
+
+The state whether the handler was executed was stored in the struct
+batadv_tvlv_handler. But the TVLV processing is started without any lock.
+Multiple parallel contexts processing TVLVs would therefore overwrite each
+others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared
+batadv_tvlv_handler.
+
+Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine,
+per TVLV buffer, whether a matching container was present by scanning the
+packet's buffer.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tvlv.c  | 63 ++++++++++++++++++++++++++++++++++++++----
+ net/batman-adv/types.h |  7 -----
+ 2 files changed, 57 insertions(+), 13 deletions(-)
+
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 63fb54024d1543..a91f1891747c0a 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -398,7 +398,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+               tvlv_handler->ogm_handler(bat_priv, orig_node,
+                                         BATADV_NO_FLAGS,
+                                         tvlv_value, tvlv_value_len);
+-              tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
+               break;
+       case BATADV_UNICAST_TVLV:
+               if (!skb)
+@@ -430,6 +429,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+       return NET_RX_SUCCESS;
+ }
++/**
++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
++ * @tvlv_value: tvlv content
++ * @tvlv_value_len: tvlv content length
++ * @type: tvlv container type to look for
++ * @version: tvlv container version to look for
++ *
++ * Return: true if a container of the given type and version is present in the
++ * tvlv buffer, false otherwise.
++ */
++static bool batadv_tvlv_containers_contain(void *tvlv_value,
++                                         u16 tvlv_value_len, u8 type,
++                                         u8 version)
++{
++      struct batadv_tvlv_hdr *tvlv_hdr;
++      u16 tvlv_value_cont_len;
++
++      while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
++              tvlv_hdr = tvlv_value;
++              tvlv_value_cont_len = ntohs(tvlv_hdr->len);
++              tvlv_value = tvlv_hdr + 1;
++              tvlv_value_len -= sizeof(*tvlv_hdr);
++
++              if (tvlv_value_cont_len > tvlv_value_len)
++                      break;
++
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
++              if (tvlv_hdr->type == type && tvlv_hdr->version == version)
++                      return true;
++
++              tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
++              tvlv_value_len -= tvlv_value_cont_len;
++      }
++
++      return false;
++}
++
+ /**
+  * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the
+  *  appropriate handlers
+@@ -449,7 +490,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+                                  struct sk_buff *skb, void *tvlv_value,
+                                  u16 tvlv_value_len)
+ {
++      u16 tvlv_value_start_len = tvlv_value_len;
+       struct batadv_tvlv_handler *tvlv_handler;
++      void *tvlv_value_start = tvlv_value;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       u16 tvlv_value_cont_len;
+       u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
+@@ -493,12 +536,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+               if (!tvlv_handler->ogm_handler)
+                       continue;
+-              if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
+-                  !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
+-                      tvlv_handler->ogm_handler(bat_priv, orig_node,
+-                                                cifnotfound, NULL, 0);
++              if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND))
++                      continue;
+-              tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
++              /* if the corresponding container was present then the handler
++               * was already called from the loop above
++               */
++              if (batadv_tvlv_containers_contain(tvlv_value_start,
++                                                 tvlv_value_start_len,
++                                                 tvlv_handler->type,
++                                                 tvlv_handler->version))
++                      continue;
++
++              tvlv_handler->ogm_handler(bat_priv, orig_node,
++                                        cifnotfound, NULL, 0);
+       }
+       rcu_read_unlock();
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index ef712ba4fff2cb..ac4494f1b8e2a8 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -2245,13 +2245,6 @@ enum batadv_tvlv_handler_flags {
+        *  will call this handler even if its type was not found (with no data)
+        */
+       BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
+-
+-      /**
+-       * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the
+-       *  API marks a handler as being called, so it won't be called if the
+-       *  BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
+-       */
+-      BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
+ };
+ #endif /* _NET_BATMAN_ADV_TYPES_H_ */
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-6.18/batman-adv-tvlv-enforce-2-byte-alignment.patch
new file mode 100644 (file)
index 0000000..2cb49ef
--- /dev/null
@@ -0,0 +1,123 @@
+From f2d6ca20d75da2c0d50989e4163b2522aac39a3e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:09 +0200
+Subject: batman-adv: tvlv: enforce 2-byte alignment
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream.
+
+The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte
+alignment, so a following OGM must start at an even offset. As the header
+length is even, an odd tvlv_len would misalign it and trigger unaligned
+accesses on strict-alignment architectures.
+
+Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in
+the mesh. Therefore, reject such malformed packets.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 11 ++++++++++-
+ net/batman-adv/bat_v_ogm.c  | 11 ++++++++++-
+ net/batman-adv/routing.c    |  6 ++++++
+ net/batman-adv/tvlv.c       |  6 ++++++
+ 4 files changed, 32 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index b8b1b997960a96..6e79f69c2fedec 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -311,14 +311,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
+                         const struct batadv_ogm_packet *ogm_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm_packet->tvlv_len);
++
++      /* the fields of an aggregated OGM are accessed assuming (at least)
++       * 2-byte alignment, so a following OGM must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 6852bf5da8c558..1f9b2d2b4831ce 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -849,14 +849,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len,
+                        const struct batadv_ogm2_packet *ogm2_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm2_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm2_packet->tvlv_len);
++
++      /* the fields of an aggregated OGMv2 are accessed assuming (at least)
++       * 2-byte alignment, so a following OGMv2 must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm2_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 4483f8d9c75831..41951c7a1c50b2 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1366,6 +1366,12 @@ int batadv_recv_mcast_packet(struct sk_buff *skb,
+       if (tvlv_buff_len > skb->len - hdr_size)
+               goto free_skb;
++      /* the fields of an multicast payload are accessed assuming (at least)
++       * 2-byte alignment, so a following packet must start at an even offset.
++       */
++      if (tvlv_buff_len & 1)
++              goto free_skb;
++
+       ret = batadv_tvlv_containers_process(bat_priv, BATADV_MCAST, NULL, skb,
+                                            tvlv_buff, tvlv_buff_len);
+       if (ret >= 0) {
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index cde798c82dcf13..63fb54024d1543 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -464,6 +464,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+               if (tvlv_value_cont_len > tvlv_value_len)
+                       break;
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
+               tvlv_handler = batadv_tvlv_handler_get(bat_priv,
+                                                      tvlv_hdr->type,
+                                                      tvlv_hdr->version);
+-- 
+2.53.0
+
diff --git a/queue-6.18/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-6.18/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
new file mode 100644 (file)
index 0000000..bd3e401
--- /dev/null
@@ -0,0 +1,106 @@
+From edc061f6f6c8712aa9256d46d7c0555f58b96e89 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:01 +0200
+Subject: batman-adv: v: prevent OGM aggregation on disabled hardif
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream.
+
+When an interface gets disabled, the worker is correctly disabled by
+batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable().
+In this process, the skb aggr_list is also freed.
+
+But batadv_v_ogm_send_meshif() can still queue new skbs (via
+batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all
+cores can no longer find the RCU protected list of hard interfaces. These
+queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work.
+
+The batadv_v_ogm_iface_disable() function must block
+batadv_v_ogm_queue_on_if() to avoid leak of skbs.
+
+Cc: stable@kernel.org
+Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_v.c     |  1 +
+ net/batman-adv/bat_v_ogm.c | 12 ++++++++++++
+ net/batman-adv/types.h     |  6 ++++++
+ 3 files changed, 19 insertions(+)
+
+diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c
+index de94447142642e..17d2a1ccdce67a 100644
+--- a/net/batman-adv/bat_v.c
++++ b/net/batman-adv/bat_v.c
+@@ -817,6 +817,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
+       hard_iface->bat_v.aggr_len = 0;
+       skb_queue_head_init(&hard_iface->bat_v.aggr_list);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq,
+                         batadv_v_ogm_aggr_work);
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index d66ca77b1aaa3c..6852bf5da8c558 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -252,11 +252,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
+       }
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      if (!hard_iface->bat_v.aggr_list_enabled) {
++              kfree_skb(skb);
++              goto unlock;
++      }
++
+       if (!batadv_v_ogm_queue_left(skb, hard_iface))
+               batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+       __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
++
++unlock:
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+@@ -417,6 +424,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
++      spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = true;
++      spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
++
+       batadv_v_ogm_start_queue_timer(hard_iface);
+       batadv_v_ogm_start_timer(bat_priv);
+@@ -432,6 +443,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
+       cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq);
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       batadv_v_ogm_aggr_list_free(hard_iface);
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index c9bd49d23547ef..417d653021c7af 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v {
+       /** @aggr_list: queue for to be aggregated OGM packets */
+       struct sk_buff_head aggr_list;
++      /**
++       * @aggr_list_enabled: aggr_list is active and new skbs can be
++       * enqueued. Protected by aggr_list.lock after initialization
++       */
++      bool aggr_list_enabled:1;
++
+       /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */
+       unsigned int aggr_len;
+-- 
+2.53.0
+
index ad2d2ee538f4328bd8c890377e9b6cb60fb8bea5..996c90fee378fdda82cfbd9df1ecc2440e3e9ada 100644 (file)
@@ -3,3 +3,29 @@ lsm-add-backing_file-lsm-hooks.patch
 selinux-fix-overlayfs-mmap-and-mprotect-access-check.patch
 revert-pci-qcom-advertise-hotplug-slot-capability-wi.patch
 lockd-fix-test-handling-when-not-all-permissions-are.patch
+batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
+batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
+batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
+batman-adv-tp_meter-avoid-window-underflow.patch
+batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
+batman-adv-tp_meter-fix-fast-recovery-precondition.patch
+batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
+batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
+batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
+batman-adv-prevent-elp-transmission-interval-underfl.patch
+batman-adv-tp_meter-initialize-last_recv_time-during.patch
+batman-adv-gw-don-t-deselect-gateway-with-active-har.patch
+batman-adv-ensure-bcast-is-writable-before-modifying.patch
+batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch
+batman-adv-frag-ensure-fragment-is-writable-before-m.patch
+batman-adv-frag-avoid-underflow-of-ttl.patch
+batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
+batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
+batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
+batman-adv-tp_meter-prevent-parallel-modifications-o.patch
+batman-adv-tp_meter-handle-overlapping-packets.patch
+batman-adv-tt-don-t-merge-change-entries-with-differ.patch
+batman-adv-tt-track-roam-count-per-vid.patch
+batman-adv-dat-prevent-false-sharing-between-vlans.patch
+batman-adv-tvlv-enforce-2-byte-alignment.patch
+batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
diff --git a/queue-6.6/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-6.6/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
new file mode 100644 (file)
index 0000000..14e9853
--- /dev/null
@@ -0,0 +1,156 @@
+From cb5246a7ab87db30dafc9e71218e1f3315d6f916 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:23 +0200
+Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream.
+
+The lasttime field for claim, backbone_gw, and loopdetect tracks the
+jiffies value of the most recent activity and is used to detect timeouts.
+These accesses are not consistently protected by a lock, so
+READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler
+optimizations.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++-------------
+ 1 file changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index cfb1eb25c6ac4d..ac12c06f11b89e 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -512,7 +512,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
+               return NULL;
+       entry->vid = vid;
+-      entry->lasttime = jiffies;
++      WRITE_ONCE(entry->lasttime, jiffies);
+       entry->crc = BATADV_BLA_CRC_INIT;
+       entry->bat_priv = bat_priv;
+       spin_lock_init(&entry->crc_lock);
+@@ -580,7 +580,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv,
+       if (unlikely(!backbone_gw))
+               return;
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       batadv_backbone_gw_put(backbone_gw);
+ }
+@@ -714,7 +714,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+               ether_addr_copy(claim->addr, mac);
+               spin_lock_init(&claim->backbone_lock);
+               claim->vid = vid;
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               kref_get(&backbone_gw->refcount);
+               claim->backbone_gw = backbone_gw;
+               kref_init(&claim->refcount);
+@@ -736,7 +736,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+                       return;
+               }
+       } else {
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               if (claim->backbone_gw == backbone_gw)
+                       /* no need to register a new backbone */
+                       goto claim_free_ref;
+@@ -769,7 +769,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+       spin_lock_bh(&backbone_gw->crc_lock);
+       backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+ claim_free_ref:
+       batadv_claim_put(claim);
+@@ -858,7 +858,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
+               return true;
+       /* handle as ANNOUNCE frame */
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       crc = ntohs(*((__force __be16 *)(&an_addr[4])));
+       batadv_dbg(BATADV_DBG_BLA, bat_priv,
+@@ -1253,7 +1253,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
+                                                 head, hash_entry) {
+                               if (now)
+                                       goto purge_now;
+-                              if (!batadv_has_timed_out(backbone_gw->lasttime,
++                              if (!batadv_has_timed_out(READ_ONCE(backbone_gw->lasttime),
+                                                         BATADV_BLA_BACKBONE_TIMEOUT))
+                                       continue;
+@@ -1334,7 +1334,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
+                                               primary_if->net_dev->dev_addr))
+                               goto skip;
+-                      if (!batadv_has_timed_out(claim->lasttime,
++                      if (!batadv_has_timed_out(READ_ONCE(claim->lasttime),
+                                                 BATADV_BLA_CLAIM_TIMEOUT))
+                               goto skip;
+@@ -1494,7 +1494,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+               eth_random_addr(bat_priv->bla.loopdetect_addr);
+               bat_priv->bla.loopdetect_addr[0] = 0xba;
+               bat_priv->bla.loopdetect_addr[1] = 0xbe;
+-              bat_priv->bla.loopdetect_lasttime = jiffies;
++              WRITE_ONCE(bat_priv->bla.loopdetect_lasttime, jiffies);
+               atomic_set(&bat_priv->bla.loopdetect_next,
+                          BATADV_BLA_LOOPDETECT_PERIODS);
+@@ -1515,7 +1515,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                                               primary_if->net_dev->dev_addr))
+                               continue;
+-                      backbone_gw->lasttime = jiffies;
++                      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+                       batadv_bla_send_announce(bat_priv, backbone_gw);
+                       if (send_loopdetect)
+@@ -1900,7 +1900,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       /* If the packet came too late, don't forward it on the mesh
+        * but don't consider that as loop. It might be a coincidence.
+        */
+-      if (batadv_has_timed_out(bat_priv->bla.loopdetect_lasttime,
++      if (batadv_has_timed_out(READ_ONCE(bat_priv->bla.loopdetect_lasttime),
+                                BATADV_BLA_LOOPDETECT_TIMEOUT))
+               return true;
+@@ -2016,7 +2016,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       if (own_claim) {
+               /* ... allow it in any case */
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               goto allow;
+       }
+@@ -2118,7 +2118,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+               /* if yes, the client has roamed and we have
+                * to unclaim it.
+                */
+-              if (batadv_has_timed_out(claim->lasttime, 100)) {
++              if (batadv_has_timed_out(READ_ONCE(claim->lasttime), 100)) {
+                       /* only unclaim if the last claim entry is
+                        * older than 100 ms to make sure we really
+                        * have a roaming client here.
+@@ -2372,7 +2372,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid,
+       backbone_crc = backbone_gw->crc;
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime);
++      msecs = jiffies_to_msecs(jiffies - READ_ONCE(backbone_gw->lasttime));
+       if (is_own)
+               if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) {
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-6.6/batman-adv-dat-prevent-false-sharing-between-vlans.patch
new file mode 100644 (file)
index 0000000..a450481
--- /dev/null
@@ -0,0 +1,57 @@
+From 576b43c844616c3e0a9ec5ae51ea4237bd071bb9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:37 +0200
+Subject: batman-adv: dat: prevent false sharing between VLANs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream.
+
+The local hash of DAT entries is supposed to be VLAN (VID) aware. But
+the adding to the hash and the search in the hash were not checking the VID
+information of the hash entries. The entries would therefore only be
+correctly separated when batadv_hash_dat() didn't select the same buckets
+for different VIDs.
+
+Cc: stable@kernel.org
+Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/distributed-arp-table.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
+index c76bddbf816ada..92d90e352d1f41 100644
+--- a/net/batman-adv/distributed-arp-table.c
++++ b/net/batman-adv/distributed-arp-table.c
+@@ -215,10 +215,13 @@ static void batadv_dat_purge(struct work_struct *work)
+  */
+ static bool batadv_compare_dat(const struct hlist_node *node, const void *data2)
+ {
+-      const void *data1 = container_of(node, struct batadv_dat_entry,
+-                                       hash_entry);
++      const struct batadv_dat_entry *entry1;
++      const struct batadv_dat_entry *entry2;
+-      return memcmp(data1, data2, sizeof(__be32)) == 0;
++      entry1 = container_of(node, struct batadv_dat_entry, hash_entry);
++      entry2 = data2;
++
++      return entry1->ip == entry2->ip && entry1->vid == entry2->vid;
+ }
+ /**
+@@ -345,6 +348,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
+               if (dat_entry->ip != ip)
+                       continue;
++              if (dat_entry->vid != vid)
++                      continue;
++
+               if (!kref_get_unless_zero(&dat_entry->refcount))
+                       continue;
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-ensure-bcast-is-writable-before-modifying.patch b/queue-6.6/batman-adv-ensure-bcast-is-writable-before-modifying.patch
new file mode 100644 (file)
index 0000000..9b02b64
--- /dev/null
@@ -0,0 +1,50 @@
+From 22d7710b0c5f12f47914a4a5df86a87adb7ed249 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:26 +0200
+Subject: batman-adv: ensure bcast is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or used skb_cow() to ensure that the data part is not
+shared.
+
+The old implementation used a shared queue and created copies before
+attempting to write to it. But with the new implementation, the broadcast
+packet is already modified when it gets received. Potentially writing to
+shared buffers in this process.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to rebroadcast the
+packet.
+
+Cc: stable@kernel.org
+Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/routing.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 163cd43c4821b6..747c3ff1c253b0 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1198,6 +1198,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
+       if (batadv_is_my_mac(bat_priv, bcast_packet->orig))
+               goto free_skb;
++      /* create a copy of the skb, if needed, to modify it. */
++      if (skb_cow(skb, ETH_HLEN) < 0)
++              goto free_skb;
++
++      bcast_packet = (struct batadv_bcast_packet *)skb->data;
++
+       if (bcast_packet->ttl-- < 2)
+               goto free_skb;
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch b/queue-6.6/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch
new file mode 100644 (file)
index 0000000..8039690
--- /dev/null
@@ -0,0 +1,118 @@
+From 90abf691e4f1bb5b3ac05a61baaf6e3f35b945c2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:27 +0200
+Subject: batman-adv: fix (m|b)cast csum after decrementing TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream.
+
+The broadcast and multicast packets can be received at the same time by the
+local system and forwarded to other nodes. Both are simply decrementing the
+TTL at the beginning of the receive path - independent of chosen paths
+(receive/forward). But such a modification of the data conflicts with the
+hw csum. This is not a problem when the packet is directly forwarded but
+can cause errors in the local receive path.
+
+Such a problem can then trigger a "hw csum failure". The receiver path must
+therefore ensure that the csum is fixed for each modification of the
+payload before batadv_interface_rx() is reached.
+
+Since all batman-adv packet types with a ttl have it as u8 at offset 2, a
+helper can be used for all of them. But it is only used at the moment for
+batadv_bcast_packet and batadv_mcast_packet because they are the only ones
+which deliver the packet locally but unconditionally modify the TTL.
+
+Cc: stable@kernel.org
+Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed")
+Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding")
+[ Context, Drop change for non-existing mcast handling ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/routing.c | 55 +++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 54 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 747c3ff1c253b0..36e9df0cca0b2f 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -8,6 +8,7 @@
+ #include "main.h"
+ #include <linux/atomic.h>
++#include <linux/build_bug.h>
+ #include <linux/byteorder/generic.h>
+ #include <linux/compiler.h>
+ #include <linux/errno.h>
+@@ -205,6 +206,58 @@ bool batadv_check_management_packet(struct sk_buff *skb,
+       return true;
+ }
++/**
++ * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe
++ * @skb: the received packet with @skb->data pointing to the batman-adv header
++ *
++ * Supports the following packet types, all of which carry the TTL at offset 2:
++ *
++ * - batadv_ogm_packet
++ * - batadv_ogm2_packet
++ * - batadv_icmp_header
++ * - batadv_icmp_packet
++ * - batadv_icmp_tp_packet
++ * - batadv_icmp_packet_rr
++ * - batadv_unicast_packet
++ * - batadv_frag_packet
++ * - batadv_bcast_packet
++ * - batadv_mcast_packet
++ * - batadv_coded_packet
++ * - batadv_unicast_tvlv_packet
++ *
++ * Return: true if the packet may be forwarded (ttl decremented),
++ *  false if it must be dropped (ttl would expire)
++ */
++static bool batadv_skb_decrement_ttl(struct sk_buff *skb)
++{
++      static const size_t ttl_offset = 2;
++      u8 *ttl_pos;
++
++      BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset);
++
++      ttl_pos = skb->data + ttl_offset;
++
++      /* would expire on this hop -> drop, leave header + csum untouched */
++      if (*ttl_pos < 2)
++              return false;
++
++      skb_postpull_rcsum(skb, ttl_pos, 1);
++      (*ttl_pos)--;
++      skb_postpush_rcsum(skb, ttl_pos, 1);
++
++      return true;
++}
++
+ /**
+  * batadv_recv_my_icmp_packet() - receive an icmp packet locally
+  * @bat_priv: the bat priv with all the soft interface information
+@@ -1204,7 +1257,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
+       bcast_packet = (struct batadv_bcast_packet *)skb->data;
+-      if (bcast_packet->ttl-- < 2)
++      if (!batadv_skb_decrement_ttl(skb))
+               goto free_skb;
+       orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig);
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-6.6/batman-adv-frag-avoid-underflow-of-ttl.patch
new file mode 100644 (file)
index 0000000..a5648a9
--- /dev/null
@@ -0,0 +1,45 @@
+From 99ddece5a90d685eedd734c45dfeaa6c8d1d4b99 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:29 +0200
+Subject: batman-adv: frag: avoid underflow of TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream.
+
+Packets with a TTL are using it to limit the amount of time this packet can
+be forwarded. But for batadv_frag_packet, the TTL was always only reduced
+but it was never evaluated. It could even underflow without any effect.
+
+Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for
+forwarding. This keeps it in sync with the not fragmented unicast packet.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index fbf030c57ac04d..0138b0953e10b5 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -420,6 +420,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (packet->ttl < 2) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
+               if (skb_cow(skb, ETH_HLEN) < 0) {
+                       kfree_skb(skb);
+                       *rx_result = NET_RX_DROP;
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-6.6/batman-adv-frag-ensure-fragment-is-writable-before-m.patch
new file mode 100644 (file)
index 0000000..24d2fe3
--- /dev/null
@@ -0,0 +1,107 @@
+From d88f6965e30135818c4dc92c84718761628e9a1a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:28 +0200
+Subject: batman-adv: frag: ensure fragment is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or use skb_cow() to ensure that the data part is not
+shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to forward the
+fragment.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 15 ++++++++++++++-
+ net/batman-adv/fragmentation.h |  3 ++-
+ net/batman-adv/routing.c       |  3 +--
+ 3 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index 4c193194034155..fbf030c57ac04d 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -385,6 +385,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  * @skb: skb to forward
+  * @recv_if: interface that the skb is received on
+  * @orig_node_src: originator that the skb is received from
++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and
++ *  NET_RX_DROP when it was dropped; only valid when true is returned
+  *
+  * Look up the next-hop of the fragments payload and check if the merged packet
+  * will exceed the MTU towards the next-hop. If so, the fragment is forwarded
+@@ -394,7 +396,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  */
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src)
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
+       struct batadv_orig_node *orig_node_dst;
+@@ -417,12 +420,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (skb_cow(skb, ETH_HLEN) < 0) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
++              packet = (struct batadv_frag_packet *)skb->data;
++
+               batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
+               batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
+                                  skb->len + ETH_HLEN);
+               packet->ttl--;
+               batadv_send_unicast_skb(skb, neigh_node);
++              *rx_result = NET_RX_SUCCESS;
+               ret = true;
+       }
+diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h
+index dbf0871f870303..51e281027ab630 100644
+--- a/net/batman-adv/fragmentation.h
++++ b/net/batman-adv/fragmentation.h
+@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig,
+                           bool (*check_cb)(struct batadv_frag_table_entry *));
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src);
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result);
+ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+                           struct batadv_orig_node *orig_node);
+ int batadv_frag_send_packet(struct sk_buff *skb,
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 36e9df0cca0b2f..ec278f73805e95 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1174,10 +1174,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb,
+       /* Route the fragment if it is not for us and too big to be merged. */
+       if (!batadv_is_my_mac(bat_priv, frag_packet->dest) &&
+-          batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) {
++          batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) {
+               /* skb was consumed */
+               skb = NULL;
+-              ret = NET_RX_SUCCESS;
+               goto put_orig_node;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-6.6/batman-adv-prevent-elp-transmission-interval-underfl.patch
new file mode 100644 (file)
index 0000000..7deeba6
--- /dev/null
@@ -0,0 +1,52 @@
+From 3b0f53c4fa6464b2ddd81bf83f61e7d974ab161b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:24 +0200
+Subject: batman-adv: prevent ELP transmission interval underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream.
+
+batadv_v_elp_start_timer() enqeues a delayed work. The time when it starts
+is randomly chosen between (elp_interval - BATADV_JITTER) and
+(elp_interval + BATADV_JITTER). The configured elp_interval must therefore
+be larger or equal to BATADV_JITTER to avoid that it causes an underflow of
+the unsigned integer. If this would happen, then a "fast" ELP interval
+would turn into a "day long" delay.
+
+At the same time, it must not be larger than the maximum value the variable
+can store.
+
+Cc: stable@kernel.org
+Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/netlink.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
+index 0c64d81a776174..03ba6282af91a3 100644
+--- a/net/batman-adv/netlink.c
++++ b/net/batman-adv/netlink.c
+@@ -938,9 +938,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb,
+ #ifdef CONFIG_BATMAN_ADV_BATMAN_V
+       if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) {
++              u32 elp_interval;
++
+               attr = info->attrs[BATADV_ATTR_ELP_INTERVAL];
++              elp_interval = nla_get_u32(attr);
++
++              elp_interval = min_t(u32, elp_interval, INT_MAX);
++              elp_interval = max_t(u32, elp_interval, BATADV_JITTER);
+-              atomic_set(&hard_iface->bat_v.elp_interval, nla_get_u32(attr));
++              atomic_set(&hard_iface->bat_v.elp_interval, elp_interval);
+       }
+       if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) {
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-6.6/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
new file mode 100644 (file)
index 0000000..3ae21bc
--- /dev/null
@@ -0,0 +1,74 @@
+From ce99a933cd31ed38056bbb10c07429de3097d142 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:22 +0200
+Subject: batman-adv: tp_meter: add only finished tp_vars to lists
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream.
+
+When the receiver variables (aka "session") are initialized, then they are
+added to the list of sessions before the timer is set up. A RCU protected
+reader could therefore find the entry and run mod_setup before
+batadv_tp_init_recv() finished the timer initialization.
+
+The same is true for batadv_tp_start(), which must first initialize the
+finish_work and the test_length to avoid a similar problem.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 089ac7cc9fbebc..2bba53fc6da5c0 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1096,21 +1096,21 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->prerandom_offset = 0;
+       spin_lock_init(&tp_vars->prerandom_lock);
+-      kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+-      spin_unlock_bh(&bat_priv->tp_list_lock);
+-
+       tp_vars->test_length = test_length;
+       if (!tp_vars->test_length)
+               tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH;
++      /* init work item for finished tp tests */
++      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
++
++      kref_get(&tp_vars->refcount);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      spin_unlock_bh(&bat_priv->tp_list_lock);
++
+       batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                  "Meter: starting throughput meter towards %pM (length=%ums)\n",
+                  dst, test_length);
+-      /* init work item for finished tp tests */
+-      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
+-
+       /* start tp kthread. This way the write() call issued from userspace can
+        * happily return and avoid to block
+        */
+@@ -1430,10 +1430,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
+       kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+       kref_get(&tp_vars->refcount);
+-      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+       batadv_tp_reset_receiver_timer(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-6.6/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
new file mode 100644 (file)
index 0000000..e8a4160
--- /dev/null
@@ -0,0 +1,66 @@
+From 9df041c16713653867e3f5fd61f2c8343fbbbec7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:32 +0200
+Subject: batman-adv: tp_meter: annotate last_recv_time access with
+ READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream.
+
+The last_recv_time field for batadv_tp_receiver tracks the jiffies value of
+the most recent activity and is used to detect timeouts. These accesses are
+not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used
+to prevent data races caused by compiler optimizations.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 638dd438a6c052..b882919a868ca0 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       bat_priv = tp_vars->bat_priv;
+       /* if there is recent activity rearm the timer */
+-      if (!batadv_has_timed_out(tp_vars->last_recv_time,
++      if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time),
+                                 BATADV_TP_RECV_TIMEOUT)) {
+               /* reset the receiver shutdown timer */
+               batadv_tp_reset_receiver_timer(tp_vars);
+@@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+       if (tp_vars) {
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+               goto out_unlock;
+       }
+@@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+-      tp_vars->last_recv_time = jiffies;
++      WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                       goto out;
+               }
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-6.6/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
new file mode 100644 (file)
index 0000000..3f521ad
--- /dev/null
@@ -0,0 +1,64 @@
+From dc86aea78d0950a0672e883560e39a401039ba94 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:19 +0200
+Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream.
+
+The cwnd is always MSS <= cwnd <= 0x20000000. But the calculation in
+batadv_tp_update_cwnd() assumes unsigned 32 bit arithmetics.
+
+    ((mss * 8) ** 2) / (cwnd * 8)
+
+In case cwnd is actually 0x20000000, it will be shifted by 3 bit to the
+left end up at 0x100000000 or U32_MAX + 1. It will therefore wrap around
+and be 0 - resulting in:
+
+    ((mss * 8) ** 2) / 0
+
+This is of course invalid and cannot be calculated. The calculation should
+must be simplified to avoid this overflow:
+
+   (mss ** 2) * 8 / cwnd
+
+It will keep the precision enhancement from the scaling (by 8) but avoid
+the overflow in the divisor.
+
+In theory, there could still be an overflow in the dividend. It is at the
+moment fixed to BATADV_TP_PLEN in batadv_tp_recv_ack() - so it is not an
+imminent problem. But allowing it to use the whole u32 bit range, would
+mean that it can still use up to 67 bits. To keep this calculation safe for
+32 bit arithmetic, mss must never use more than floor((32 - 3) / 2) bits -
+or in other words: must never be larger than 16383.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 0fdcafca3aa02b..4ff80e4214ff0a 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -154,9 +154,12 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss)
+               return;
+       }
++      /* prevent overflow in (mss * mss) << 3 */
++      mss = min_t(u32, mss, (1U << 14) - 1);
++
+       /* increment CWND at least of 1 (section 3.1 of RFC5681) */
+       tp_vars->dec_cwnd += max_t(u32, 1U << 3,
+-                                 ((mss * mss) << 6) / (tp_vars->cwnd << 3));
++                                 ((mss * mss) << 3) / tp_vars->cwnd);
+       if (tp_vars->dec_cwnd < (mss << 3)) {
+               spin_unlock_bh(&tp_vars->cwnd_lock);
+               return;
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-6.6/batman-adv-tp_meter-avoid-window-underflow.patch
new file mode 100644 (file)
index 0000000..4a4007f
--- /dev/null
@@ -0,0 +1,56 @@
+From 2da25fca733480ae48254e672697397abd827735 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:18 +0200
+Subject: batman-adv: tp_meter: avoid window underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream.
+
+In batadv_tp_avail(), win_left is calculated with 32-bit unsigned
+arithmetic: win_left = win_limit - tp_vars->last_sent;
+
+During Fast Recovery, cwnd is inflated and last_sent advances rapidly. When
+Fast Recovery ends, cwnd drops abruptly back to ss_threshold. If the newly
+shrunk win_limit is less than last_sent, the unsigned subtraction will
+underflow, wrapping to a massive positive value. Instead of returning that
+the window is full (unavailable), it returns that the sender can continue
+sending.
+
+To handle this situation, it must be checked whether the windows end
+sequence number (win_limit) has to be compared with the last sent sequence
+number. If it would be before the last sent sequence number, then more acks
+are needed before the transmission can be started again.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index f6ccb639744a2a..0fdcafca3aa02b 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+ static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars,
+                           size_t payload_len)
+ {
++      u32 last_sent = READ_ONCE(tp_vars->last_sent);
+       u32 win_left, win_limit;
+       win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd;
+-      win_left = win_limit - tp_vars->last_sent;
++
++      if (batadv_seq_before(last_sent, win_limit))
++              win_left = win_limit - last_sent;
++      else
++              win_left = 0;
+       return win_left >= payload_len;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-6.6/batman-adv-tp_meter-fix-fast-recovery-precondition.patch
new file mode 100644 (file)
index 0000000..03a41cd
--- /dev/null
@@ -0,0 +1,48 @@
+From 6509380ba93875ee13c52d53add68c9d4ee8d87d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:20 +0200
+Subject: batman-adv: tp_meter: fix fast recovery precondition
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream.
+
+The fast recovery precondition checks if the recover (initialized to
+BATADV_TP_FIRST_SEQ) is bigger than the received ack. But since recover is
+only updated when this check is successful, it will never enter the fast
+recovery mode.
+
+According to RFC6582 Section 3.2 step 2, the check should actually be
+different:
+
+> When the third duplicate ACK is received, the TCP sender first
+> checks the value of recover to see if the Cumulative
+> Acknowledgment field covers more than recover
+
+The precondition must therefore check if recover is smaller than the
+received ack - basically swapping the operands of the current check.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 4ff80e4214ff0a..c79352cfddc4ae 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (recv_ack >= tp_vars->recover)
++              if (tp_vars->recover >= recv_ack)
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-6.6/batman-adv-tp_meter-handle-overlapping-packets.patch
new file mode 100644 (file)
index 0000000..faf993c
--- /dev/null
@@ -0,0 +1,109 @@
+From 8d52b3bc303833fc0c728d4bc76a15608b93babd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:34 +0200
+Subject: batman-adv: tp_meter: handle overlapping packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit cbde75c38b21f022891525078622587ad557b7c1 upstream.
+
+If the size of the packets would change during the transmission, it could
+happen that some retries of packets are overlapping. In this case, precise
+comparisons of sequence numbers by the receiver would be wrong. It is then
+necessary to check if the start sequence number to the end sequence number
+("seqno + length") would contain a new range.
+
+If this is the case then this is enough to accept this packet. In all other
+cases, the packet still has to be dropped (and not acked).
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 25 +++++++++++--------------
+ 1 file changed, 11 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index f745bc09a04963..50b83e0438a07b 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+ /**
+  * batadv_tp_handle_out_of_order() - store an out of order packet
+  * @tp_vars: the private data of the current TP meter session
+- * @skb: the buffer containing the received packet
++ * @seqno: sequence number of new received packet
++ * @payload_len: length of the received packet
+  *
+  * Store the out of order packet in the unacked list for late processing. This
+  * packets are kept in this list so that they can be ACKed at once as soon as
+@@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  * Return: true if the packed has been successfully processed, false otherwise
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+-                                        const struct sk_buff *skb)
++                                        u32 seqno, u32 payload_len)
+       __must_hold(&tp_vars->unacked_lock)
+ {
+-      const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+-      u32 payload_len;
+       bool added = false;
+       new = kmalloc(sizeof(*new), GFP_ATOMIC);
+       if (unlikely(!new))
+               return false;
+-      icmp = (struct batadv_icmp_tp_packet *)skb->data;
+-
+-      new->seqno = ntohl(icmp->seqno);
+-      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      new->seqno = seqno;
+       new->len = payload_len;
+       /* if the list is empty immediately attach this new object */
+@@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+-      size_t packet_size;
++      u32 payload_len;
+       u32 to_ack;
+       u32 seqno;
+@@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+-      if (batadv_seq_before(seqno, tp_vars->last_recv))
++      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      to_ack = seqno + payload_len;
++      if (batadv_seq_before(to_ack, tp_vars->last_recv))
+               goto send_ack;
+       /* if the packet is out of order enqueue it */
+-      if (ntohl(icmp->seqno) != tp_vars->last_recv) {
++      if (batadv_seq_before(tp_vars->last_recv, seqno)) {
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++              if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
+                       spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
+               }
+@@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       }
+       /* if everything was fine count the ACKed bytes */
+-      packet_size = skb->len - sizeof(struct batadv_unicast_packet);
+-      tp_vars->last_recv += packet_size;
++      tp_vars->last_recv = to_ack;
+       /* check if this ordered message filled a gap.... */
+       batadv_tp_ack_unordered(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-6.6/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
new file mode 100644 (file)
index 0000000..56fc187
--- /dev/null
@@ -0,0 +1,40 @@
+From e9b6e7ba8f3dbd6c4e1d47d35900a8eead4cabe2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:21 +0200
+Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery
+ detection
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream.
+
+The recover variable and the last_sent sequence number are initialized on
+purpose as a really high value which will wrap-around after the first 2000
+bytes. The fast recovery precondition must therefore not use simple integer
+comparisons but use helpers which are aware of the sequence number
+wrap-arounds.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index c79352cfddc4ae..089ac7cc9fbebc 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (tp_vars->recover >= recv_ack)
++              if (!batadv_seq_before(tp_vars->recover, recv_ack))
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-6.6/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
new file mode 100644 (file)
index 0000000..ef1d76c
--- /dev/null
@@ -0,0 +1,40 @@
+From 6145606fe38b320c24ea6660a8a00b929abfe0c2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:17 +0200
+Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit febfb1b86224489535312296ecfa3d4bf467f339 upstream.
+
+When batadv_tp_update_cwnd() is called, dec_cwnd is increased. But dec_cwnd
+is only initialixed (to 0) when a duplicate Ack was received or when cwnd
+is below the ss_threshold.
+
+Just initialize the cwnd during the initialization to avoid any potential
+access of uninitialized data.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 750e5e9d3dc9d6..f6ccb639744a2a 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1055,6 +1055,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+        * soft_interface, hence its MTU
+        */
+       tp_vars->cwnd = BATADV_TP_PLEN * 3;
++      tp_vars->dec_cwnd = 0;
++
+       /* at the beginning initialise the SS threshold to the biggest possible
+        * window size, hence the AWND size
+        */
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-6.6/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
new file mode 100644 (file)
index 0000000..cebe24b
--- /dev/null
@@ -0,0 +1,42 @@
+From 3be63c0302467f403a076aaa413d5835c2333034 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:16 +0200
+Subject: batman-adv: tp_meter: initialize dup_acks explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b2b68b32a715e0328662801576974aa37b942b00 upstream.
+
+When an ack with a sequence number equal to the last_acked is received, the
+dup_acks counter is increased to decide whether fast retransmit should be
+performed. Only when the sequence numbers are not equal, the dup_acks is
+set to the initial value (0).
+
+But if the initial packet would have the sequence number
+BATADV_TP_FIRST_SEQ, dup_acks would not be initialized and atomic_inc would
+operate on an undefined starting value. It is therefore required to have it
+explicitly initialized during the start of the sender session.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index f350a60e6c76b9..750e5e9d3dc9d6 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1045,6 +1045,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->icmp_uid = icmp_uid;
+       tp_vars->last_sent = BATADV_TP_FIRST_SEQ;
++      atomic_set(&tp_vars->dup_acks, 0);
+       atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ);
+       tp_vars->fast_recovery = false;
+       tp_vars->recover = BATADV_TP_FIRST_SEQ;
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-6.6/batman-adv-tp_meter-initialize-last_recv_time-during.patch
new file mode 100644 (file)
index 0000000..a29729f
--- /dev/null
@@ -0,0 +1,68 @@
+From 1a12b4e88d4defb3b9b531da9d319d123ffc9c46 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:25 +0200
+Subject: batman-adv: tp_meter: initialize last_recv_time during init
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream.
+
+The last_recv_time is the most important indicator for a receiver session
+to figure out whether a session timed out or not. But this information was
+only initialized after the session was added to the tp_receiver_list and
+after the timer was started.
+
+In the worst case, the timer (function) could have tried to access this
+information before the actual initialization was reached. Like rest of the
+variables of the tp_meter receiver session, this field has to be filled out
+before any other (parallel running) context has the chance to access it.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 2bba53fc6da5c0..133eed2fa9507f 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1403,8 +1403,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+-      if (tp_vars)
++      if (tp_vars) {
++              tp_vars->last_recv_time = jiffies;
+               goto out_unlock;
++      }
+       if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) {
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+@@ -1432,6 +1434,8 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      tp_vars->last_recv_time = jiffies;
++
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1480,9 +1484,9 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                                  icmp->orig);
+                       goto out;
+               }
+-      }
+-      tp_vars->last_recv_time = jiffies;
++              tp_vars->last_recv_time = jiffies;
++      }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-6.6/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
new file mode 100644 (file)
index 0000000..54fccfc
--- /dev/null
@@ -0,0 +1,44 @@
+From 8ecfc8c90700f52bb6d5c91a50e24cd6be319a55 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:15 +0200
+Subject: batman-adv: tp_meter: keep unacked list in ascending ordered
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream.
+
+When batadv_tp_handle_out_of_order inserts a new entry in the list of
+unacked (out of order) packets, it searches from the entry with the newest
+sequence number towards oldest sequence number. If an entry is found which
+is older than the newly entry, the new entry has to be added after the
+found one to keep the ascending order.
+
+But for this operation list_add_tail() was used. But this function adds an
+entry _before_ another one. As result, the list would contain a lot of
+swapped sequence numbers. The consumer of this list
+(batadv_tp_ack_unordered()) would then fail to correctly ack packets.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index dfc3374549921f..f350a60e6c76b9 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1325,7 +1325,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                * one is attached _after_ it. In this way the list is kept in
+                * ascending order
+                */
+-              list_add_tail(&new->list, &un->list);
++              list_add(&new->list, &un->list);
+               added = true;
+               break;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-6.6/batman-adv-tp_meter-prevent-parallel-modifications-o.patch
new file mode 100644 (file)
index 0000000..026f7ca
--- /dev/null
@@ -0,0 +1,157 @@
+From b1d63ded013db25261513ec8b576d7d72b490f3f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:33 +0200
+Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream.
+
+When last_recv is updated to store the last receive sequence number, it is
+assuming that nothing is modifying in parallel while:
+
+* check for outdated packets is done
+* out of order check is performed (and packets are stored in out-of-order
+  queue)
+* the out-of-order queue was searched for closed gaps
+* sequence number for next ack is calculated
+
+Nothing of that was actually protected. It could therefore happen that the
+last_recv was updated multiple times in parallel and the final sequence
+number was calculated with deltas which had no connection to the sequence
+number they were added to.
+
+Lock this whole region with the same lock which was already used to protect
+the unacked (out-of-order) list.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 22 +++++++++++++---------
+ net/batman-adv/types.h    |  2 +-
+ 2 files changed, 14 insertions(+), 10 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index b882919a868ca0..f745bc09a04963 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                                         const struct sk_buff *skb)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+@@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       payload_len = skb->len - sizeof(struct batadv_unicast_packet);
+       new->len = payload_len;
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
+               tp_vars->unacked_count++;
+-              goto out;
++              return true;
+       }
+       /* otherwise loop over the list and either drop the packet because this
+@@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+               tp_vars->unacked_count--;
+       }
+-out:
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+-
+       return true;
+ }
+@@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+  * @tp_vars: the private data of the current TP meter session
+  */
+ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       struct batadv_tp_unacked *un, *safe;
+       u32 to_ack;
+@@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+       /* go through the unacked packet list and possibly ACK them as
+        * well
+        */
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               /* the list is ordered, therefore it is possible to stop as soon
+                * there is a gap between the last acked seqno and the seqno of
+@@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               kfree(un);
+               tp_vars->unacked_count--;
+       }
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+ /**
+@@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+       size_t packet_size;
++      u32 to_ack;
+       u32 seqno;
+       icmp = (struct batadv_icmp_tp_packet *)skb->data;
+@@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
++      spin_lock_bh(&tp_vars->unacked_lock);
++
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+@@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb))
++              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++                      spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
++              }
+               /* send a duplicate ACK */
+               goto send_ack;
+@@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       batadv_tp_ack_unordered(tp_vars);
+ send_ack:
++      to_ack = tp_vars->last_recv;
++      spin_unlock_bh(&tp_vars->unacked_lock);
++
+       /* send the ACK. If the received packet was out of order, the ACK that
+        * is going to be sent is a duplicate (the sender will count them and
+        * possibly enter Fast Retransmit as soon as it has reached 3)
+        */
+-      batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv,
++      batadv_tp_send_ack(bat_priv, icmp->orig, to_ack,
+                          icmp->timestamp, icmp->session, icmp->uid);
+ out:
+       batadv_tp_vars_put(tp_vars);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 7c32947414cd80..883da8acae7381 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1491,7 +1491,7 @@ struct batadv_tp_vars {
+       /** @unacked_list: list of unacked packets (meta-info only) */
+       struct list_head unacked_list;
+-      /** @unacked_lock: protect unacked_list */
++      /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
+       spinlock_t unacked_lock;
+       /** @unacked_count: number of unacked entries */
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-6.6/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
new file mode 100644 (file)
index 0000000..e619c8f
--- /dev/null
@@ -0,0 +1,124 @@
+From 642ada629b7c53545d4af2a4bc52a2e1fd84d0c0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:31 +0200
+Subject: batman-adv: tp_meter: restrict number of unacked list entries
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream.
+
+When the unacked_list is unbound, an attacker could send messages with
+small lengths and appropriated seqno + gaps to force the receiver to
+allocate more and more unacked_list entries. And the end either causing an
+out-of-memory situation or increase the management overhead for the (large)
+list that significant portions of CPU cycles are wasted in searching
+through the list.
+
+When limiting the list to a specific number, it is important to still
+correctly add a new entry to the list. But if the list became larger than
+the limit, the last entry of the list (with the highest seqno) must be
+dropped to still allow the earlier seqnos to finish and therefore to
+continue the process. Otherwise, the process might get stuck with too high
+seqnos which are not handled by batadv_tp_ack_unordered().
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++-
+ net/batman-adv/types.h    |  3 +++
+ 2 files changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 133eed2fa9507f..638dd438a6c052 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -87,6 +87,11 @@
+ #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \
+                       sizeof(struct batadv_unicast_packet))
++/**
++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack
++ */
++#define BATADV_TP_MAX_UNACKED 100
++
+ static u8 batadv_tp_prerandom[4096] __read_mostly;
+ /**
+@@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
+               goto out;
+       }
+@@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                */
+               list_add(&new->list, &un->list);
+               added = true;
++              tp_vars->unacked_count++;
+               break;
+       }
+       /* received packet with smallest seqno out of order; add it to front */
+-      if (!added)
++      if (!added) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
++      }
++
++      /* remove the last (biggest) unacked seqno when list is too large */
++      if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) {
++              un = list_last_entry(&tp_vars->unacked_list,
++                                   struct batadv_tp_unacked, list);
++              list_del(&un->list);
++              kfree(un);
++              tp_vars->unacked_count--;
++      }
+ out:
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+@@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       spin_lock_init(&tp_vars->unacked_lock);
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
++      tp_vars->unacked_count = 0;
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index d56886951177d9..7c32947414cd80 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1494,6 +1494,9 @@ struct batadv_tp_vars {
+       /** @unacked_lock: protect unacked_list */
+       spinlock_t unacked_lock;
++      /** @unacked_count: number of unacked entries */
++      size_t unacked_count;
++
+       /** @last_recv_time: time (jiffies) a msg was received */
+       unsigned long last_recv_time;
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-6.6/batman-adv-tt-don-t-merge-change-entries-with-differ.patch
new file mode 100644 (file)
index 0000000..d7e48cb
--- /dev/null
@@ -0,0 +1,46 @@
+From 36b4b364317e4ff265b360a55140487feff9e64f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:35 +0200
+Subject: batman-adv: tt: don't merge change entries with different VIDs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream.
+
+batadv_tt_local_event() merges/cancels events for the same client which
+would conflict or be duplicates. The matching of the queued events only
+compares the MAC address - the VLAN ID stored in each event is ignored.
+
+If a MAC would now appear on multiple VID, the two ADD change events (for
+VID 1 and VID 2) would be merged to a single vid event. The remote can
+therefore not calculate the correct TT table and desync. A full translation
+table exchange is required to recover from this state.
+
+A check of VID is therefore necessary to avoid such wrong merges/cancels.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 7041cd69e20070..69a42bc3fa02e6 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -485,6 +485,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
+               if (!batadv_compare_eth(entry->change.addr, common->addr))
+                       continue;
++              if (entry->change.vid != tt_change_node->change.vid)
++                      continue;
++
+               /* DEL+ADD in the same orig interval have no effect and can be
+                * removed to avoid silly behaviour on the receiver side. The
+                * other way around (ADD+DEL) can happen in case of roaming of
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tt-track-roam-count-per-vid.patch b/queue-6.6/batman-adv-tt-track-roam-count-per-vid.patch
new file mode 100644 (file)
index 0000000..9eb7bb8
--- /dev/null
@@ -0,0 +1,89 @@
+From 1684d7c52136f57f14fae426b995cf59286509ca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:36 +0200
+Subject: batman-adv: tt: track roam count per VID
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream.
+
+batadv_tt_check_roam_count() is supposed to track roaming of a TT entry.
+But TT entries are for a MAC + VID. The VID was completely missed and thus
+leads to incorrect detection of ROAM counts when a client MAC exists in
+multiple VLANs.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 9 +++++++--
+ net/batman-adv/types.h             | 3 +++
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 69a42bc3fa02e6..2a7caab5c197ea 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -3517,6 +3517,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  * batadv_tt_check_roam_count() - check if a client has roamed too frequently
+  * @bat_priv: the bat priv with all the soft interface information
+  * @client: mac address of the roaming client
++ * @vid: VLAN identifier
+  *
+  * This function checks whether the client already reached the
+  * maximum number of possible roaming phases. In this case the ROAMING_ADV
+@@ -3524,7 +3525,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  *
+  * Return: true if the ROAMING_ADV can be sent, false otherwise
+  */
+-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid)
+ {
+       struct batadv_tt_roam_node *tt_roam_node;
+       bool ret = false;
+@@ -3537,6 +3538,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               if (!batadv_compare_eth(tt_roam_node->addr, client))
+                       continue;
++              if (tt_roam_node->vid != vid)
++                      continue;
++
+               if (batadv_has_timed_out(tt_roam_node->first_time,
+                                        BATADV_ROAMING_MAX_TIME))
+                       continue;
+@@ -3558,6 +3562,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               atomic_set(&tt_roam_node->counter,
+                          BATADV_ROAMING_MAX_COUNT - 1);
+               ether_addr_copy(tt_roam_node->addr, client);
++              tt_roam_node->vid = vid;
+               list_add(&tt_roam_node->list, &bat_priv->tt.roam_list);
+               ret = true;
+@@ -3594,7 +3599,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
+       /* before going on we have to check whether the client has
+        * already roamed to us too many times
+        */
+-      if (!batadv_tt_check_roam_count(bat_priv, client))
++      if (!batadv_tt_check_roam_count(bat_priv, client, vid))
+               goto out;
+       batadv_dbg(BATADV_DBG_TT, bat_priv,
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 883da8acae7381..0368cb93865f39 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1995,6 +1995,9 @@ struct batadv_tt_roam_node {
+       /** @addr: mac address of the client in the roaming phase */
+       u8 addr[ETH_ALEN];
++      /** @vid: VLAN identifier */
++      u16 vid;
++
+       /**
+        * @counter: number of allowed roaming events per client within a single
+        * OGM interval (changes are committed with each OGM)
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-6.6/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
new file mode 100644 (file)
index 0000000..5a1649e
--- /dev/null
@@ -0,0 +1,154 @@
+From c4bfecb070ea143bda1e2db4896c482dcf803d35 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:39 +0200
+Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream.
+
+TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to
+signal that the OGM handler should be called (with NULL for data) when the
+specific TVLV container was not found in the OGM. This is used by:
+
+* DAT
+* GW
+* Multicast (OGM + Tracker)
+
+The state whether the handler was executed was stored in the struct
+batadv_tvlv_handler. But the TVLV processing is started without any lock.
+Multiple parallel contexts processing TVLVs would therefore overwrite each
+others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared
+batadv_tvlv_handler.
+
+Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine,
+per TVLV buffer, whether a matching container was present by scanning the
+packet's buffer.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tvlv.c  | 63 ++++++++++++++++++++++++++++++++++++++----
+ net/batman-adv/types.h |  7 -----
+ 2 files changed, 57 insertions(+), 13 deletions(-)
+
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index e1cd27b99bd119..cc66e231ccb8a0 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -398,7 +398,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+               tvlv_handler->ogm_handler(bat_priv, orig_node,
+                                         BATADV_NO_FLAGS,
+                                         tvlv_value, tvlv_value_len);
+-              tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
+               break;
+       case BATADV_UNICAST_TVLV:
+               if (!skb)
+@@ -430,6 +429,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+       return NET_RX_SUCCESS;
+ }
++/**
++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
++ * @tvlv_value: tvlv content
++ * @tvlv_value_len: tvlv content length
++ * @type: tvlv container type to look for
++ * @version: tvlv container version to look for
++ *
++ * Return: true if a container of the given type and version is present in the
++ * tvlv buffer, false otherwise.
++ */
++static bool batadv_tvlv_containers_contain(void *tvlv_value,
++                                         u16 tvlv_value_len, u8 type,
++                                         u8 version)
++{
++      struct batadv_tvlv_hdr *tvlv_hdr;
++      u16 tvlv_value_cont_len;
++
++      while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
++              tvlv_hdr = tvlv_value;
++              tvlv_value_cont_len = ntohs(tvlv_hdr->len);
++              tvlv_value = tvlv_hdr + 1;
++              tvlv_value_len -= sizeof(*tvlv_hdr);
++
++              if (tvlv_value_cont_len > tvlv_value_len)
++                      break;
++
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
++              if (tvlv_hdr->type == type && tvlv_hdr->version == version)
++                      return true;
++
++              tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
++              tvlv_value_len -= tvlv_value_cont_len;
++      }
++
++      return false;
++}
++
+ /**
+  * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the
+  *  appropriate handlers
+@@ -449,7 +490,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+                                  struct sk_buff *skb, void *tvlv_value,
+                                  u16 tvlv_value_len)
+ {
++      u16 tvlv_value_start_len = tvlv_value_len;
+       struct batadv_tvlv_handler *tvlv_handler;
++      void *tvlv_value_start = tvlv_value;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       u16 tvlv_value_cont_len;
+       u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
+@@ -493,12 +536,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+               if (!tvlv_handler->ogm_handler)
+                       continue;
+-              if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
+-                  !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
+-                      tvlv_handler->ogm_handler(bat_priv, orig_node,
+-                                                cifnotfound, NULL, 0);
++              if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND))
++                      continue;
+-              tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
++              /* if the corresponding container was present then the handler
++               * was already called from the loop above
++               */
++              if (batadv_tvlv_containers_contain(tvlv_value_start,
++                                                 tvlv_value_start_len,
++                                                 tvlv_handler->type,
++                                                 tvlv_handler->version))
++                      continue;
++
++              tvlv_handler->ogm_handler(bat_priv, orig_node,
++                                        cifnotfound, NULL, 0);
+       }
+       rcu_read_unlock();
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 0368cb93865f39..4856cc98fc3c51 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -2413,13 +2413,6 @@ enum batadv_tvlv_handler_flags {
+        *  will call this handler even if its type was not found (with no data)
+        */
+       BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
+-
+-      /**
+-       * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the
+-       *  API marks a handler as being called, so it won't be called if the
+-       *  BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
+-       */
+-      BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
+ };
+ #endif /* _NET_BATMAN_ADV_TYPES_H_ */
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-6.6/batman-adv-tvlv-enforce-2-byte-alignment.patch
new file mode 100644 (file)
index 0000000..b41d3f1
--- /dev/null
@@ -0,0 +1,106 @@
+From b9884b623fa18c2def868eac1a8509453235f6c8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:38 +0200
+Subject: batman-adv: tvlv: enforce 2-byte alignment
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream.
+
+The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte
+alignment, so a following OGM must start at an even offset. As the header
+length is even, an odd tvlv_len would misalign it and trigger unaligned
+accesses on strict-alignment architectures.
+
+Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in
+the mesh. Therefore, reject such malformed packets.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+[ Drop change for non-existing mcast handling ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 11 ++++++++++-
+ net/batman-adv/bat_v_ogm.c  | 11 ++++++++++-
+ net/batman-adv/tvlv.c       |  6 ++++++
+ 3 files changed, 26 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index b37c9fb178ae50..5dd3e1f281bab2 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -310,14 +310,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
+                         const struct batadv_ogm_packet *ogm_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm_packet->tvlv_len);
++
++      /* the fields of an aggregated OGM are accessed assuming (at least)
++       * 2-byte alignment, so a following OGM must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 48a67705eba85c..c5c4d33cb19838 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -853,14 +853,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len,
+                        const struct batadv_ogm2_packet *ogm2_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm2_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm2_packet->tvlv_len);
++
++      /* the fields of an aggregated OGMv2 are accessed assuming (at least)
++       * 2-byte alignment, so a following OGMv2 must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm2_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 8d6b017c433cc9..e1cd27b99bd119 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -464,6 +464,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+               if (tvlv_value_cont_len > tvlv_value_len)
+                       break;
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
+               tvlv_handler = batadv_tvlv_handler_get(bat_priv,
+                                                      tvlv_hdr->type,
+                                                      tvlv_hdr->version);
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-6.6/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
new file mode 100644 (file)
index 0000000..97e6c5a
--- /dev/null
@@ -0,0 +1,106 @@
+From 55980242fb7e3c6e754d68b05c6794e5b5b8c3d8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:11:30 +0200
+Subject: batman-adv: v: prevent OGM aggregation on disabled hardif
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream.
+
+When an interface gets disabled, the worker is correctly disabled by
+batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable().
+In this process, the skb aggr_list is also freed.
+
+But batadv_v_ogm_send_meshif() can still queue new skbs (via
+batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all
+cores can no longer find the RCU protected list of hard interfaces. These
+queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work.
+
+The batadv_v_ogm_iface_disable() function must block
+batadv_v_ogm_queue_on_if() to avoid leak of skbs.
+
+Cc: stable@kernel.org
+Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_v.c     |  1 +
+ net/batman-adv/bat_v_ogm.c | 12 ++++++++++++
+ net/batman-adv/types.h     |  6 ++++++
+ 3 files changed, 19 insertions(+)
+
+diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c
+index d35479c465e2c4..e13b39121f2db8 100644
+--- a/net/batman-adv/bat_v.c
++++ b/net/batman-adv/bat_v.c
+@@ -819,6 +819,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
+       hard_iface->bat_v.aggr_len = 0;
+       skb_queue_head_init(&hard_iface->bat_v.aggr_list);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq,
+                         batadv_v_ogm_aggr_work);
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 8cfc3944dcfd52..48a67705eba85c 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -254,11 +254,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
+       }
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      if (!hard_iface->bat_v.aggr_list_enabled) {
++              kfree_skb(skb);
++              goto unlock;
++      }
++
+       if (!batadv_v_ogm_queue_left(skb, hard_iface))
+               batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+       __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
++
++unlock:
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+@@ -421,6 +428,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
++      spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = true;
++      spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
++
+       batadv_v_ogm_start_queue_timer(hard_iface);
+       batadv_v_ogm_start_timer(bat_priv);
+@@ -436,6 +447,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
+       cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq);
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       batadv_v_ogm_aggr_list_free(hard_iface);
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 2dfdc78d4ded99..d56886951177d9 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v {
+       /** @aggr_list: queue for to be aggregated OGM packets */
+       struct sk_buff_head aggr_list;
++      /**
++       * @aggr_list_enabled: aggr_list is active and new skbs can be
++       * enqueued. Protected by aggr_list.lock after initialization
++       */
++      bool aggr_list_enabled:1;
++
+       /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */
+       unsigned int aggr_len;
+-- 
+2.53.0
+
diff --git a/queue-6.6/fs-prepare-for-adding-lsm-blob-to-backing_file.patch b/queue-6.6/fs-prepare-for-adding-lsm-blob-to-backing_file.patch
new file mode 100644 (file)
index 0000000..42b4f23
--- /dev/null
@@ -0,0 +1,89 @@
+From aa14e051d0d097c168a672517c3cf44173b3a8cd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 27 Jun 2026 14:57:18 +0800
+Subject: fs: prepare for adding LSM blob to backing_file
+
+From: Amir Goldstein <amir73il@gmail.com>
+
+[ Upstream commit 880bd496ec72a6dcb00cb70c430ef752ba242ae7 ]
+
+In preparation to adding LSM blob to backing_file struct, factor out
+helpers init_backing_file() and backing_file_free().
+
+Cc: stable@vger.kernel.org
+Cc: linux-fsdevel@vger.kernel.org
+Cc: linux-unionfs@vger.kernel.org
+Cc: linux-erofs@lists.ozlabs.org
+Signed-off-by: Amir Goldstein <amir73il@gmail.com>
+Reviewed-by: Serge Hallyn <serge@hallyn.com>
+[PM: use the term "LSM blob", fix comment style to match file]
+Signed-off-by: Paul Moore <paul@paul-moore.com>
+[1. The commit def3ae83da02
+("fs: store real path instead of fake path in backing file f_path")
+is not merged, The 6.6 LTS version accordingly operates on
+&ff->real_path instead of &ff->user_path.
+
+2. Mainline's file_free() does both the backing_file cleanup and the
+kmem_cache_free() synchronously.  Linux 6.6.y defers the actual kfree()
+to file_free_rcu() via call_rcu(), so only path_put() is done
+synchronously in file_free().]
+Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/file_table.c | 20 +++++++++++++++++++-
+ 1 file changed, 19 insertions(+), 1 deletion(-)
+
+diff --git a/fs/file_table.c b/fs/file_table.c
+index 234284ef72a9a5..b4c208a771539d 100644
+--- a/fs/file_table.c
++++ b/fs/file_table.c
+@@ -72,11 +72,16 @@ static void file_free_rcu(struct rcu_head *head)
+               kmem_cache_free(filp_cachep, f);
+ }
++static inline void backing_file_free(struct backing_file *ff)
++{
++      path_put(&ff->real_path);
++}
++
+ static inline void file_free(struct file *f)
+ {
+       security_file_free(f);
+       if (unlikely(f->f_mode & FMODE_BACKING))
+-              path_put(backing_file_real_path(f));
++              backing_file_free(backing_file(f));
+       if (likely(!(f->f_mode & FMODE_NOACCOUNT)))
+               percpu_counter_dec(&nr_files);
+       call_rcu(&f->f_rcuhead, file_free_rcu);
+@@ -252,6 +257,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
+       return f;
+ }
++static int init_backing_file(struct backing_file *ff)
++{
++      memset(&ff->real_path, 0, sizeof(ff->real_path));
++      return 0;
++}
++
+ /*
+  * Variant of alloc_empty_file() that allocates a backing_file container
+  * and doesn't check and modify nr_files.
+@@ -274,7 +285,14 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
+               return ERR_PTR(error);
+       }
++      /* The f_mode flags must be set before fput(). */
+       ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
++      error = init_backing_file(ff);
++      if (unlikely(error)) {
++              fput(&ff->file);
++              return ERR_PTR(error);
++      }
++
+       return &ff->file;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/lsm-add-backing_file-lsm-hooks.patch b/queue-6.6/lsm-add-backing_file-lsm-hooks.patch
new file mode 100644 (file)
index 0000000..ce76bb3
--- /dev/null
@@ -0,0 +1,527 @@
+From f8d61919aeb3768608afa3cc2158c74dda3ee5a8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 27 Jun 2026 14:57:19 +0800
+Subject: lsm: add backing_file LSM hooks
+
+From: Paul Moore <paul@paul-moore.com>
+
+[ Upstream commit 6af36aeb147a06dea47c49859cd6ca5659aeb987 ]
+
+Stacked filesystems such as overlayfs do not currently provide the
+necessary mechanisms for LSMs to properly enforce access controls on the
+mmap() and mprotect() operations.  In order to resolve this gap, a LSM
+security blob is being added to the backing_file struct and the following
+new LSM hooks are being created:
+
+ security_backing_file_alloc()
+ security_backing_file_free()
+ security_mmap_backing_file()
+
+The first two hooks are to manage the lifecycle of the LSM security blob
+in the backing_file struct, while the third provides a new mmap() access
+control point for the underlying backing file.  It is also expected that
+LSMs will likely want to update their security_file_mprotect() callback
+to address issues with their mprotect() controls, but that does not
+require a change to the security_file_mprotect() LSM hook.
+
+There are a three other small changes to support these new LSM hooks:
+* Pass the user file associated with a backing file down to
+alloc_empty_backing_file() so it can be included in the
+security_backing_file_alloc() hook.
+* Add getter and setter functions for the backing_file struct LSM blob
+as the backing_file struct remains private to fs/file_table.c.
+* Constify the file struct field in the LSM common_audit_data struct to
+better support LSMs that need to pass a const file struct pointer into
+the common LSM audit code.
+
+Thanks to Arnd Bergmann for identifying the missing EXPORT_SYMBOL_GPL()
+and supplying a fixup.
+
+Cc: stable@vger.kernel.org
+Cc: linux-fsdevel@vger.kernel.org
+Cc: linux-unionfs@vger.kernel.org
+Cc: linux-erofs@lists.ozlabs.org
+Reviewed-by: Amir Goldstein <amir73il@gmail.com>
+Reviewed-by: Serge Hallyn <serge@hallyn.com>
+Reviewed-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Paul Moore <paul@paul-moore.com>
+[1. Mainline uses call_int_hook(FUNC, ...) with the default IRC baked
+into the macro. Linux 6.6.y uses call_int_hook(FUNC, IRC, ...) requiring
+an explicit default return value.
+
+2. fs/backing-file.c does not exist in LTS
+Linux 6.6.y places backing_file_open() in fs/open.c and lacks a
+dedicated fs/backing-file.c.  The backing_file_mmap() function and
+scoped_with_creds() do not exist in 6.6.y.  Therefore the LTS patch calls
+security_mmap_backing_file() directly in ovl_mmap() in
+fs/overlayfs/file.c rather than modifying backing_file_mmap().
+
+3. Missing filesystems/modules
+Linux 6.6.y does not have backing_tmpfile_open(), fs/fuse/passthrough.c,
+or the erofs ishare mmap path that the mainline patch touches.  These hunks
+are dropped in the 6.6 LTS backport.
+
+4. Use macro backing_file to replace inline function to eliminate the
+const warning.]
+Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/file_table.c               |  32 +++++++---
+ fs/internal.h                 |   3 +-
+ fs/open.c                     |   7 ++-
+ fs/overlayfs/file.c           |   8 ++-
+ include/linux/fs.h            |  15 ++++-
+ include/linux/lsm_audit.h     |   2 +-
+ include/linux/lsm_hook_defs.h |   5 ++
+ include/linux/lsm_hooks.h     |   1 +
+ include/linux/security.h      |  22 +++++++
+ security/security.c           | 110 ++++++++++++++++++++++++++++++++++
+ 10 files changed, 190 insertions(+), 15 deletions(-)
+
+diff --git a/fs/file_table.c b/fs/file_table.c
+index b4c208a771539d..fa4d4c54f790c8 100644
+--- a/fs/file_table.c
++++ b/fs/file_table.c
+@@ -48,12 +48,12 @@ static struct percpu_counter nr_files __cacheline_aligned_in_smp;
+ struct backing_file {
+       struct file file;
+       struct path real_path;
++#ifdef CONFIG_SECURITY
++      void *security;
++#endif
+ };
+-static inline struct backing_file *backing_file(struct file *f)
+-{
+-      return container_of(f, struct backing_file, file);
+-}
++#define backing_file(f) container_of(f, struct backing_file, file)
+ struct path *backing_file_real_path(struct file *f)
+ {
+@@ -72,8 +72,21 @@ static void file_free_rcu(struct rcu_head *head)
+               kmem_cache_free(filp_cachep, f);
+ }
++#ifdef CONFIG_SECURITY
++void *backing_file_security(const struct file *f)
++{
++      return backing_file(f)->security;
++}
++
++void backing_file_set_security(struct file *f, void *security)
++{
++      backing_file(f)->security = security;
++}
++#endif /* CONFIG_SECURITY */
++
+ static inline void backing_file_free(struct backing_file *ff)
+ {
++      security_backing_file_free(&ff->file);
+       path_put(&ff->real_path);
+ }
+@@ -257,10 +270,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
+       return f;
+ }
+-static int init_backing_file(struct backing_file *ff)
++static int init_backing_file(struct backing_file *ff,
++                           const struct file *user_file)
+ {
+       memset(&ff->real_path, 0, sizeof(ff->real_path));
+-      return 0;
++      backing_file_set_security(&ff->file, NULL);
++      return security_backing_file_alloc(&ff->file, user_file);
+ }
+ /*
+@@ -270,7 +285,8 @@ static int init_backing_file(struct backing_file *ff)
+  * This is only for kernel internal use, and the allocate file must not be
+  * installed into file tables or such.
+  */
+-struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
++struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
++                                    const struct file *user_file)
+ {
+       struct backing_file *ff;
+       int error;
+@@ -287,7 +303,7 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
+       /* The f_mode flags must be set before fput(). */
+       ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
+-      error = init_backing_file(ff);
++      error = init_backing_file(ff, user_file);
+       if (unlikely(error)) {
+               fput(&ff->file);
+               return ERR_PTR(error);
+diff --git a/fs/internal.h b/fs/internal.h
+index d64ae03998cce9..576026e1ce3aab 100644
+--- a/fs/internal.h
++++ b/fs/internal.h
+@@ -93,7 +93,8 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
+  */
+ struct file *alloc_empty_file(int flags, const struct cred *cred);
+ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred);
+-struct file *alloc_empty_backing_file(int flags, const struct cred *cred);
++struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
++                                    const struct file *user_file);
+ static inline void put_file_access(struct file *file)
+ {
+diff --git a/fs/open.c b/fs/open.c
+index b5ea1dcbfb2242..5b164481d80b34 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -1175,18 +1175,19 @@ EXPORT_SYMBOL_GPL(kernel_file_open);
+  * the backing inode on the underlying filesystem, which can be
+  * retrieved using backing_file_real_path().
+  */
+-struct file *backing_file_open(const struct path *path, int flags,
++struct file *backing_file_open(const struct file *user_file, int flags,
+                              const struct path *real_path,
+                              const struct cred *cred)
+ {
++      const struct path *user_path = &user_file->f_path;
+       struct file *f;
+       int error;
+-      f = alloc_empty_backing_file(flags, cred);
++      f = alloc_empty_backing_file(flags, cred, user_file);
+       if (IS_ERR(f))
+               return f;
+-      f->f_path = *path;
++      f->f_path = *user_path;
+       path_get(real_path);
+       *backing_file_real_path(f) = *real_path;
+       error = do_dentry_open(f, d_inode(real_path->dentry), NULL);
+diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
+index 8be4dc050d1ed2..07f00cf91977ee 100644
+--- a/fs/overlayfs/file.c
++++ b/fs/overlayfs/file.c
+@@ -60,7 +60,7 @@ static struct file *ovl_open_realfile(const struct file *file,
+               if (!inode_owner_or_capable(real_idmap, realinode))
+                       flags &= ~O_NOATIME;
+-              realfile = backing_file_open(&file->f_path, flags, realpath,
++              realfile = backing_file_open(file, flags, realpath,
+                                            current_cred());
+       }
+       revert_creds(old_cred);
+@@ -527,6 +527,7 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+ static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
+ {
++      struct file *user_file = vma->vm_file;
+       struct file *realfile = file->private_data;
+       const struct cred *old_cred;
+       int ret;
+@@ -540,6 +541,11 @@ static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
+       vma_set_file(vma, realfile);
+       old_cred = ovl_override_creds(file_inode(file)->i_sb);
++      ret = security_mmap_backing_file(vma, realfile, user_file);
++      if (ret) {
++              revert_creds(old_cred);
++              return ret;
++      }
+       ret = call_mmap(vma->vm_file, vma);
+       revert_creds(old_cred);
+       ovl_file_accessed(file);
+diff --git a/include/linux/fs.h b/include/linux/fs.h
+index 4cdeeaedaa404f..5a897ee50f29cf 100644
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -2515,11 +2515,24 @@ struct file *dentry_open(const struct path *path, int flags,
+                        const struct cred *creds);
+ struct file *dentry_create(const struct path *path, int flags, umode_t mode,
+                          const struct cred *cred);
+-struct file *backing_file_open(const struct path *path, int flags,
++struct file *backing_file_open(const struct file *user_file, int flags,
+                              const struct path *real_path,
+                              const struct cred *cred);
+ struct path *backing_file_real_path(struct file *f);
++#ifdef CONFIG_SECURITY
++void *backing_file_security(const struct file *f);
++void backing_file_set_security(struct file *f, void *security);
++#else
++static inline void *backing_file_security(const struct file *f)
++{
++      return NULL;
++}
++static inline void backing_file_set_security(struct file *f, void *security)
++{
++}
++#endif /* CONFIG_SECURITY */
++
+ /*
+  * file_real_path - get the path corresponding to f_inode
+  *
+diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
+index 97a8b21eb03339..c0a2839253fa1f 100644
+--- a/include/linux/lsm_audit.h
++++ b/include/linux/lsm_audit.h
+@@ -93,7 +93,7 @@ struct common_audit_data {
+ #endif
+               char *kmod_name;
+               struct lsm_ioctlop_audit *op;
+-              struct file *file;
++              const struct file *file;
+               struct lsm_ibpkey_audit *ibpkey;
+               struct lsm_ibendport_audit *ibendport;
+               int reason;
+diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
+index 2923754c13bce6..567d7f16609c8b 100644
+--- a/include/linux/lsm_hook_defs.h
++++ b/include/linux/lsm_hook_defs.h
+@@ -169,6 +169,9 @@ LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir,
+ LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
+ LSM_HOOK(int, 0, file_alloc_security, struct file *file)
+ LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
++LSM_HOOK(int, 0, backing_file_alloc, struct file *backing_file,
++       const struct file *user_file)
++LSM_HOOK(void, LSM_RET_VOID, backing_file_free, struct file *backing_file)
+ LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd,
+        unsigned long arg)
+ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
+@@ -176,6 +179,8 @@ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
+ LSM_HOOK(int, 0, mmap_addr, unsigned long addr)
+ LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot,
+        unsigned long prot, unsigned long flags)
++LSM_HOOK(int, 0, mmap_backing_file, struct vm_area_struct *vma,
++       struct file *backing_file, struct file *user_file)
+ LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma,
+        unsigned long reqprot, unsigned long prot)
+ LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd)
+diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
+index dcb5e5b5eb1352..3b56b60195ceb9 100644
+--- a/include/linux/lsm_hooks.h
++++ b/include/linux/lsm_hooks.h
+@@ -59,6 +59,7 @@ struct security_hook_list {
+ struct lsm_blob_sizes {
+       int     lbs_cred;
+       int     lbs_file;
++      int lbs_backing_file;
+       int     lbs_inode;
+       int     lbs_superblock;
+       int     lbs_ipc;
+diff --git a/include/linux/security.h b/include/linux/security.h
+index 937840870d86c3..4866ffdb4e6c1d 100644
+--- a/include/linux/security.h
++++ b/include/linux/security.h
+@@ -389,11 +389,17 @@ int security_kernfs_init_security(struct kernfs_node *kn_dir,
+ int security_file_permission(struct file *file, int mask);
+ int security_file_alloc(struct file *file);
+ void security_file_free(struct file *file);
++int security_backing_file_alloc(struct file *backing_file,
++                              const struct file *user_file);
++void security_backing_file_free(struct file *backing_file);
+ int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+ int security_file_ioctl_compat(struct file *file, unsigned int cmd,
+                              unsigned long arg);
+ int security_mmap_file(struct file *file, unsigned long prot,
+                       unsigned long flags);
++int security_mmap_backing_file(struct vm_area_struct *vma,
++                             struct file *backing_file,
++                             struct file *user_file);
+ int security_mmap_addr(unsigned long addr);
+ int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+                          unsigned long prot);
+@@ -984,6 +990,15 @@ static inline int security_file_alloc(struct file *file)
+ static inline void security_file_free(struct file *file)
+ { }
++static inline int security_backing_file_alloc(struct file *backing_file,
++                                            const struct file *user_file)
++{
++      return 0;
++}
++
++static inline void security_backing_file_free(struct file *backing_file)
++{ }
++
+ static inline int security_file_ioctl(struct file *file, unsigned int cmd,
+                                     unsigned long arg)
+ {
+@@ -1003,6 +1018,13 @@ static inline int security_mmap_file(struct file *file, unsigned long prot,
+       return 0;
+ }
++static inline int security_mmap_backing_file(struct vm_area_struct *vma,
++                                           struct file *backing_file,
++                                           struct file *user_file)
++{
++      return 0;
++}
++
+ static inline int security_mmap_addr(unsigned long addr)
+ {
+       return cap_mmap_addr(addr);
+diff --git a/security/security.c b/security/security.c
+index 1794860fd614ea..4b61766c3d27bd 100644
+--- a/security/security.c
++++ b/security/security.c
+@@ -78,6 +78,7 @@ struct security_hook_heads security_hook_heads __ro_after_init;
+ static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
+ static struct kmem_cache *lsm_file_cache;
++static struct kmem_cache *lsm_backing_file_cache;
+ static struct kmem_cache *lsm_inode_cache;
+ char *lsm_names;
+@@ -200,6 +201,8 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
+       lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
+       lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
++      lsm_set_blob_size(&needed->lbs_backing_file,
++                           &blob_sizes.lbs_backing_file);
+       /*
+        * The inode blob gets an rcu_head in addition to
+        * what the modules might need.
+@@ -374,6 +377,7 @@ static void __init ordered_lsm_init(void)
+       init_debug("cred blob size       = %d\n", blob_sizes.lbs_cred);
+       init_debug("file blob size       = %d\n", blob_sizes.lbs_file);
++      init_debug("backing_file blob size  = %d\n", blob_sizes.lbs_backing_file);
+       init_debug("inode blob size      = %d\n", blob_sizes.lbs_inode);
+       init_debug("ipc blob size        = %d\n", blob_sizes.lbs_ipc);
+       init_debug("msg_msg blob size    = %d\n", blob_sizes.lbs_msg_msg);
+@@ -388,6 +392,11 @@ static void __init ordered_lsm_init(void)
+               lsm_file_cache = kmem_cache_create("lsm_file_cache",
+                                                  blob_sizes.lbs_file, 0,
+                                                  SLAB_PANIC, NULL);
++      if (blob_sizes.lbs_backing_file)
++              lsm_backing_file_cache = kmem_cache_create(
++                                                 "lsm_backing_file_cache",
++                                                 blob_sizes.lbs_backing_file,
++                                                 0, SLAB_PANIC, NULL);
+       if (blob_sizes.lbs_inode)
+               lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
+                                                   blob_sizes.lbs_inode, 0,
+@@ -616,6 +625,30 @@ static int lsm_file_alloc(struct file *file)
+       return 0;
+ }
++/**
++ * lsm_backing_file_alloc - allocate a composite backing file blob
++ * @backing_file: the backing file
++ *
++ * Allocate the backing file blob for all the modules.
++ *
++ * Returns 0, or -ENOMEM if memory can't be allocated.
++ */
++static int lsm_backing_file_alloc(struct file *backing_file)
++{
++      void *blob;
++
++      if (!lsm_backing_file_cache) {
++              backing_file_set_security(backing_file, NULL);
++              return 0;
++      }
++
++      blob = kmem_cache_zalloc(lsm_backing_file_cache, GFP_KERNEL);
++      backing_file_set_security(backing_file, blob);
++      if (!blob)
++              return -ENOMEM;
++      return 0;
++}
++
+ /**
+  * lsm_inode_alloc - allocate a composite inode blob
+  * @inode: the inode that needs a blob
+@@ -2630,6 +2663,57 @@ void security_file_free(struct file *file)
+       }
+ }
++/**
++ * security_backing_file_alloc() - Allocate and setup a backing file blob
++ * @backing_file: the backing file
++ * @user_file: the associated user visible file
++ *
++ * Allocate a backing file LSM blob and perform any necessary initialization of
++ * the LSM blob.  There will be some operations where the LSM will not have
++ * access to @user_file after this point, so any important state associated
++ * with @user_file that is important to the LSM should be captured in the
++ * backing file's LSM blob.
++ *
++ * LSM's should avoid taking a reference to @user_file in this hook as it will
++ * result in problems later when the system attempts to drop/put the file
++ * references due to a circular dependency.
++ *
++ * Return: Return 0 if the hook is successful, negative values otherwise.
++ */
++int security_backing_file_alloc(struct file *backing_file,
++                              const struct file *user_file)
++{
++      int rc;
++
++      rc = lsm_backing_file_alloc(backing_file);
++      if (rc)
++              return rc;
++      rc = call_int_hook(backing_file_alloc, 0, backing_file, user_file);
++      if (unlikely(rc))
++              security_backing_file_free(backing_file);
++
++      return rc;
++}
++
++/**
++ * security_backing_file_free() - Free a backing file blob
++ * @backing_file: the backing file
++ *
++ * Free any LSM state associate with a backing file's LSM blob, including the
++ * blob itself.
++ */
++void security_backing_file_free(struct file *backing_file)
++{
++      void *blob = backing_file_security(backing_file);
++
++      call_void_hook(backing_file_free, backing_file);
++
++      if (blob) {
++              backing_file_set_security(backing_file, NULL);
++              kmem_cache_free(lsm_backing_file_cache, blob);
++      }
++}
++
+ /**
+  * security_file_ioctl() - Check if an ioctl is allowed
+  * @file: associated file
+@@ -2723,6 +2807,32 @@ int security_mmap_file(struct file *file, unsigned long prot,
+       return ima_file_mmap(file, prot, prot_adj, flags);
+ }
++/**
++ * security_mmap_backing_file - Check if mmap'ing a backing file is allowed
++ * @vma: the vm_area_struct for the mmap'd region
++ * @backing_file: the backing file being mmap'd
++ * @user_file: the user file being mmap'd
++ *
++ * Check permissions for a mmap operation on a stacked filesystem.  This hook
++ * is called after the security_mmap_file() and is responsible for authorizing
++ * the mmap on @backing_file.  It is important to note that the mmap operation
++ * on @user_file has already been authorized and the @vma->vm_file has been
++ * set to @backing_file.
++ *
++ * Return: Returns 0 if permission is granted.
++ */
++int security_mmap_backing_file(struct vm_area_struct *vma,
++                             struct file *backing_file,
++                             struct file *user_file)
++{
++      /* recommended by the stackable filesystem devs */
++      if (WARN_ON_ONCE(!(backing_file->f_mode & FMODE_BACKING)))
++              return -EIO;
++
++      return call_int_hook(mmap_backing_file, 0, vma, backing_file, user_file);
++}
++EXPORT_SYMBOL_GPL(security_mmap_backing_file);
++
+ /**
+  * security_mmap_addr() - Check if mmap'ing an address is allowed
+  * @addr: address
+-- 
+2.53.0
+
diff --git a/queue-6.6/selinux-fix-overlayfs-mmap-and-mprotect-access-check.patch b/queue-6.6/selinux-fix-overlayfs-mmap-and-mprotect-access-check.patch
new file mode 100644 (file)
index 0000000..f4978dc
--- /dev/null
@@ -0,0 +1,429 @@
+From bb1643bbb774b8de439075556cf7364333729fbd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 27 Jun 2026 14:57:20 +0800
+Subject: selinux: fix overlayfs mmap() and mprotect() access checks
+
+From: Paul Moore <paul@paul-moore.com>
+
+[ Upstream commit 82544d36b1729153c8aeb179e84750f0c085d3b1 ]
+
+The existing SELinux security model for overlayfs is to allow access if
+the current task is able to access the top level file (the "user" file)
+and the mounter's credentials are sufficient to access the lower
+level file (the "backing" file).  Unfortunately, the current code does
+not properly enforce these access controls for both mmap() and mprotect()
+operations on overlayfs filesystems.
+
+This patch makes use of the newly created security_mmap_backing_file()
+LSM hook to provide the missing backing file enforcement for mmap()
+operations, and leverages the backing file API and new LSM blob to
+provide the necessary information to properly enforce the mprotect()
+access controls.
+
+Cc: stable@vger.kernel.org
+Acked-by: Amir Goldstein <amir73il@gmail.com>
+Signed-off-by: Paul Moore <paul@paul-moore.com>
+[backing_file_user_path() not available
+Mainline uses backing_file_user_path(file) to obtain the user-visible path
+from a backing file. The 6.6.y version uses &file->f_path directly]
+Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ security/selinux/hooks.c          | 242 ++++++++++++++++++++++--------
+ security/selinux/include/objsec.h |  11 ++
+ 2 files changed, 189 insertions(+), 64 deletions(-)
+
+diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
+index 60092d0b013ce0..3f11c5ae8fbf0e 100644
+--- a/security/selinux/hooks.c
++++ b/security/selinux/hooks.c
+@@ -1717,49 +1717,72 @@ static inline int file_path_has_perm(const struct cred *cred,
+ static int bpf_fd_pass(const struct file *file, u32 sid);
+ #endif
+-/* Check whether a task can use an open file descriptor to
+-   access an inode in a given way.  Check access to the
+-   descriptor itself, and then use dentry_has_perm to
+-   check a particular permission to the file.
+-   Access to the descriptor is implicitly granted if it
+-   has the same SID as the process.  If av is zero, then
+-   access to the file is not checked, e.g. for cases
+-   where only the descriptor is affected like seek. */
+-static int file_has_perm(const struct cred *cred,
+-                       struct file *file,
+-                       u32 av)
++static int __file_has_perm(const struct cred *cred, const struct file *file,
++                         u32 av, bool bf_user_file)
++
+ {
+-      struct file_security_struct *fsec = selinux_file(file);
+-      struct inode *inode = file_inode(file);
+       struct common_audit_data ad;
+-      u32 sid = cred_sid(cred);
++      struct inode *inode;
++      u32 ssid = cred_sid(cred);
++      u32 tsid_fd;
+       int rc;
+-      ad.type = LSM_AUDIT_DATA_FILE;
+-      ad.u.file = file;
++      if (bf_user_file) {
++              struct backing_file_security_struct *bfsec;
++              const struct path *path;
+-      if (sid != fsec->sid) {
+-              rc = avc_has_perm(sid, fsec->sid,
+-                                SECCLASS_FD,
+-                                FD__USE,
+-                                &ad);
++              if (WARN_ON(!(file->f_mode & FMODE_BACKING)))
++                      return -EIO;
++
++              bfsec = selinux_backing_file(file);
++              path = &file->f_path;
++              tsid_fd = bfsec->uf_sid;
++              inode = d_inode(path->dentry);
++
++              ad.type = LSM_AUDIT_DATA_PATH;
++              ad.u.path = *path;
++      } else {
++              struct file_security_struct *fsec = selinux_file(file);
++
++              tsid_fd = fsec->sid;
++              inode = file_inode(file);
++
++              ad.type = LSM_AUDIT_DATA_FILE;
++              ad.u.file = file;
++      }
++
++      if (ssid != tsid_fd) {
++              rc = avc_has_perm(ssid, tsid_fd, SECCLASS_FD, FD__USE, &ad);
+               if (rc)
+-                      goto out;
++                      return rc;
+       }
+ #ifdef CONFIG_BPF_SYSCALL
+-      rc = bpf_fd_pass(file, cred_sid(cred));
++      /* regardless of backing vs user file, use the underlying file here */
++      rc = bpf_fd_pass(file, ssid);
+       if (rc)
+               return rc;
+ #endif
+       /* av is zero if only checking access to the descriptor. */
+-      rc = 0;
+       if (av)
+-              rc = inode_has_perm(cred, inode, av, &ad);
++              return inode_has_perm(cred, inode, av, &ad);
+-out:
+-      return rc;
++      return 0;
++}
++
++/* Check whether a task can use an open file descriptor to
++   access an inode in a given way.  Check access to the
++   descriptor itself, and then use dentry_has_perm to
++   check a particular permission to the file.
++   Access to the descriptor is implicitly granted if it
++   has the same SID as the process.  If av is zero, then
++   access to the file is not checked, e.g. for cases
++   where only the descriptor is affected like seek. */
++static inline int file_has_perm(const struct cred *cred,
++                              const struct file *file, u32 av)
++{
++      return __file_has_perm(cred, file, av, false);
+ }
+ /*
+@@ -3638,6 +3661,17 @@ static int selinux_file_alloc_security(struct file *file)
+       return 0;
+ }
++static int selinux_backing_file_alloc(struct file *backing_file,
++                                    const struct file *user_file)
++{
++      struct backing_file_security_struct *bfsec;
++
++      bfsec = selinux_backing_file(backing_file);
++      bfsec->uf_sid = selinux_file(user_file)->sid;
++
++      return 0;
++}
++
+ /*
+  * Check whether a task has the ioctl permission and cmd
+  * operation to an inode.
+@@ -3755,42 +3789,55 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd,
+ static int default_noexec __ro_after_init;
+-static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
++static int __file_map_prot_check(const struct cred *cred,
++                               const struct file *file, unsigned long prot,
++                               bool shared, bool bf_user_file)
+ {
+-      const struct cred *cred = current_cred();
+-      u32 sid = cred_sid(cred);
+-      int rc = 0;
++      struct inode *inode = NULL;
++      bool prot_exec = prot & PROT_EXEC;
++      bool prot_write = prot & PROT_WRITE;
++
++      if (file) {
++              if (bf_user_file)
++                      inode = d_inode(file->f_path.dentry);
++              else
++                      inode = file_inode(file);
++      }
++
++      if (default_noexec && prot_exec &&
++          (!file || IS_PRIVATE(inode) || (!shared && prot_write))) {
++              int rc;
++              u32 sid = cred_sid(cred);
+-      if (default_noexec &&
+-          (prot & PROT_EXEC) && (!file || IS_PRIVATE(file_inode(file)) ||
+-                                 (!shared && (prot & PROT_WRITE)))) {
+               /*
+-               * We are making executable an anonymous mapping or a
+-               * private file mapping that will also be writable.
+-               * This has an additional check.
++               * We are making executable an anonymous mapping or a private
++               * file mapping that will also be writable.
+                */
+-              rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
+-                                PROCESS__EXECMEM, NULL);
++              rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM,
++                                NULL);
+               if (rc)
+-                      goto error;
++                      return rc;
+       }
+       if (file) {
+-              /* read access is always possible with a mapping */
++              /* "read" always possible, "write" only if shared */
+               u32 av = FILE__READ;
+-
+-              /* write access only matters if the mapping is shared */
+-              if (shared && (prot & PROT_WRITE))
++              if (shared && prot_write)
+                       av |= FILE__WRITE;
+-
+-              if (prot & PROT_EXEC)
++              if (prot_exec)
+                       av |= FILE__EXECUTE;
+-              return file_has_perm(cred, file, av);
++              return __file_has_perm(cred, file, av, bf_user_file);
+       }
+-error:
+-      return rc;
++      return 0;
++}
++
++static inline int file_map_prot_check(const struct cred *cred,
++                                    const struct file *file,
++                                    unsigned long prot, bool shared)
++{
++      return __file_map_prot_check(cred, file, prot, shared, false);
+ }
+ static int selinux_mmap_addr(unsigned long addr)
+@@ -3806,36 +3853,80 @@ static int selinux_mmap_addr(unsigned long addr)
+       return rc;
+ }
+-static int selinux_mmap_file(struct file *file,
+-                           unsigned long reqprot __always_unused,
+-                           unsigned long prot, unsigned long flags)
++static int selinux_mmap_file_common(const struct cred *cred, struct file *file,
++                                  unsigned long prot, bool shared)
+ {
+-      struct common_audit_data ad;
+-      int rc;
+-
+       if (file) {
++              int rc;
++              struct common_audit_data ad;
++
+               ad.type = LSM_AUDIT_DATA_FILE;
+               ad.u.file = file;
+-              rc = inode_has_perm(current_cred(), file_inode(file),
+-                                  FILE__MAP, &ad);
++              rc = inode_has_perm(cred, file_inode(file), FILE__MAP, &ad);
+               if (rc)
+                       return rc;
+       }
+-      return file_map_prot_check(file, prot,
+-                                 (flags & MAP_TYPE) == MAP_SHARED);
++      return file_map_prot_check(cred, file, prot, shared);
++}
++
++static int selinux_mmap_file(struct file *file,
++                           unsigned long reqprot __always_unused,
++                           unsigned long prot, unsigned long flags)
++{
++      return selinux_mmap_file_common(current_cred(), file, prot,
++                                      (flags & MAP_TYPE) == MAP_SHARED);
++}
++
++/**
++ * selinux_mmap_backing_file - Check mmap permissions on a backing file
++ * @vma: memory region
++ * @backing_file: stacked filesystem backing file
++ * @user_file: user visible file
++ *
++ * This is called after selinux_mmap_file() on stacked filesystems, and it
++ * is this function's responsibility to verify access to @backing_file and
++ * setup the SELinux state for possible later use in the mprotect() code path.
++ *
++ * By the time this function is called, mmap() access to @user_file has already
++ * been authorized and @vma->vm_file has been set to point to @backing_file.
++ *
++ * Return zero on success, negative values otherwise.
++ */
++static int selinux_mmap_backing_file(struct vm_area_struct *vma,
++                                   struct file *backing_file,
++                                   struct file *user_file __always_unused)
++{
++      unsigned long prot = 0;
++
++      /* translate vma->vm_flags perms into PROT perms */
++      if (vma->vm_flags & VM_READ)
++              prot |= PROT_READ;
++      if (vma->vm_flags & VM_WRITE)
++              prot |= PROT_WRITE;
++      if (vma->vm_flags & VM_EXEC)
++              prot |= PROT_EXEC;
++
++      return selinux_mmap_file_common(backing_file->f_cred, backing_file,
++                                      prot, vma->vm_flags & VM_SHARED);
+ }
+ static int selinux_file_mprotect(struct vm_area_struct *vma,
+                                unsigned long reqprot __always_unused,
+                                unsigned long prot)
+ {
++      int rc;
+       const struct cred *cred = current_cred();
+       u32 sid = cred_sid(cred);
++      const struct file *file = vma->vm_file;
++      bool backing_file;
++      bool shared = vma->vm_flags & VM_SHARED;
++
++      /* check if we need to trigger the "backing files are awful" mode */
++      backing_file = file && (file->f_mode & FMODE_BACKING);
+       if (default_noexec &&
+           (prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
+-              int rc = 0;
+               /*
+                * We don't use the vma_is_initial_heap() helper as it has
+                * a history of problems and is currently broken on systems
+@@ -3849,11 +3940,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
+                   vma->vm_end <= vma->vm_mm->brk) {
+                       rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
+                                         PROCESS__EXECHEAP, NULL);
+-              } else if (!vma->vm_file && (vma_is_initial_stack(vma) ||
++                      if (rc)
++                              return rc;
++              } else if (!file && (vma_is_initial_stack(vma) ||
+                           vma_is_stack_for_current(vma))) {
+                       rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
+                                         PROCESS__EXECSTACK, NULL);
+-              } else if (vma->vm_file && vma->anon_vma) {
++                      if (rc)
++                              return rc;
++              } else if (file && vma->anon_vma) {
+                       /*
+                        * We are making executable a file mapping that has
+                        * had some COW done. Since pages might have been
+@@ -3861,13 +3956,29 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
+                        * modified content.  This typically should only
+                        * occur for text relocations.
+                        */
+-                      rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD);
++                      rc = __file_has_perm(cred, file, FILE__EXECMOD,
++                                           backing_file);
++                      if (rc)
++                              return rc;
++                      if (backing_file) {
++                              rc = file_has_perm(file->f_cred, file,
++                                                 FILE__EXECMOD);
++                              if (rc)
++                                      return rc;
++                      }
+               }
++      }
++
++      rc = __file_map_prot_check(cred, file, prot, shared, backing_file);
++      if (rc)
++              return rc;
++      if (backing_file) {
++              rc = file_map_prot_check(file->f_cred, file, prot, shared);
+               if (rc)
+                       return rc;
+       }
+-      return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
++      return 0;
+ }
+ static int selinux_file_lock(struct file *file, unsigned int cmd)
+@@ -6870,6 +6981,7 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
+ struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
+       .lbs_cred = sizeof(struct task_security_struct),
+       .lbs_file = sizeof(struct file_security_struct),
++      .lbs_backing_file = sizeof(struct backing_file_security_struct),
+       .lbs_inode = sizeof(struct inode_security_struct),
+       .lbs_ipc = sizeof(struct ipc_security_struct),
+       .lbs_msg_msg = sizeof(struct msg_security_struct),
+@@ -7074,9 +7186,11 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
+       LSM_HOOK_INIT(file_permission, selinux_file_permission),
+       LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
++      LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc),
+       LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
+       LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat),
+       LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
++      LSM_HOOK_INIT(mmap_backing_file, selinux_mmap_backing_file),
+       LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
+       LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect),
+       LSM_HOOK_INIT(file_lock, selinux_file_lock),
+diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
+index 8159fd53c3de2e..541933dd295c07 100644
+--- a/security/selinux/include/objsec.h
++++ b/security/selinux/include/objsec.h
+@@ -60,6 +60,10 @@ struct file_security_struct {
+       u32 pseqno;             /* Policy seqno at the time of file open */
+ };
++struct backing_file_security_struct {
++      u32 uf_sid; /* associated user file fsec->sid */
++};
++
+ struct superblock_security_struct {
+       u32 sid;                        /* SID of file system superblock */
+       u32 def_sid;                    /* default SID for labeling */
+@@ -158,6 +162,13 @@ static inline struct file_security_struct *selinux_file(const struct file *file)
+       return file->f_security + selinux_blob_sizes.lbs_file;
+ }
++static inline struct backing_file_security_struct *
++selinux_backing_file(const struct file *backing_file)
++{
++      void *blob = backing_file_security(backing_file);
++      return blob + selinux_blob_sizes.lbs_backing_file;
++}
++
+ static inline struct inode_security_struct *selinux_inode(
+                                               const struct inode *inode)
+ {
+-- 
+2.53.0
+
index bf52f3ae0aac6f6822cf7de923e395cf1b1131d8..f4821efb0a3e778c4f5f5bb53a0c7cce7f508390 100644 (file)
@@ -85,3 +85,31 @@ kvm-x86-mmu-ensure-hugepage-is-in-by-slot-before-che.patch
 revert-ptp-add-testptp-mask-test.patch
 bluetooth-btmtk-validate-wmt-event-skb-length-before.patch
 bluetooth-btmtk-accept-too-short-wmt-func_ctrl-event.patch
+fs-prepare-for-adding-lsm-blob-to-backing_file.patch
+lsm-add-backing_file-lsm-hooks.patch
+selinux-fix-overlayfs-mmap-and-mprotect-access-check.patch
+batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
+batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
+batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
+batman-adv-tp_meter-avoid-window-underflow.patch
+batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
+batman-adv-tp_meter-fix-fast-recovery-precondition.patch
+batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
+batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
+batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
+batman-adv-prevent-elp-transmission-interval-underfl.patch
+batman-adv-tp_meter-initialize-last_recv_time-during.patch
+batman-adv-ensure-bcast-is-writable-before-modifying.patch
+batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch
+batman-adv-frag-ensure-fragment-is-writable-before-m.patch
+batman-adv-frag-avoid-underflow-of-ttl.patch
+batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
+batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
+batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
+batman-adv-tp_meter-prevent-parallel-modifications-o.patch
+batman-adv-tp_meter-handle-overlapping-packets.patch
+batman-adv-tt-don-t-merge-change-entries-with-differ.patch
+batman-adv-tt-track-roam-count-per-vid.patch
+batman-adv-dat-prevent-false-sharing-between-vlans.patch
+batman-adv-tvlv-enforce-2-byte-alignment.patch
+batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
diff --git a/queue-7.1/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-7.1/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
new file mode 100644 (file)
index 0000000..e11d3e7
--- /dev/null
@@ -0,0 +1,156 @@
+From 96e7f68e827d16e65c8378d4d5ae404b7758082e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:24 +0200
+Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream.
+
+The lasttime field for claim, backbone_gw, and loopdetect tracks the
+jiffies value of the most recent activity and is used to detect timeouts.
+These accesses are not consistently protected by a lock, so
+READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler
+optimizations.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++-------------
+ 1 file changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index ffe854018bd3a5..7f643de42579df 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -512,7 +512,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
+               return NULL;
+       entry->vid = vid;
+-      entry->lasttime = jiffies;
++      WRITE_ONCE(entry->lasttime, jiffies);
+       entry->crc = BATADV_BLA_CRC_INIT;
+       entry->bat_priv = bat_priv;
+       spin_lock_init(&entry->crc_lock);
+@@ -580,7 +580,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv,
+       if (unlikely(!backbone_gw))
+               return;
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       batadv_backbone_gw_put(backbone_gw);
+ }
+@@ -714,7 +714,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+               ether_addr_copy(claim->addr, mac);
+               spin_lock_init(&claim->backbone_lock);
+               claim->vid = vid;
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               kref_get(&backbone_gw->refcount);
+               claim->backbone_gw = backbone_gw;
+               kref_init(&claim->refcount);
+@@ -736,7 +736,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+                       return;
+               }
+       } else {
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               if (claim->backbone_gw == backbone_gw)
+                       /* no need to register a new backbone */
+                       goto claim_free_ref;
+@@ -769,7 +769,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
+       spin_lock_bh(&backbone_gw->crc_lock);
+       backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+ claim_free_ref:
+       batadv_claim_put(claim);
+@@ -858,7 +858,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
+               return true;
+       /* handle as ANNOUNCE frame */
+-      backbone_gw->lasttime = jiffies;
++      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+       crc = ntohs(*((__force __be16 *)(&an_addr[4])));
+       batadv_dbg(BATADV_DBG_BLA, bat_priv,
+@@ -1253,7 +1253,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
+                                                 head, hash_entry) {
+                               if (now)
+                                       goto purge_now;
+-                              if (!batadv_has_timed_out(backbone_gw->lasttime,
++                              if (!batadv_has_timed_out(READ_ONCE(backbone_gw->lasttime),
+                                                         BATADV_BLA_BACKBONE_TIMEOUT))
+                                       continue;
+@@ -1334,7 +1334,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
+                                               primary_if->net_dev->dev_addr))
+                               goto skip;
+-                      if (!batadv_has_timed_out(claim->lasttime,
++                      if (!batadv_has_timed_out(READ_ONCE(claim->lasttime),
+                                                 BATADV_BLA_CLAIM_TIMEOUT))
+                               goto skip;
+@@ -1494,7 +1494,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+               eth_random_addr(bat_priv->bla.loopdetect_addr);
+               bat_priv->bla.loopdetect_addr[0] = 0xba;
+               bat_priv->bla.loopdetect_addr[1] = 0xbe;
+-              bat_priv->bla.loopdetect_lasttime = jiffies;
++              WRITE_ONCE(bat_priv->bla.loopdetect_lasttime, jiffies);
+               atomic_set(&bat_priv->bla.loopdetect_next,
+                          BATADV_BLA_LOOPDETECT_PERIODS);
+@@ -1515,7 +1515,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                                               primary_if->net_dev->dev_addr))
+                               continue;
+-                      backbone_gw->lasttime = jiffies;
++                      WRITE_ONCE(backbone_gw->lasttime, jiffies);
+                       batadv_bla_send_announce(bat_priv, backbone_gw);
+                       if (send_loopdetect)
+@@ -1899,7 +1899,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       /* If the packet came too late, don't forward it on the mesh
+        * but don't consider that as loop. It might be a coincidence.
+        */
+-      if (batadv_has_timed_out(bat_priv->bla.loopdetect_lasttime,
++      if (batadv_has_timed_out(READ_ONCE(bat_priv->bla.loopdetect_lasttime),
+                                BATADV_BLA_LOOPDETECT_TIMEOUT))
+               return true;
+@@ -2014,7 +2014,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       if (own_claim) {
+               /* ... allow it in any case */
+-              claim->lasttime = jiffies;
++              WRITE_ONCE(claim->lasttime, jiffies);
+               goto allow;
+       }
+@@ -2116,7 +2116,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+               /* if yes, the client has roamed and we have
+                * to unclaim it.
+                */
+-              if (batadv_has_timed_out(claim->lasttime, 100)) {
++              if (batadv_has_timed_out(READ_ONCE(claim->lasttime), 100)) {
+                       /* only unclaim if the last claim entry is
+                        * older than 100 ms to make sure we really
+                        * have a roaming client here.
+@@ -2361,7 +2361,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid,
+       backbone_crc = backbone_gw->crc;
+       spin_unlock_bh(&backbone_gw->crc_lock);
+-      msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime);
++      msecs = jiffies_to_msecs(jiffies - READ_ONCE(backbone_gw->lasttime));
+       if (is_own)
+               if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) {
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-7.1/batman-adv-dat-prevent-false-sharing-between-vlans.patch
new file mode 100644 (file)
index 0000000..67ecae8
--- /dev/null
@@ -0,0 +1,57 @@
+From d31ab8c648c9cf65c6f0b843643f6ddfab6eafdf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:39 +0200
+Subject: batman-adv: dat: prevent false sharing between VLANs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream.
+
+The local hash of DAT entries is supposed to be VLAN (VID) aware. But
+the adding to the hash and the search in the hash were not checking the VID
+information of the hash entries. The entries would therefore only be
+correctly separated when batadv_hash_dat() didn't select the same buckets
+for different VIDs.
+
+Cc: stable@kernel.org
+Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/distributed-arp-table.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
+index 0a8bd95e2f99e8..86fb5de5022aca 100644
+--- a/net/batman-adv/distributed-arp-table.c
++++ b/net/batman-adv/distributed-arp-table.c
+@@ -214,10 +214,13 @@ static void batadv_dat_purge(struct work_struct *work)
+  */
+ static bool batadv_compare_dat(const struct hlist_node *node, const void *data2)
+ {
+-      const void *data1 = container_of(node, struct batadv_dat_entry,
+-                                       hash_entry);
++      const struct batadv_dat_entry *entry1;
++      const struct batadv_dat_entry *entry2;
+-      return memcmp(data1, data2, sizeof(__be32)) == 0;
++      entry1 = container_of(node, struct batadv_dat_entry, hash_entry);
++      entry2 = data2;
++
++      return entry1->ip == entry2->ip && entry1->vid == entry2->vid;
+ }
+ /**
+@@ -344,6 +347,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
+               if (dat_entry->ip != ip)
+                       continue;
++              if (dat_entry->vid != vid)
++                      continue;
++
+               if (!kref_get_unless_zero(&dat_entry->refcount))
+                       continue;
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-ensure-bcast-is-writable-before-modifying.patch b/queue-7.1/batman-adv-ensure-bcast-is-writable-before-modifying.patch
new file mode 100644 (file)
index 0000000..a2392bd
--- /dev/null
@@ -0,0 +1,50 @@
+From dc8162d9a2fb72a3646b361fd3d9fe049049171b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:28 +0200
+Subject: batman-adv: ensure bcast is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or used skb_cow() to ensure that the data part is not
+shared.
+
+The old implementation used a shared queue and created copies before
+attempting to write to it. But with the new implementation, the broadcast
+packet is already modified when it gets received. Potentially writing to
+shared buffers in this process.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to rebroadcast the
+packet.
+
+Cc: stable@kernel.org
+Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/routing.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 12c16f81cc51df..0672dc30bed3b1 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1191,6 +1191,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
+       if (batadv_is_my_mac(bat_priv, bcast_packet->orig))
+               goto free_skb;
++      /* create a copy of the skb, if needed, to modify it. */
++      if (skb_cow(skb, ETH_HLEN) < 0)
++              goto free_skb;
++
++      bcast_packet = (struct batadv_bcast_packet *)skb->data;
++
+       if (bcast_packet->ttl-- < 2)
+               goto free_skb;
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch b/queue-7.1/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch
new file mode 100644 (file)
index 0000000..f4c287b
--- /dev/null
@@ -0,0 +1,127 @@
+From 271603aa8370798e690548246db1a265c5245906 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:29 +0200
+Subject: batman-adv: fix (m|b)cast csum after decrementing TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream.
+
+The broadcast and multicast packets can be received at the same time by the
+local system and forwarded to other nodes. Both are simply decrementing the
+TTL at the beginning of the receive path - independent of chosen paths
+(receive/forward). But such a modification of the data conflicts with the
+hw csum. This is not a problem when the packet is directly forwarded but
+can cause errors in the local receive path.
+
+Such a problem can then trigger a "hw csum failure". The receiver path must
+therefore ensure that the csum is fixed for each modification of the
+payload before batadv_interface_rx() is reached.
+
+Since all batman-adv packet types with a ttl have it as u8 at offset 2, a
+helper can be used for all of them. But it is only used at the moment for
+batadv_bcast_packet and batadv_mcast_packet because they are the only ones
+which deliver the packet locally but unconditionally modify the TTL.
+
+Cc: stable@kernel.org
+Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed")
+Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/routing.c | 58 ++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 56 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 0672dc30bed3b1..cdcea90db61235 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -8,6 +8,7 @@
+ #include "main.h"
+ #include <linux/atomic.h>
++#include <linux/build_bug.h>
+ #include <linux/byteorder/generic.h>
+ #include <linux/compiler.h>
+ #include <linux/errno.h>
+@@ -204,6 +205,59 @@ bool batadv_check_management_packet(struct sk_buff *skb,
+       return true;
+ }
++/**
++ * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe
++ * @skb: the received packet with @skb->data pointing to the batman-adv header
++ *
++ * Supports the following packet types, all of which carry the TTL at offset 2:
++ *
++ * - batadv_ogm_packet
++ * - batadv_ogm2_packet
++ * - batadv_icmp_header
++ * - batadv_icmp_packet
++ * - batadv_icmp_tp_packet
++ * - batadv_icmp_packet_rr
++ * - batadv_unicast_packet
++ * - batadv_frag_packet
++ * - batadv_bcast_packet
++ * - batadv_mcast_packet
++ * - batadv_coded_packet
++ * - batadv_unicast_tvlv_packet
++ *
++ * Return: true if the packet may be forwarded (ttl decremented),
++ *  false if it must be dropped (ttl would expire)
++ */
++static bool batadv_skb_decrement_ttl(struct sk_buff *skb)
++{
++      static const size_t ttl_offset = 2;
++      u8 *ttl_pos;
++
++      BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_mcast_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset);
++      BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset);
++
++      ttl_pos = skb->data + ttl_offset;
++
++      /* would expire on this hop -> drop, leave header + csum untouched */
++      if (*ttl_pos < 2)
++              return false;
++
++      skb_postpull_rcsum(skb, ttl_pos, 1);
++      (*ttl_pos)--;
++      skb_postpush_rcsum(skb, ttl_pos, 1);
++
++      return true;
++}
++
+ /**
+  * batadv_recv_my_icmp_packet() - receive an icmp packet locally
+  * @bat_priv: the bat priv with all the mesh interface information
+@@ -1197,7 +1251,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
+       bcast_packet = (struct batadv_bcast_packet *)skb->data;
+-      if (bcast_packet->ttl-- < 2)
++      if (!batadv_skb_decrement_ttl(skb))
+               goto free_skb;
+       orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig);
+@@ -1304,7 +1358,7 @@ int batadv_recv_mcast_packet(struct sk_buff *skb,
+               goto free_skb;
+       mcast_packet = (struct batadv_mcast_packet *)skb->data;
+-      if (mcast_packet->ttl-- < 2)
++      if (!batadv_skb_decrement_ttl(skb))
+               goto free_skb;
+       tvlv_buff = (unsigned char *)(skb->data + hdr_size);
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-7.1/batman-adv-frag-avoid-underflow-of-ttl.patch
new file mode 100644 (file)
index 0000000..e0cc4c1
--- /dev/null
@@ -0,0 +1,45 @@
+From e821ae76c6a01d79effefa9559c830143de0539c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:31 +0200
+Subject: batman-adv: frag: avoid underflow of TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream.
+
+Packets with a TTL are using it to limit the amount of time this packet can
+be forwarded. But for batadv_frag_packet, the TTL was always only reduced
+but it was never evaluated. It could even underflow without any effect.
+
+Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for
+forwarding. This keeps it in sync with the not fragmented unicast packet.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index 5c2a7629e4e09d..9a5927ecc4746f 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -415,6 +415,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (packet->ttl < 2) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
+               if (skb_cow(skb, ETH_HLEN) < 0) {
+                       kfree_skb(skb);
+                       *rx_result = NET_RX_DROP;
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-7.1/batman-adv-frag-ensure-fragment-is-writable-before-m.patch
new file mode 100644 (file)
index 0000000..e67cbb8
--- /dev/null
@@ -0,0 +1,106 @@
+From 88dbf2a8c2ce086ac680e2d454702c79fb3a8b09 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:30 +0200
+Subject: batman-adv: frag: ensure fragment is writable before modifying TTL
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream.
+
+Before batman-adv is allowed to write to an skb, it either has to have its
+own copy of the skb or use skb_cow() to ensure that the data part is not
+shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared.
+
+Adding a skb_cow() right before this operation avoids this and can at the
+same time prepare it for the modifications required to forward the
+fragment.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/fragmentation.c | 15 ++++++++++++++-
+ net/batman-adv/fragmentation.h |  3 ++-
+ net/batman-adv/routing.c       |  3 +--
+ 3 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
+index e9553db423491a..5c2a7629e4e09d 100644
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -384,6 +384,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  * @skb: skb to forward
+  * @recv_if: interface that the skb is received on
+  * @orig_node_src: originator that the skb is received from
++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and
++ *  NET_RX_DROP when it was dropped; only valid when true is returned
+  *
+  * Look up the next-hop of the fragments payload and check if the merged packet
+  * will exceed the MTU towards the next-hop. If so, the fragment is forwarded
+@@ -393,7 +395,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+  */
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src)
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(recv_if->mesh_iface);
+       struct batadv_neigh_node *neigh_node = NULL;
+@@ -412,12 +415,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
++              if (skb_cow(skb, ETH_HLEN) < 0) {
++                      kfree_skb(skb);
++                      *rx_result = NET_RX_DROP;
++                      ret = true;
++                      goto out;
++              }
++
++              packet = (struct batadv_frag_packet *)skb->data;
++
+               batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
+               batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
+                                  skb->len + ETH_HLEN);
+               packet->ttl--;
+               batadv_send_unicast_skb(skb, neigh_node);
++              *rx_result = NET_RX_SUCCESS;
+               ret = true;
+       }
+diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h
+index dbf0871f870303..51e281027ab630 100644
+--- a/net/batman-adv/fragmentation.h
++++ b/net/batman-adv/fragmentation.h
+@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig,
+                           bool (*check_cb)(struct batadv_frag_table_entry *));
+ bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+-                       struct batadv_orig_node *orig_node_src);
++                       struct batadv_orig_node *orig_node_src,
++                       int *rx_result);
+ bool batadv_frag_skb_buffer(struct sk_buff **skb,
+                           struct batadv_orig_node *orig_node);
+ int batadv_frag_send_packet(struct sk_buff *skb,
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index cdcea90db61235..4483f8d9c75831 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1168,10 +1168,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb,
+       /* Route the fragment if it is not for us and too big to be merged. */
+       if (!batadv_is_my_mac(bat_priv, frag_packet->dest) &&
+-          batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) {
++          batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) {
+               /* skb was consumed */
+               skb = NULL;
+-              ret = NET_RX_SUCCESS;
+               goto put_orig_node;
+       }
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-gw-don-t-deselect-gateway-with-active-har.patch b/queue-7.1/batman-adv-gw-don-t-deselect-gateway-with-active-har.patch
new file mode 100644 (file)
index 0000000..9967e56
--- /dev/null
@@ -0,0 +1,84 @@
+From 194cbd473d76a79408f0c1b4dd250a56bc9cf038 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:27 +0200
+Subject: batman-adv: gw: don't deselect gateway with active hardif
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit df97a7107b16375a10a36d7a63e9b4291a8ac680 upstream.
+
+The batadv_hardif_cnt() was previously checking if there is an
+batadv_hard_iface->mesh_iface which is has the same mesh_iface. And since
+batadv_hardif_disable_interface() was resetting the
+batadv_hard_iface->mesh_iface after this check, it had to verify whether
+*1* interface was still part of the mesh_iface before it started the
+gateway deselection.
+
+But after batadv_hardif_cnt() is now checking the lower interfaces of
+mesh_iface and batadv_hardif_disable_interface() already removed the
+interface via netdev_upper_dev_unlink() earlier in this function, the check
+must now make sure that *0* interfaces can be found by batadv_hardif_cnt()
+before selected gateway must be deselected. Otherwise the deselection would
+already happen one batadv_hard_iface too early.
+
+Because a 0 hardif count from batadv_hardif_cnt() is equal to an empty
+list, it is possible to replace the counting with a simple list_empty().
+
+Cc: stable@kernel.org
+Fixes: 7dc284702bcd ("batman-adv: store hard_iface as iflink private data")
+Reviewed-by: Nora Schiffer <neocturne@universe-factory.net>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/hard-interface.c | 28 ++--------------------------
+ 1 file changed, 2 insertions(+), 26 deletions(-)
+
+diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
+index d6732c34aeafc8..c7dbd24ea19985 100644
+--- a/net/batman-adv/hard-interface.c
++++ b/net/batman-adv/hard-interface.c
+@@ -786,30 +786,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
+       return ret;
+ }
+-/**
+- * batadv_hardif_cnt() - get number of interfaces enslaved to mesh interface
+- * @mesh_iface: mesh interface to check
+- *
+- * This function is only using RCU for locking - the result can therefore be
+- * off when another function is modifying the list at the same time. The
+- * caller can use the rtnl_lock to make sure that the count is accurate.
+- *
+- * Return: number of connected/enslaved hard interfaces
+- */
+-static size_t batadv_hardif_cnt(struct net_device *mesh_iface)
+-{
+-      struct batadv_hard_iface *hard_iface;
+-      struct list_head *iter;
+-      size_t count = 0;
+-
+-      rcu_read_lock();
+-      netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter)
+-              count++;
+-      rcu_read_unlock();
+-
+-      return count;
+-}
+-
+ /**
+  * batadv_hardif_disable_interface() - Remove hard interface from mesh interface
+  * @hard_iface: hard interface to be removed
+@@ -850,8 +826,8 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
+       netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface);
+       batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface);
+-      /* nobody uses this interface anymore */
+-      if (batadv_hardif_cnt(hard_iface->mesh_iface) <= 1)
++      /* nobody uses this mesh interface anymore */
++      if (list_empty(&hard_iface->mesh_iface->adj_list.lower))
+               batadv_gw_check_client_stop(bat_priv);
+       hard_iface->mesh_iface = NULL;
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-7.1/batman-adv-prevent-elp-transmission-interval-underfl.patch
new file mode 100644 (file)
index 0000000..5d31813
--- /dev/null
@@ -0,0 +1,52 @@
+From e6dfffb8565c4514bb23a60d4e965684785bd1e7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:25 +0200
+Subject: batman-adv: prevent ELP transmission interval underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream.
+
+batadv_v_elp_start_timer() enqeues a delayed work. The time when it starts
+is randomly chosen between (elp_interval - BATADV_JITTER) and
+(elp_interval + BATADV_JITTER). The configured elp_interval must therefore
+be larger or equal to BATADV_JITTER to avoid that it causes an underflow of
+the unsigned integer. If this would happen, then a "fast" ELP interval
+would turn into a "day long" delay.
+
+At the same time, it must not be larger than the maximum value the variable
+can store.
+
+Cc: stable@kernel.org
+Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/netlink.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
+index 78c651f634cd39..1d144d8cc0928e 100644
+--- a/net/batman-adv/netlink.c
++++ b/net/batman-adv/netlink.c
+@@ -917,9 +917,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb,
+ #ifdef CONFIG_BATMAN_ADV_BATMAN_V
+       if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) {
++              u32 elp_interval;
++
+               attr = info->attrs[BATADV_ATTR_ELP_INTERVAL];
++              elp_interval = nla_get_u32(attr);
++
++              elp_interval = min_t(u32, elp_interval, INT_MAX);
++              elp_interval = max_t(u32, elp_interval, BATADV_JITTER);
+-              atomic_set(&hard_iface->bat_v.elp_interval, nla_get_u32(attr));
++              atomic_set(&hard_iface->bat_v.elp_interval, elp_interval);
+       }
+       if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) {
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-7.1/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
new file mode 100644 (file)
index 0000000..0862472
--- /dev/null
@@ -0,0 +1,74 @@
+From ad368ca6b053b93eb4848ac734e0593d5d3c7a97 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:23 +0200
+Subject: batman-adv: tp_meter: add only finished tp_vars to lists
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream.
+
+When the receiver variables (aka "session") are initialized, then they are
+added to the list of sessions before the timer is set up. A RCU protected
+reader could therefore find the entry and run mod_setup before
+batadv_tp_init_recv() finished the timer initialization.
+
+The same is true for batadv_tp_start(), which must first initialize the
+finish_work and the test_length to avoid a similar problem.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 0801dcf93540ac..7bfad65c862e9a 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1096,21 +1096,21 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->prerandom_offset = 0;
+       spin_lock_init(&tp_vars->prerandom_lock);
+-      kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+-      spin_unlock_bh(&bat_priv->tp_list_lock);
+-
+       tp_vars->test_length = test_length;
+       if (!tp_vars->test_length)
+               tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH;
++      /* init work item for finished tp tests */
++      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
++
++      kref_get(&tp_vars->refcount);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      spin_unlock_bh(&bat_priv->tp_list_lock);
++
+       batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                  "Meter: starting throughput meter towards %pM (length=%ums)\n",
+                  dst, test_length);
+-      /* init work item for finished tp tests */
+-      INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
+-
+       /* start tp kthread. This way the write() call issued from userspace can
+        * happily return and avoid to block
+        */
+@@ -1430,10 +1430,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
+       kref_get(&tp_vars->refcount);
+-      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
++      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+       kref_get(&tp_vars->refcount);
+-      timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+       batadv_tp_reset_receiver_timer(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-7.1/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
new file mode 100644 (file)
index 0000000..eda6e69
--- /dev/null
@@ -0,0 +1,66 @@
+From b10da62fe1bf22c0d4f160d236d23da6f962a8cd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:34 +0200
+Subject: batman-adv: tp_meter: annotate last_recv_time access with
+ READ/WRITE_ONCE
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream.
+
+The last_recv_time field for batadv_tp_receiver tracks the jiffies value of
+the most recent activity and is used to detect timeouts. These accesses are
+not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used
+to prevent data races caused by compiler optimizations.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 4f8af909893ab9..e23b75846ebe1a 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       bat_priv = tp_vars->bat_priv;
+       /* if there is recent activity rearm the timer */
+-      if (!batadv_has_timed_out(tp_vars->last_recv_time,
++      if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time),
+                                 BATADV_TP_RECV_TIMEOUT)) {
+               /* reset the receiver shutdown timer */
+               batadv_tp_reset_receiver_timer(tp_vars);
+@@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+       if (tp_vars) {
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+               goto out_unlock;
+       }
+@@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+-      tp_vars->last_recv_time = jiffies;
++      WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                       goto out;
+               }
+-              tp_vars->last_recv_time = jiffies;
++              WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-7.1/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
new file mode 100644 (file)
index 0000000..97373aa
--- /dev/null
@@ -0,0 +1,64 @@
+From 50472f5e86ad53e0eb270d2686fb05c3bfe4fd12 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:20 +0200
+Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream.
+
+The cwnd is always MSS <= cwnd <= 0x20000000. But the calculation in
+batadv_tp_update_cwnd() assumes unsigned 32 bit arithmetics.
+
+    ((mss * 8) ** 2) / (cwnd * 8)
+
+In case cwnd is actually 0x20000000, it will be shifted by 3 bit to the
+left end up at 0x100000000 or U32_MAX + 1. It will therefore wrap around
+and be 0 - resulting in:
+
+    ((mss * 8) ** 2) / 0
+
+This is of course invalid and cannot be calculated. The calculation should
+must be simplified to avoid this overflow:
+
+   (mss ** 2) * 8 / cwnd
+
+It will keep the precision enhancement from the scaling (by 8) but avoid
+the overflow in the divisor.
+
+In theory, there could still be an overflow in the dividend. It is at the
+moment fixed to BATADV_TP_PLEN in batadv_tp_recv_ack() - so it is not an
+imminent problem. But allowing it to use the whole u32 bit range, would
+mean that it can still use up to 67 bits. To keep this calculation safe for
+32 bit arithmetic, mss must never use more than floor((32 - 3) / 2) bits -
+or in other words: must never be larger than 16383.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index d3b8f02ca08b14..e4f76c141af3e2 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -154,9 +154,12 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss)
+               return;
+       }
++      /* prevent overflow in (mss * mss) << 3 */
++      mss = min_t(u32, mss, (1U << 14) - 1);
++
+       /* increment CWND at least of 1 (section 3.1 of RFC5681) */
+       tp_vars->dec_cwnd += max_t(u32, 1U << 3,
+-                                 ((mss * mss) << 6) / (tp_vars->cwnd << 3));
++                                 ((mss * mss) << 3) / tp_vars->cwnd);
+       if (tp_vars->dec_cwnd < (mss << 3)) {
+               spin_unlock_bh(&tp_vars->cwnd_lock);
+               return;
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-7.1/batman-adv-tp_meter-avoid-window-underflow.patch
new file mode 100644 (file)
index 0000000..dc9d68e
--- /dev/null
@@ -0,0 +1,56 @@
+From 1e14937db4cc5ff86a2c73915c4afa6354b9e044 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:19 +0200
+Subject: batman-adv: tp_meter: avoid window underflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream.
+
+In batadv_tp_avail(), win_left is calculated with 32-bit unsigned
+arithmetic: win_left = win_limit - tp_vars->last_sent;
+
+During Fast Recovery, cwnd is inflated and last_sent advances rapidly. When
+Fast Recovery ends, cwnd drops abruptly back to ss_threshold. If the newly
+shrunk win_limit is less than last_sent, the unsigned subtraction will
+underflow, wrapping to a massive positive value. Instead of returning that
+the window is full (unavailable), it returns that the sender can continue
+sending.
+
+To handle this situation, it must be checked whether the windows end
+sequence number (win_limit) has to be compared with the last sent sequence
+number. If it would be before the last sent sequence number, then more acks
+are needed before the transmission can be started again.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 2ff7aa5ed19fb3..d3b8f02ca08b14 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+ static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars,
+                           size_t payload_len)
+ {
++      u32 last_sent = READ_ONCE(tp_vars->last_sent);
+       u32 win_left, win_limit;
+       win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd;
+-      win_left = win_limit - tp_vars->last_sent;
++
++      if (batadv_seq_before(last_sent, win_limit))
++              win_left = win_limit - last_sent;
++      else
++              win_left = 0;
+       return win_left >= payload_len;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-7.1/batman-adv-tp_meter-fix-fast-recovery-precondition.patch
new file mode 100644 (file)
index 0000000..96d4958
--- /dev/null
@@ -0,0 +1,48 @@
+From 0c428129fc44a0745c0dda017f0504b2c9938d95 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:21 +0200
+Subject: batman-adv: tp_meter: fix fast recovery precondition
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream.
+
+The fast recovery precondition checks if the recover (initialized to
+BATADV_TP_FIRST_SEQ) is bigger than the received ack. But since recover is
+only updated when this check is successful, it will never enter the fast
+recovery mode.
+
+According to RFC6582 Section 3.2 step 2, the check should actually be
+different:
+
+> When the third duplicate ACK is received, the TCP sender first
+> checks the value of recover to see if the Cumulative
+> Acknowledgment field covers more than recover
+
+The precondition must therefore check if recover is smaller than the
+received ack - basically swapping the operands of the current check.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index e4f76c141af3e2..77bc69573a5624 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (recv_ack >= tp_vars->recover)
++              if (tp_vars->recover >= recv_ack)
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-7.1/batman-adv-tp_meter-handle-overlapping-packets.patch
new file mode 100644 (file)
index 0000000..9f63e5f
--- /dev/null
@@ -0,0 +1,109 @@
+From 889335ce69ea5dbae87aa12923b8dd7e51e60ab1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:36 +0200
+Subject: batman-adv: tp_meter: handle overlapping packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit cbde75c38b21f022891525078622587ad557b7c1 upstream.
+
+If the size of the packets would change during the transmission, it could
+happen that some retries of packets are overlapping. In this case, precise
+comparisons of sequence numbers by the receiver would be wrong. It is then
+necessary to check if the start sequence number to the end sequence number
+("seqno + length") would contain a new range.
+
+If this is the case then this is enough to accept this packet. In all other
+cases, the packet still has to be dropped (and not acked).
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 25 +++++++++++--------------
+ 1 file changed, 11 insertions(+), 14 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 6a23050f03d89e..dbaae33db0f1f8 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+ /**
+  * batadv_tp_handle_out_of_order() - store an out of order packet
+  * @tp_vars: the private data of the current TP meter session
+- * @skb: the buffer containing the received packet
++ * @seqno: sequence number of new received packet
++ * @payload_len: length of the received packet
+  *
+  * Store the out of order packet in the unacked list for late processing. This
+  * packets are kept in this list so that they can be ACKed at once as soon as
+@@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  * Return: true if the packed has been successfully processed, false otherwise
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+-                                        const struct sk_buff *skb)
++                                        u32 seqno, u32 payload_len)
+       __must_hold(&tp_vars->unacked_lock)
+ {
+-      const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+-      u32 payload_len;
+       bool added = false;
+       new = kmalloc_obj(*new, GFP_ATOMIC);
+       if (unlikely(!new))
+               return false;
+-      icmp = (struct batadv_icmp_tp_packet *)skb->data;
+-
+-      new->seqno = ntohl(icmp->seqno);
+-      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      new->seqno = seqno;
+       new->len = payload_len;
+       /* if the list is empty immediately attach this new object */
+@@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+-      size_t packet_size;
++      u32 payload_len;
+       u32 to_ack;
+       u32 seqno;
+@@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+-      if (batadv_seq_before(seqno, tp_vars->last_recv))
++      payload_len = skb->len - sizeof(struct batadv_unicast_packet);
++      to_ack = seqno + payload_len;
++      if (batadv_seq_before(to_ack, tp_vars->last_recv))
+               goto send_ack;
+       /* if the packet is out of order enqueue it */
+-      if (ntohl(icmp->seqno) != tp_vars->last_recv) {
++      if (batadv_seq_before(tp_vars->last_recv, seqno)) {
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++              if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
+                       spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
+               }
+@@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       }
+       /* if everything was fine count the ACKed bytes */
+-      packet_size = skb->len - sizeof(struct batadv_unicast_packet);
+-      tp_vars->last_recv += packet_size;
++      tp_vars->last_recv = to_ack;
+       /* check if this ordered message filled a gap.... */
+       batadv_tp_ack_unordered(tp_vars);
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-7.1/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
new file mode 100644 (file)
index 0000000..09f1de3
--- /dev/null
@@ -0,0 +1,40 @@
+From ba12ba73ab6ec278d8d10821c5609298ea477e4b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:22 +0200
+Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery
+ detection
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream.
+
+The recover variable and the last_sent sequence number are initialized on
+purpose as a really high value which will wrap-around after the first 2000
+bytes. The fast recovery precondition must therefore not use simple integer
+comparisons but use helpers which are aware of the sequence number
+wrap-arounds.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 77bc69573a5624..0801dcf93540ac 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+               if (atomic_read(&tp_vars->dup_acks) != 3)
+                       goto out;
+-              if (tp_vars->recover >= recv_ack)
++              if (!batadv_seq_before(tp_vars->recover, recv_ack))
+                       goto out;
+               /* if this is the third duplicate ACK do Fast Retransmit */
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-7.1/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
new file mode 100644 (file)
index 0000000..b9e8a60
--- /dev/null
@@ -0,0 +1,40 @@
+From d5ff1d0f45192d289c6b78bc7aa2334b7377b8fb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:18 +0200
+Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit febfb1b86224489535312296ecfa3d4bf467f339 upstream.
+
+When batadv_tp_update_cwnd() is called, dec_cwnd is increased. But dec_cwnd
+is only initialixed (to 0) when a duplicate Ack was received or when cwnd
+is below the ss_threshold.
+
+Just initialize the cwnd during the initialization to avoid any potential
+access of uninitialized data.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 0325b951ff8a8e..2ff7aa5ed19fb3 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1055,6 +1055,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+        * mesh_interface, hence its MTU
+        */
+       tp_vars->cwnd = BATADV_TP_PLEN * 3;
++      tp_vars->dec_cwnd = 0;
++
+       /* at the beginning initialise the SS threshold to the biggest possible
+        * window size, hence the AWND size
+        */
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-7.1/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
new file mode 100644 (file)
index 0000000..b544cfa
--- /dev/null
@@ -0,0 +1,42 @@
+From c1a29c5c5fefc0bf5899f771c793854209adc01f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:17 +0200
+Subject: batman-adv: tp_meter: initialize dup_acks explicitly
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b2b68b32a715e0328662801576974aa37b942b00 upstream.
+
+When an ack with a sequence number equal to the last_acked is received, the
+dup_acks counter is increased to decide whether fast retransmit should be
+performed. Only when the sequence numbers are not equal, the dup_acks is
+set to the initial value (0).
+
+But if the initial packet would have the sequence number
+BATADV_TP_FIRST_SEQ, dup_acks would not be initialized and atomic_inc would
+operate on an undefined starting value. It is therefore required to have it
+explicitly initialized during the start of the sender session.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index e8941f753a9697..0325b951ff8a8e 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1045,6 +1045,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       tp_vars->icmp_uid = icmp_uid;
+       tp_vars->last_sent = BATADV_TP_FIRST_SEQ;
++      atomic_set(&tp_vars->dup_acks, 0);
+       atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ);
+       tp_vars->fast_recovery = false;
+       tp_vars->recover = BATADV_TP_FIRST_SEQ;
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-7.1/batman-adv-tp_meter-initialize-last_recv_time-during.patch
new file mode 100644 (file)
index 0000000..42a630c
--- /dev/null
@@ -0,0 +1,68 @@
+From f4e0258629da50d0465ea4a44d140cf08fcb7c3e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:26 +0200
+Subject: batman-adv: tp_meter: initialize last_recv_time during init
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream.
+
+The last_recv_time is the most important indicator for a receiver session
+to figure out whether a session timed out or not. But this information was
+only initialized after the session was added to the tp_receiver_list and
+after the timer was started.
+
+In the worst case, the timer (function) could have tried to access this
+information before the actual initialization was reached. Like rest of the
+variables of the tp_meter receiver session, this field has to be filled out
+before any other (parallel running) context has the chance to access it.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 7bfad65c862e9a..397b9456353568 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1403,8 +1403,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+                                             icmp->session, BATADV_TP_RECEIVER);
+-      if (tp_vars)
++      if (tp_vars) {
++              tp_vars->last_recv_time = jiffies;
+               goto out_unlock;
++      }
+       if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) {
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+@@ -1432,6 +1434,8 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
++      tp_vars->last_recv_time = jiffies;
++
+       kref_get(&tp_vars->refcount);
+       hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+@@ -1480,9 +1484,9 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+                                  icmp->orig);
+                       goto out;
+               }
+-      }
+-      tp_vars->last_recv_time = jiffies;
++              tp_vars->last_recv_time = jiffies;
++      }
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-7.1/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
new file mode 100644 (file)
index 0000000..de2dc03
--- /dev/null
@@ -0,0 +1,44 @@
+From 32e3c4739f435b27983632f24739b66c0f08a86f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:16 +0200
+Subject: batman-adv: tp_meter: keep unacked list in ascending ordered
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream.
+
+When batadv_tp_handle_out_of_order inserts a new entry in the list of
+unacked (out of order) packets, it searches from the entry with the newest
+sequence number towards oldest sequence number. If an entry is found which
+is older than the newly entry, the new entry has to be added after the
+found one to keep the ascending order.
+
+But for this operation list_add_tail() was used. But this function adds an
+entry _before_ another one. As result, the list would contain a lot of
+swapped sequence numbers. The consumer of this list
+(batadv_tp_ack_unordered()) would then fail to correctly ack packets.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 0fc4ca78e84ebe..e8941f753a9697 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1325,7 +1325,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                * one is attached _after_ it. In this way the list is kept in
+                * ascending order
+                */
+-              list_add_tail(&new->list, &un->list);
++              list_add(&new->list, &un->list);
+               added = true;
+               break;
+       }
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-7.1/batman-adv-tp_meter-prevent-parallel-modifications-o.patch
new file mode 100644 (file)
index 0000000..5a43284
--- /dev/null
@@ -0,0 +1,157 @@
+From 3f0174568d3a659bba18a1577dcbe5395e5f4273 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:35 +0200
+Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream.
+
+When last_recv is updated to store the last receive sequence number, it is
+assuming that nothing is modifying in parallel while:
+
+* check for outdated packets is done
+* out of order check is performed (and packets are stored in out-of-order
+  queue)
+* the out-of-order queue was searched for closed gaps
+* sequence number for next ack is calculated
+
+Nothing of that was actually protected. It could therefore happen that the
+last_recv was updated multiple times in parallel and the final sequence
+number was calculated with deltas which had no connection to the sequence
+number they were added to.
+
+Lock this whole region with the same lock which was already used to protect
+the unacked (out-of-order) list.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 22 +++++++++++++---------
+ net/batman-adv/types.h    |  2 +-
+ 2 files changed, 14 insertions(+), 10 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index e23b75846ebe1a..6a23050f03d89e 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+  */
+ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                                         const struct sk_buff *skb)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_unacked *un, *new;
+@@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       payload_len = skb->len - sizeof(struct batadv_unicast_packet);
+       new->len = payload_len;
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
+               tp_vars->unacked_count++;
+-              goto out;
++              return true;
+       }
+       /* otherwise loop over the list and either drop the packet because this
+@@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+               tp_vars->unacked_count--;
+       }
+-out:
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+-
+       return true;
+ }
+@@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+  * @tp_vars: the private data of the current TP meter session
+  */
+ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
++      __must_hold(&tp_vars->unacked_lock)
+ {
+       struct batadv_tp_unacked *un, *safe;
+       u32 to_ack;
+@@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+       /* go through the unacked packet list and possibly ACK them as
+        * well
+        */
+-      spin_lock_bh(&tp_vars->unacked_lock);
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               /* the list is ordered, therefore it is possible to stop as soon
+                * there is a gap between the last acked seqno and the seqno of
+@@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               kfree(un);
+               tp_vars->unacked_count--;
+       }
+-      spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+ /**
+@@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       const struct batadv_icmp_tp_packet *icmp;
+       struct batadv_tp_vars *tp_vars;
+       size_t packet_size;
++      u32 to_ack;
+       u32 seqno;
+       icmp = (struct batadv_icmp_tp_packet *)skb->data;
+@@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               WRITE_ONCE(tp_vars->last_recv_time, jiffies);
+       }
++      spin_lock_bh(&tp_vars->unacked_lock);
++
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+        * lost. Resend the ACK
+        */
+@@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               /* exit immediately (and do not send any ACK) if the packet has
+                * not been enqueued correctly
+                */
+-              if (!batadv_tp_handle_out_of_order(tp_vars, skb))
++              if (!batadv_tp_handle_out_of_order(tp_vars, skb)) {
++                      spin_unlock_bh(&tp_vars->unacked_lock);
+                       goto out;
++              }
+               /* send a duplicate ACK */
+               goto send_ack;
+@@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+       batadv_tp_ack_unordered(tp_vars);
+ send_ack:
++      to_ack = tp_vars->last_recv;
++      spin_unlock_bh(&tp_vars->unacked_lock);
++
+       /* send the ACK. If the received packet was out of order, the ACK that
+        * is going to be sent is a duplicate (the sender will count them and
+        * possibly enter Fast Retransmit as soon as it has reached 3)
+        */
+-      batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv,
++      batadv_tp_send_ack(bat_priv, icmp->orig, to_ack,
+                          icmp->timestamp, icmp->session, icmp->uid);
+ out:
+       batadv_tp_vars_put(tp_vars);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 4f9f1820e3a92a..3f940a201cdf84 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1423,7 +1423,7 @@ struct batadv_tp_vars {
+       /** @unacked_list: list of unacked packets (meta-info only) */
+       struct list_head unacked_list;
+-      /** @unacked_lock: protect unacked_list */
++      /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
+       spinlock_t unacked_lock;
+       /** @unacked_count: number of unacked entries */
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-7.1/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
new file mode 100644 (file)
index 0000000..edeba31
--- /dev/null
@@ -0,0 +1,124 @@
+From 523e22bf49a45ae523732a740dcd032b7c5ca624 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:33 +0200
+Subject: batman-adv: tp_meter: restrict number of unacked list entries
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream.
+
+When the unacked_list is unbound, an attacker could send messages with
+small lengths and appropriated seqno + gaps to force the receiver to
+allocate more and more unacked_list entries. And the end either causing an
+out-of-memory situation or increase the management overhead for the (large)
+list that significant portions of CPU cycles are wasted in searching
+through the list.
+
+When limiting the list to a specific number, it is important to still
+correctly add a new entry to the list. But if the list became larger than
+the limit, the last entry of the list (with the highest seqno) must be
+dropped to still allow the earlier seqnos to finish and therefore to
+continue the process. Otherwise, the process might get stuck with too high
+seqnos which are not handled by batadv_tp_ack_unordered().
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Switch to pre-splitted tp_vars structure names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++-
+ net/batman-adv/types.h    |  3 +++
+ 2 files changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 397b9456353568..4f8af909893ab9 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -87,6 +87,11 @@
+ #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \
+                       sizeof(struct batadv_unicast_packet))
++/**
++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack
++ */
++#define BATADV_TP_MAX_UNACKED 100
++
+ static u8 batadv_tp_prerandom[4096] __read_mostly;
+ /**
+@@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+       /* if the list is empty immediately attach this new object */
+       if (list_empty(&tp_vars->unacked_list)) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
+               goto out;
+       }
+@@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+                */
+               list_add(&new->list, &un->list);
+               added = true;
++              tp_vars->unacked_count++;
+               break;
+       }
+       /* received packet with smallest seqno out of order; add it to front */
+-      if (!added)
++      if (!added) {
+               list_add(&new->list, &tp_vars->unacked_list);
++              tp_vars->unacked_count++;
++      }
++
++      /* remove the last (biggest) unacked seqno when list is too large */
++      if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) {
++              un = list_last_entry(&tp_vars->unacked_list,
++                                   struct batadv_tp_unacked, list);
++              list_del(&un->list);
++              kfree(un);
++              tp_vars->unacked_count--;
++      }
+ out:
+       spin_unlock_bh(&tp_vars->unacked_lock);
+@@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+               list_del(&un->list);
+               kfree(un);
++              tp_vars->unacked_count--;
+       }
+       spin_unlock_bh(&tp_vars->unacked_lock);
+ }
+@@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+       spin_lock_init(&tp_vars->unacked_lock);
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
++      tp_vars->unacked_count = 0;
+       kref_get(&tp_vars->refcount);
+       timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 765ba71fe8c69f..4f9f1820e3a92a 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1426,6 +1426,9 @@ struct batadv_tp_vars {
+       /** @unacked_lock: protect unacked_list */
+       spinlock_t unacked_lock;
++      /** @unacked_count: number of unacked entries */
++      size_t unacked_count;
++
+       /** @last_recv_time: time (jiffies) a msg was received */
+       unsigned long last_recv_time;
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-7.1/batman-adv-tt-don-t-merge-change-entries-with-differ.patch
new file mode 100644 (file)
index 0000000..d808c51
--- /dev/null
@@ -0,0 +1,45 @@
+From d6f9bbc2de4900719a81c75f567bc79acb45823a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:37 +0200
+Subject: batman-adv: tt: don't merge change entries with different VIDs
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream.
+
+batadv_tt_local_event() merges/cancels events for the same client which
+would conflict or be duplicates. The matching of the queued events only
+compares the MAC address - the VLAN ID stored in each event is ignored.
+
+If a MAC would now appear on multiple VID, the two ADD change events (for
+VID 1 and VID 2) would be merged to a single vid event. The remote can
+therefore not calculate the correct TT table and desync. A full translation
+table exchange is required to recover from this state.
+
+A check of VID is therefore necessary to avoid such wrong merges/cancels.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 9f6e67771ffa80..acd8af4446671f 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -446,6 +446,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
+               if (!batadv_compare_eth(entry->change.addr, common->addr))
+                       continue;
++              if (entry->change.vid != tt_change_node->change.vid)
++                      continue;
++
+               del_op_entry = entry->change.flags & BATADV_TT_CLIENT_DEL;
+               if (del_op_requested != del_op_entry) {
+                       /* DEL+ADD in the same orig interval have no effect and
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tt-track-roam-count-per-vid.patch b/queue-7.1/batman-adv-tt-track-roam-count-per-vid.patch
new file mode 100644 (file)
index 0000000..5857c73
--- /dev/null
@@ -0,0 +1,88 @@
+From 0e598bfd4bdb4c923bed647e81511140b39bceb4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:38 +0200
+Subject: batman-adv: tt: track roam count per VID
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream.
+
+batadv_tt_check_roam_count() is supposed to track roaming of a TT entry.
+But TT entries are for a MAC + VID. The VID was completely missed and thus
+leads to incorrect detection of ROAM counts when a client MAC exists in
+multiple VLANs.
+
+Cc: stable@kernel.org
+Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 9 +++++++--
+ net/batman-adv/types.h             | 3 +++
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index acd8af4446671f..83dfd804a143ab 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -3442,6 +3442,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  * batadv_tt_check_roam_count() - check if a client has roamed too frequently
+  * @bat_priv: the bat priv with all the mesh interface information
+  * @client: mac address of the roaming client
++ * @vid: VLAN identifier
+  *
+  * This function checks whether the client already reached the
+  * maximum number of possible roaming phases. In this case the ROAMING_ADV
+@@ -3449,7 +3450,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
+  *
+  * Return: true if the ROAMING_ADV can be sent, false otherwise
+  */
+-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid)
+ {
+       struct batadv_tt_roam_node *tt_roam_node;
+       bool ret = false;
+@@ -3462,6 +3463,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               if (!batadv_compare_eth(tt_roam_node->addr, client))
+                       continue;
++              if (tt_roam_node->vid != vid)
++                      continue;
++
+               if (batadv_has_timed_out(tt_roam_node->first_time,
+                                        BATADV_ROAMING_MAX_TIME))
+                       continue;
+@@ -3483,6 +3487,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
+               atomic_set(&tt_roam_node->counter,
+                          BATADV_ROAMING_MAX_COUNT - 1);
+               ether_addr_copy(tt_roam_node->addr, client);
++              tt_roam_node->vid = vid;
+               list_add(&tt_roam_node->list, &bat_priv->tt.roam_list);
+               ret = true;
+@@ -3519,7 +3524,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
+       /* before going on we have to check whether the client has
+        * already roamed to us too many times
+        */
+-      if (!batadv_tt_check_roam_count(bat_priv, client))
++      if (!batadv_tt_check_roam_count(bat_priv, client, vid))
+               goto out;
+       batadv_dbg(BATADV_DBG_TT, bat_priv,
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 3f940a201cdf84..4dce996f01ccea 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1912,6 +1912,9 @@ struct batadv_tt_roam_node {
+       /** @addr: mac address of the client in the roaming phase */
+       u8 addr[ETH_ALEN];
++      /** @vid: VLAN identifier */
++      u16 vid;
++
+       /**
+        * @counter: number of allowed roaming events per client within a single
+        * OGM interval (changes are committed with each OGM)
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-7.1/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch
new file mode 100644 (file)
index 0000000..baa3d55
--- /dev/null
@@ -0,0 +1,154 @@
+From 9f855c0e6e12c62a92462b2aef9b08a82442f26a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:41 +0200
+Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream.
+
+TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to
+signal that the OGM handler should be called (with NULL for data) when the
+specific TVLV container was not found in the OGM. This is used by:
+
+* DAT
+* GW
+* Multicast (OGM + Tracker)
+
+The state whether the handler was executed was stored in the struct
+batadv_tvlv_handler. But the TVLV processing is started without any lock.
+Multiple parallel contexts processing TVLVs would therefore overwrite each
+others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared
+batadv_tvlv_handler.
+
+Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine,
+per TVLV buffer, whether a matching container was present by scanning the
+packet's buffer.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tvlv.c  | 63 ++++++++++++++++++++++++++++++++++++++----
+ net/batman-adv/types.h |  7 -----
+ 2 files changed, 57 insertions(+), 13 deletions(-)
+
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index baadf97f98e5f0..afb61a46d7a67c 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -398,7 +398,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+               tvlv_handler->ogm_handler(bat_priv, orig_node,
+                                         BATADV_NO_FLAGS,
+                                         tvlv_value, tvlv_value_len);
+-              tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
+               break;
+       case BATADV_UNICAST_TVLV:
+               if (!skb)
+@@ -430,6 +429,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+       return NET_RX_SUCCESS;
+ }
++/**
++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
++ * @tvlv_value: tvlv content
++ * @tvlv_value_len: tvlv content length
++ * @type: tvlv container type to look for
++ * @version: tvlv container version to look for
++ *
++ * Return: true if a container of the given type and version is present in the
++ * tvlv buffer, false otherwise.
++ */
++static bool batadv_tvlv_containers_contain(void *tvlv_value,
++                                         u16 tvlv_value_len, u8 type,
++                                         u8 version)
++{
++      struct batadv_tvlv_hdr *tvlv_hdr;
++      u16 tvlv_value_cont_len;
++
++      while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
++              tvlv_hdr = tvlv_value;
++              tvlv_value_cont_len = ntohs(tvlv_hdr->len);
++              tvlv_value = tvlv_hdr + 1;
++              tvlv_value_len -= sizeof(*tvlv_hdr);
++
++              if (tvlv_value_cont_len > tvlv_value_len)
++                      break;
++
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
++              if (tvlv_hdr->type == type && tvlv_hdr->version == version)
++                      return true;
++
++              tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
++              tvlv_value_len -= tvlv_value_cont_len;
++      }
++
++      return false;
++}
++
+ /**
+  * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the
+  *  appropriate handlers
+@@ -449,7 +490,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+                                  struct sk_buff *skb, void *tvlv_value,
+                                  u16 tvlv_value_len)
+ {
++      u16 tvlv_value_start_len = tvlv_value_len;
+       struct batadv_tvlv_handler *tvlv_handler;
++      void *tvlv_value_start = tvlv_value;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       u16 tvlv_value_cont_len;
+       u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
+@@ -493,12 +536,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+               if (!tvlv_handler->ogm_handler)
+                       continue;
+-              if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
+-                  !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
+-                      tvlv_handler->ogm_handler(bat_priv, orig_node,
+-                                                cifnotfound, NULL, 0);
++              if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND))
++                      continue;
+-              tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
++              /* if the corresponding container was present then the handler
++               * was already called from the loop above
++               */
++              if (batadv_tvlv_containers_contain(tvlv_value_start,
++                                                 tvlv_value_start_len,
++                                                 tvlv_handler->type,
++                                                 tvlv_handler->version))
++                      continue;
++
++              tvlv_handler->ogm_handler(bat_priv, orig_node,
++                                        cifnotfound, NULL, 0);
+       }
+       rcu_read_unlock();
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 4dce996f01ccea..30c870cacf4374 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -2245,13 +2245,6 @@ enum batadv_tvlv_handler_flags {
+        *  will call this handler even if its type was not found (with no data)
+        */
+       BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
+-
+-      /**
+-       * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the
+-       *  API marks a handler as being called, so it won't be called if the
+-       *  BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
+-       */
+-      BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
+ };
+ #endif /* _NET_BATMAN_ADV_TYPES_H_ */
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-7.1/batman-adv-tvlv-enforce-2-byte-alignment.patch
new file mode 100644 (file)
index 0000000..8e04ddb
--- /dev/null
@@ -0,0 +1,123 @@
+From 01e84cf126f63ab51ef800b816bf69a429f182a0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:40 +0200
+Subject: batman-adv: tvlv: enforce 2-byte alignment
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream.
+
+The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte
+alignment, so a following OGM must start at an even offset. As the header
+length is even, an odd tvlv_len would misalign it and trigger unaligned
+accesses on strict-alignment architectures.
+
+Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in
+the mesh. Therefore, reject such malformed packets.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 11 ++++++++++-
+ net/batman-adv/bat_v_ogm.c  | 11 ++++++++++-
+ net/batman-adv/routing.c    |  6 ++++++
+ net/batman-adv/tvlv.c       |  6 ++++++
+ 4 files changed, 32 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index b8b1b997960a96..6e79f69c2fedec 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -311,14 +311,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
+                         const struct batadv_ogm_packet *ogm_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm_packet->tvlv_len);
++
++      /* the fields of an aggregated OGM are accessed assuming (at least)
++       * 2-byte alignment, so a following OGM must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 6852bf5da8c558..1f9b2d2b4831ce 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -849,14 +849,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len,
+                        const struct batadv_ogm2_packet *ogm2_packet)
+ {
+       int next_buff_pos = 0;
++      u16 tvlv_len;
+       /* check if there is enough space for the header */
+       next_buff_pos += buff_pos + sizeof(*ogm2_packet);
+       if (next_buff_pos > packet_len)
+               return false;
++      tvlv_len = ntohs(ogm2_packet->tvlv_len);
++
++      /* the fields of an aggregated OGMv2 are accessed assuming (at least)
++       * 2-byte alignment, so a following OGMv2 must start at an even offset.
++       */
++      if (tvlv_len & 1)
++              return false;
++
+       /* check if there is enough space for the optional TVLV */
+-      next_buff_pos += ntohs(ogm2_packet->tvlv_len);
++      next_buff_pos += tvlv_len;
+       return next_buff_pos <= packet_len;
+ }
+diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
+index 4483f8d9c75831..41951c7a1c50b2 100644
+--- a/net/batman-adv/routing.c
++++ b/net/batman-adv/routing.c
+@@ -1366,6 +1366,12 @@ int batadv_recv_mcast_packet(struct sk_buff *skb,
+       if (tvlv_buff_len > skb->len - hdr_size)
+               goto free_skb;
++      /* the fields of an multicast payload are accessed assuming (at least)
++       * 2-byte alignment, so a following packet must start at an even offset.
++       */
++      if (tvlv_buff_len & 1)
++              goto free_skb;
++
+       ret = batadv_tvlv_containers_process(bat_priv, BATADV_MCAST, NULL, skb,
+                                            tvlv_buff, tvlv_buff_len);
+       if (ret >= 0) {
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index cc6ac580c62085..baadf97f98e5f0 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -464,6 +464,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+               if (tvlv_value_cont_len > tvlv_value_len)
+                       break;
++              /* the next tvlv header is accessed assuming (at least) 2-byte
++               * alignment, so it must start at an even offset.
++               */
++              if (tvlv_value_cont_len & 1)
++                      break;
++
+               tvlv_handler = batadv_tvlv_handler_get(bat_priv,
+                                                      tvlv_hdr->type,
+                                                      tvlv_hdr->version);
+-- 
+2.53.0
+
diff --git a/queue-7.1/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-7.1/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
new file mode 100644 (file)
index 0000000..7ab993e
--- /dev/null
@@ -0,0 +1,106 @@
+From 748bca1ecf89bc43ed6b143f25c29f48e352a93f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jun 2026 18:12:32 +0200
+Subject: batman-adv: v: prevent OGM aggregation on disabled hardif
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream.
+
+When an interface gets disabled, the worker is correctly disabled by
+batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable().
+In this process, the skb aggr_list is also freed.
+
+But batadv_v_ogm_send_meshif() can still queue new skbs (via
+batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all
+cores can no longer find the RCU protected list of hard interfaces. These
+queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work.
+
+The batadv_v_ogm_iface_disable() function must block
+batadv_v_ogm_queue_on_if() to avoid leak of skbs.
+
+Cc: stable@kernel.org
+Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_v.c     |  1 +
+ net/batman-adv/bat_v_ogm.c | 12 ++++++++++++
+ net/batman-adv/types.h     |  6 ++++++
+ 3 files changed, 19 insertions(+)
+
+diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c
+index de94447142642e..17d2a1ccdce67a 100644
+--- a/net/batman-adv/bat_v.c
++++ b/net/batman-adv/bat_v.c
+@@ -817,6 +817,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
+       hard_iface->bat_v.aggr_len = 0;
+       skb_queue_head_init(&hard_iface->bat_v.aggr_list);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq,
+                         batadv_v_ogm_aggr_work);
+ }
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index d66ca77b1aaa3c..6852bf5da8c558 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -252,11 +252,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
+       }
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      if (!hard_iface->bat_v.aggr_list_enabled) {
++              kfree_skb(skb);
++              goto unlock;
++      }
++
+       if (!batadv_v_ogm_queue_left(skb, hard_iface))
+               batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+       __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
++
++unlock:
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+@@ -417,6 +424,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
+ {
+       struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
++      spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = true;
++      spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
++
+       batadv_v_ogm_start_queue_timer(hard_iface);
+       batadv_v_ogm_start_timer(bat_priv);
+@@ -432,6 +443,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
+       cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq);
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
++      hard_iface->bat_v.aggr_list_enabled = false;
+       batadv_v_ogm_aggr_list_free(hard_iface);
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+ }
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index a01ee46d97f34f..765ba71fe8c69f 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v {
+       /** @aggr_list: queue for to be aggregated OGM packets */
+       struct sk_buff_head aggr_list;
++      /**
++       * @aggr_list_enabled: aggr_list is active and new skbs can be
++       * enqueued. Protected by aggr_list.lock after initialization
++       */
++      bool aggr_list_enabled:1;
++
+       /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */
+       unsigned int aggr_len;
+-- 
+2.53.0
+
index a385adea484e5cfbf0f0a3c26b71f680b4ebe484..9a76cbafd27646cf69d8c5f067ff8efbcd0f3e35 100644 (file)
@@ -1 +1,27 @@
 kvm-x86-fix-shadow-paging-use-after-free-due-to-unex.patch
+batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch
+batman-adv-tp_meter-initialize-dup_acks-explicitly.patch
+batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch
+batman-adv-tp_meter-avoid-window-underflow.patch
+batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch
+batman-adv-tp_meter-fix-fast-recovery-precondition.patch
+batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch
+batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch
+batman-adv-bla-annotate-lasttime-access-with-read-wr.patch
+batman-adv-prevent-elp-transmission-interval-underfl.patch
+batman-adv-tp_meter-initialize-last_recv_time-during.patch
+batman-adv-gw-don-t-deselect-gateway-with-active-har.patch
+batman-adv-ensure-bcast-is-writable-before-modifying.patch
+batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch
+batman-adv-frag-ensure-fragment-is-writable-before-m.patch
+batman-adv-frag-avoid-underflow-of-ttl.patch
+batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch
+batman-adv-tp_meter-restrict-number-of-unacked-list-.patch
+batman-adv-tp_meter-annotate-last_recv_time-access-w.patch
+batman-adv-tp_meter-prevent-parallel-modifications-o.patch
+batman-adv-tp_meter-handle-overlapping-packets.patch
+batman-adv-tt-don-t-merge-change-entries-with-differ.patch
+batman-adv-tt-track-roam-count-per-vid.patch
+batman-adv-dat-prevent-false-sharing-between-vlans.patch
+batman-adv-tvlv-enforce-2-byte-alignment.patch
+batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch