batman-adv: merge bugfixes from 2026.3

* ensure minimal ethernet header on TX
* fix VLAN priority offset
* tt: avoid request storms during pending request
* frag: fix primary_if leak on failed linearization
* frag: free unfragmentable packet
* mcast: avoid OOB read of num_dests header
* tt: prevent TVLV OOB check overflow
* clean untagged VLAN on netdev registration failure
* dat: fix tie-break for candidate selection
* bla: avoid CRC corruption due to parallel claim add
* bla: prevent CRC corruptions after claim flush
* dat: avoid unaligned fault in IP extraction
* dat: atomically update mac addresses
* fix TX priority extraction for BATADV_FORW_MCAST
* mcast: ensure unshared skb for multicast packets
* mcast: linearize skbuff for packet generation
* fix stale receive device on merged fragments
* tvlv: handle negative tvlv processing return codes
* bla: fix freeing of claims on meshif deletion
* reject unrepresentable multicast TVLV offsets

Signed-off-by: Sven Eckelmann <sven@narfation.org>
This commit is contained in:
Sven Eckelmann committed 2026-09-01 23:06:29 +02:00
1 parent 271a5d1794
commit 85fe1cf7b8
22 files changed
+1159 -1

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=batman-adv
PKG_VERSION:=2025.4
PKG_RELEASE:=6
PKG_RELEASE:=7
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://downloads.open-mesh.org/batman/releases/batman-adv-$(PKG_VERSION)
@@ -0,0 +1,32 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Thu, 2 Jul 2026 11:34:54 +0200
Subject: batman-adv: ensure minimal ethernet header on TX
As documented in commit 8bd67ebb50c0 ("net: bridge: xmit: make sure we have
at least eth header len bytes"), it is possible by for a local user with
eBPF TC hook access to attach a tc filter which truncates the packet and
redirects to an batadv interface. But the code assumes that at least
ETH_HLEN bytes are available and thus might read outside of the available
buffer.
The batadv_interface_tx() must therefore always check itself if enough data
is available for the ethernet header and don't rely on min_header_len.
Fixes: 094a751463a9 ("route outgoing traffic")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=c54eb3034180f1e5230da89cdb6885849d93c397
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -193,6 +193,9 @@ static netdev_tx_t batadv_interface_tx(s
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
goto dropped;
+ if (!pskb_may_pull(skb, ETH_HLEN))
+ goto dropped;
+
/* reset control block to avoid left overs from previous users */
memset(skb->cb, 0, sizeof(struct batadv_skb_cb));
@@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Thu, 2 Jul 2026 20:41:13 +0200
Subject: batman-adv: fix VLAN priority offset
The batadv_skb_set_priority() receives an SKB with the inner ethernet
header at position "offset". When it tries to extract the IPv4 and IPv6
header, it needs to skip the ethernet header to get access to the IP
header.
But for VLAN header, it performs the access with the struct vlan_ethhdr.
This struct contains both both the ethernet header and the VLAN header. It
is therefore incorrect to skip over the whole vlan_ethhdr size to get
access to the vlan_ethhdr.
Fixes: eb7643628a60 ("batman-adv: set skb priority according to content")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=23b07325f689f8b88db714b1388d8dee136bc9d9
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -354,7 +354,7 @@ void batadv_skb_set_priority(struct sk_b
switch (ethhdr->h_proto) {
case htons(ETH_P_8021Q):
- vhdr = skb_header_pointer(skb, offset + sizeof(*vhdr),
+ vhdr = skb_header_pointer(skb, offset,
sizeof(*vhdr), &vhdr_tmp);
if (!vhdr)
return;
@@ -0,0 +1,32 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Thu, 2 Jul 2026 20:56:14 +0200
Subject: batman-adv: tt: avoid request storms during pending request
batadv_send_tt_request() allocates a tt_req_node when none exists for the
destination originator node. This should prevent that a multiple TT
requests are send at the same time to an originator.
But if allocation of the send buffer failed, this request must be cleaned
up again. But indicator for such a failure is "ret == false". But the
actual implementation is checking for "ret == true".
The check must be inverted to not loose the information about the TT
request directly after it was attempted to be sent out. This should avoid
potential request storms.
Fixes: 6228419df3a5 ("batman-adv: tvlv - convert tt query packet to use tvlv unicast packets")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=1315afbabaa985f87fa772616b70df025bbe3dcc
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -3003,7 +3003,7 @@ static bool batadv_send_tt_request(struc
out:
batadv_hardif_put(primary_if);
- if (ret && tt_req_node) {
+ if (!ret && tt_req_node) {
spin_lock_bh(&bat_priv->tt.req_list_lock);
if (!hlist_unhashed(&tt_req_node->list)) {
hlist_del_init(&tt_req_node->list);
@@ -0,0 +1,25 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Fri, 3 Jul 2026 20:51:44 +0200
Subject: batman-adv: frag: fix primary_if leak on failed linearization
If the skb has a frag_list, it must be linearized before it can be split
using skb_split(). But when this step failed, it must not only free the skb
but also take care of the reference to the already found primary_if.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Fixes: d467720acaf1 ("batman-adv: Don't skb_split skbuffs with frag_list")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=54ddb49d2d97e1667bd5f2b6a8ee291a668f37f2
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -543,7 +543,7 @@ int batadv_frag_send_packet(struct sk_bu
*/
if (skb_has_frag_list(skb) && __skb_linearize(skb)) {
ret = -ENOMEM;
- goto free_skb;
+ goto put_primary_if;
}
/* Create one header to be copied to all fragments */
@@ -0,0 +1,31 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Fri, 3 Jul 2026 20:21:16 +0200
Subject: batman-adv: frag: free unfragmentable packet
The caller of batadv_frag_send_packet() assume that the skb provided to the
function are always consumed. But the pre-check for an empty payload or the
zero fragment size returned an error without any further actions.
A failed pre-check must use the same error handling code as the rest of the
function.
Fixes: db56e4ecf5c2 ("batman-adv: Fragment and send skbs larger than mtu")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=2cc7404af2a7d18357134237a41a55b303eea700
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -516,8 +516,10 @@ int batadv_frag_send_packet(struct sk_bu
mtu = min_t(unsigned int, mtu, BATADV_FRAG_MAX_FRAG_SIZE);
max_fragment_size = mtu - header_size;
- if (skb->len == 0 || max_fragment_size == 0)
- return -EINVAL;
+ if (skb->len == 0 || max_fragment_size == 0) {
+ ret = -EINVAL;
+ goto free_skb;
+ }
num_fragments = (skb->len - 1) / max_fragment_size + 1;
max_fragment_size = (skb->len - 1) / num_fragments + 1;
@@ -0,0 +1,46 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Fri, 3 Jul 2026 20:33:20 +0200
Subject: batman-adv: mcast: avoid OOB read of num_dests header
Before the access to struct batadv_tvlv_mcast_tracker's num_dests, it is
attempted to check whether enough space is actually in the network header.
But instead of using offsetofend() to check for the whole size (2) which
must be accessible, offsetof() of is called. The latter is always returning
0. The comparison with the network header length will always return that
enough data is available - even when only 1 or 0 bytes are accessible.
Instead of using offsetofend(), use the more common check for the whole
header.
Fixes: 8ed36122d709 ("batman-adv: mcast: implement multicast packet reception and forwarding")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=34d3097e0313f9a2b44c96fb99d941c1acb896fa
--- a/net/batman-adv/multicast_forw.c
+++ b/net/batman-adv/multicast_forw.c
@@ -927,11 +927,11 @@ static int batadv_mcast_forw_packet(stru
{
struct batadv_tvlv_mcast_tracker *mcast_tracker;
struct batadv_neigh_node *neigh_node;
- unsigned long offset, num_dests_off;
struct sk_buff *nexthop_skb;
unsigned char *skb_net_hdr;
bool local_recv = false;
unsigned int tvlv_len;
+ unsigned long offset;
bool xmitted = false;
u8 *dest, *next_dest;
u16 num_dests;
@@ -940,9 +940,8 @@ static int batadv_mcast_forw_packet(stru
/* (at least) TVLV part needs to be linearized */
SKB_LINEAR_ASSERT(skb);
- /* check if num_dests is within skb length */
- num_dests_off = offsetof(struct batadv_tvlv_mcast_tracker, num_dests);
- if (num_dests_off > skb_network_header_len(skb))
+ /* check if batadv_tvlv_mcast_tracker header is within skb length */
+ if (sizeof(*mcast_tracker) > skb_network_header_len(skb))
return -EINVAL;
skb_net_hdr = skb_network_header(skb);
@@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Fri, 3 Jul 2026 22:19:01 +0200
Subject: batman-adv: tt: prevent TVLV OOB check overflow
A TT unicast TVLV contains the number of VLANs stored in it. This number is
an u16 and gets multiplied by the size of the struct
batadv_tvlv_tt_vlan_data (8 bytes). The size can therefore overflow the u16
used to store the tt_vlan_len. All additional safety checks to prevent
out-of-bounds access of the TVLV buffer are invalid due to this overflow.
Using size_t prevents this overflow and ensures that the safety checks
compare against the actual buffer requirements.
Fixes: 21a57f6e7a3b ("batman-adv: make the TT CRC logic VLAN specific")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=f11771cdce654b3b5e17d0fc08fb32c0d6fa6a39
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -4065,7 +4065,8 @@ static int batadv_tt_tvlv_unicast_handle
u16 tvlv_value_len)
{
struct batadv_tvlv_tt_data *tt_data;
- u16 tt_vlan_len, tt_num_entries;
+ u16 tt_num_entries;
+ size_t tt_vlan_len;
char tt_flag;
bool ret;
@@ -0,0 +1,92 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Sat, 4 Jul 2026 09:19:00 +0200
Subject: batman-adv: clean untagged VLAN on netdev registration failure
When an mesh interface is registered, it creates an untagged struct
batadv_meshif_vlan on top of it via the NETDEV_REGISTER notifier. But in
this process, another receiver of this notification can veto the
registration. The netdev registration will be aborted because of this veto.
The register_netdevice() call will try to clean up the net_device using
unregister_netdevice_queue() - which only uses the .priv_destructor to
free private resources. In this situation, .dellink will not be called.
The cleanup of the untagged batadv_meshif_vlan must thefore be done in the
destructor to avoid a leak of this object.
Fixes: 952cebb57518 ("batman-adv: add per VLAN interface attribute framework")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=ebb41e16afeea3cfc0fd0c2abfa05b78a3ecec32
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -245,6 +245,7 @@ err_orig:
void batadv_mesh_free(struct net_device *mesh_iface)
{
struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
+ struct batadv_meshif_vlan *vlan;
atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
@@ -259,6 +260,13 @@ void batadv_mesh_free(struct net_device
batadv_mcast_free(bat_priv);
+ /* destroy the "untagged" VLAN */
+ vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS);
+ if (vlan) {
+ batadv_meshif_destroy_vlan(bat_priv, vlan);
+ batadv_meshif_vlan_put(vlan);
+ }
+
/* Free the TT and the originator tables only after having terminated
* all the other depending components which may use these structures for
* their purposes.
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -593,8 +593,8 @@ int batadv_meshif_create_vlan(struct bat
* @bat_priv: the bat priv with all the mesh interface information
* @vlan: the object to remove
*/
-static void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv,
- struct batadv_meshif_vlan *vlan)
+void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv,
+ struct batadv_meshif_vlan *vlan)
{
/* explicitly remove the associated TT local entry because it is marked
* with the NOPURGE flag
@@ -1103,22 +1103,13 @@ static int batadv_meshif_newlink(struct
static void batadv_meshif_destroy_netlink(struct net_device *mesh_iface,
struct list_head *head)
{
- struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
struct batadv_hard_iface *hard_iface;
- struct batadv_meshif_vlan *vlan;
while (!list_empty(&mesh_iface->adj_list.lower)) {
hard_iface = netdev_adjacent_get_private(mesh_iface->adj_list.lower.next);
batadv_hardif_disable_interface(hard_iface);
}
- /* destroy the "untagged" VLAN */
- vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS);
- if (vlan) {
- batadv_meshif_destroy_vlan(bat_priv, vlan);
- batadv_meshif_vlan_put(vlan);
- }
-
unregister_netdevice_queue(mesh_iface, head);
}
--- a/net/batman-adv/mesh-interface.h
+++ b/net/batman-adv/mesh-interface.h
@@ -21,6 +21,8 @@ void batadv_interface_rx(struct net_devi
bool batadv_meshif_is_valid(const struct net_device *net_dev);
extern struct rtnl_link_ops batadv_link_ops;
int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid);
+void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv,
+ struct batadv_meshif_vlan *vlan);
void batadv_meshif_vlan_release(struct kref *ref);
struct batadv_meshif_vlan *batadv_meshif_vlan_get(struct batadv_priv *bat_priv,
unsigned short vid);
@@ -0,0 +1,34 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Thu, 2 Jul 2026 19:32:40 +0200
Subject: batman-adv: dat: fix tie-break for candidate selection
The original version of the candidate selection for DAT attempted to
compare both candidate and max_orig_node to identify which has the smaller
MAC address. This comparison is required as tie-break when a hash collision
happened.
But the used function returned 0 when the function was not equal and a
non-zero value when it was equal. As result, the actually selected
node was dependent on the order of entries in the orig_hash and not
actually on the mac addresses. The last originator in the hash collision
would always win.
To have a proper ordering, it must diff the actual MAC address bytes and
reject the candidate when the diff is not smaller than 0.
Fixes: 34b3c3850e7d ("batman-adv: Distributed ARP Table - create DHT helper functions")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=b9185d1d5d7b5031f41d65913056532f78802d50
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -545,7 +545,7 @@ static bool batadv_is_orig_node_eligible
* the one with the lowest address
*/
if (tmp_max == max && max_orig_node &&
- batadv_compare_eth(candidate->orig, max_orig_node->orig))
+ memcmp(candidate->orig, max_orig_node->orig, ETH_ALEN) >= 0)
goto out;
ret = true;
@@ -0,0 +1,148 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Fri, 3 Jul 2026 00:30:53 +0200
Subject: batman-adv: bla: avoid CRC corruption due to parallel claim add
batadv_bla_add_claim() is used to add claims and modify the backbone of
claims for CLAIM frames from remote backbones and local packets. When it
handles a claim, it needs to either
* add the new claim's CRC to the backbone CRC
* remove the already existing claim's CRC from the old backbone and add it
to the new backbone
But when the "new" claim code was running in parallel to the "change
backbone" code, it can happen that the CRC was invalid because the
backbone_gw of the claim was changed twice in the "new" claim code path:
* CPU0 creates the claim for gateway A and publishes it in the claim
hash. The crc16 of the address has not yet been added to A's crc at
this point.
* CPU1 processes a claim frame of gateway B for the same client, finds
the just published claim, and performs the ownership change: it
switches the pointer to B, removes the crc16 from A's crc - which
never contained it - and adds it to B's crc.
* CPU0 continues behind the creation branch, unconditionally switches
the pointer back to A without compensating B's crc (its remove_crc
is false for the creation path), and finally adds the crc16 to A's
crc
The CRC is then wrong for both:
* claim belongs to A: but CRC is not part of backbone A's CRC
* claim doesn't belong to B: CRC is still part of backbone B's CRC
This wrong CRC is never recomputated from the stored claims. For local
backbone claims, this can also not recovered using syncs.
To avoid this, split the functionality in clear separate parts:
* new claim which always adds claim CRC to the backbone CRC (but never
changes the already set backbone_gw of the claim back)
* update of existing claim which automatically changes the backbone_gw
entry and only updates both backbone CRCs when there was an actual change
Reported-by: Sashiko <sashiko-bot@kernel.org>
Fixes: a9ce0dc43e2c ("batman-adv: add basic bridge loop avoidance code")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=d88d7ea0d0befbb759d142682a5413531feb05a6
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -699,12 +699,14 @@ static void batadv_bla_add_claim(struct
struct batadv_bla_backbone_gw *old_backbone_gw;
struct batadv_bla_claim *claim;
struct batadv_bla_claim search_claim;
- bool remove_crc = false;
int hash_added;
+ u16 claim_crc;
+ bool changed;
ether_addr_copy(search_claim.addr, mac);
search_claim.vid = vid;
claim = batadv_claim_hash_find(bat_priv, &search_claim);
+ claim_crc = crc16(0, mac, ETH_ALEN);
/* create a new claim entry if it does not exist yet. */
if (!claim) {
@@ -736,43 +738,55 @@ static void batadv_bla_add_claim(struct
kfree(claim);
return;
}
+
+ spin_lock_bh(&backbone_gw->crc_lock);
+ backbone_gw->crc ^= claim_crc;
+ spin_unlock_bh(&backbone_gw->crc_lock);
+
+ WRITE_ONCE(backbone_gw->lasttime, jiffies);
+
+ batadv_claim_put(claim);
+ return;
+ }
+
+ WRITE_ONCE(claim->lasttime, jiffies);
+
+ /* replace backbone_gw atomically and adjust reference counters */
+ spin_lock_bh(&claim->backbone_lock);
+ if (claim->backbone_gw != backbone_gw) {
+ changed = true;
+
+ old_backbone_gw = claim->backbone_gw;
+ kref_get(&backbone_gw->refcount);
+ claim->backbone_gw = backbone_gw;
} else {
- WRITE_ONCE(claim->lasttime, jiffies);
- if (claim->backbone_gw == backbone_gw)
- /* no need to register a new backbone */
- goto claim_free_ref;
+ old_backbone_gw = NULL;
+ changed = false;
+ }
+ spin_unlock_bh(&claim->backbone_lock);
+ if (changed) {
batadv_dbg(BATADV_DBG_BLA, bat_priv,
"%s(): changing ownership for %pM, vid %d to gw %pM\n",
__func__, mac, batadv_print_vid(vid),
backbone_gw->orig);
- remove_crc = true;
+ /* add claim address to new backbone_gw */
+ spin_lock_bh(&backbone_gw->crc_lock);
+ backbone_gw->crc ^= claim_crc;
+ spin_unlock_bh(&backbone_gw->crc_lock);
}
- /* replace backbone_gw atomically and adjust reference counters */
- spin_lock_bh(&claim->backbone_lock);
- old_backbone_gw = claim->backbone_gw;
- kref_get(&backbone_gw->refcount);
- claim->backbone_gw = backbone_gw;
- spin_unlock_bh(&claim->backbone_lock);
-
- if (remove_crc) {
+ if (old_backbone_gw) {
/* remove claim address from old backbone_gw */
spin_lock_bh(&old_backbone_gw->crc_lock);
- old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+ old_backbone_gw->crc ^= claim_crc;
spin_unlock_bh(&old_backbone_gw->crc_lock);
- }
- batadv_backbone_gw_put(old_backbone_gw);
+ batadv_backbone_gw_put(old_backbone_gw);
+ }
- /* add claim address to new backbone_gw */
- spin_lock_bh(&backbone_gw->crc_lock);
- backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
- spin_unlock_bh(&backbone_gw->crc_lock);
WRITE_ONCE(backbone_gw->lasttime, jiffies);
-
-claim_free_ref:
batadv_claim_put(claim);
}
@@ -0,0 +1,37 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Sun, 5 Jul 2026 22:21:50 +0200
Subject: batman-adv: bla: prevent CRC corruptions after claim flush
When batadv_bla_del_backbone_claims() tried to remove all claims of a
backbone, it sets the CRC to 0. It assumes that the it had the last
reference of the claims because batadv_claim_release() (which runs after
the last reference was released), is XORing the crc16 of the claim address
with the backbone CRC.
If there would be a parallel holder of any of these references, it could
happen that the backbone CRC is (0 ^ crc16(delayed_released_claim)). Which
is the wrong starting point for the new claims it may receive when the
remote answers the claim request from batadv_bla_send_request().
This reinitializations can be completely dropped to avoid this problem.
batadv_claim_release() will take care of fixing the backbone CRC.
Fixes: a9ce0dc43e2c ("batman-adv: add basic bridge loop avoidance code")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=76e6026f15c9f4aa9ad1f83e122fac6446fc1132
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -324,11 +324,6 @@ batadv_bla_del_backbone_claims(struct ba
}
spin_unlock_bh(list_lock);
}
-
- /* all claims gone, initialize CRC */
- spin_lock_bh(&backbone_gw->crc_lock);
- backbone_gw->crc = BATADV_BLA_CRC_INIT;
- spin_unlock_bh(&backbone_gw->crc_lock);
}
/**
@@ -0,0 +1,41 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Mon, 6 Jul 2026 19:46:37 +0200
Subject: batman-adv: dat: avoid unaligned fault in IP extraction
Independent of the alignment of the ARP packet in the SKB, either the
batadv_arp_ip_src or the batadv_arp_ip_dst will have an unaligned access
(on HW without native unaligned read support).
Use get_unaligned() to handle this properly on all architectures.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Fixes: 0591c25abca9 ("batman-adv: Distributed ARP Table - add ARP parsing functions")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=2ba19c19805d21753cba095cdad3a32b0d49971b
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -249,7 +249,10 @@ static u8 *batadv_arp_hw_src(struct sk_b
*/
static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size)
{
- return *(__force __be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN);
+ u8 *src = batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN;
+ __be32 *ip = (__force __be32 *)src;
+
+ return get_unaligned(ip);
}
/**
@@ -274,8 +277,9 @@ static u8 *batadv_arp_hw_dst(struct sk_b
static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
{
u8 *dst = batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN * 2 + 4;
+ __be32 *ip = (__force __be32 *)dst;
- return *(__force __be32 *)dst;
+ return get_unaligned(ip);
}
/**
@@ -0,0 +1,202 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Mon, 6 Jul 2026 18:36:47 +0200
Subject: batman-adv: dat: atomically update mac addresses
When a MAC address is updated in batadv_dat_entry_add(), it is done using a
simple copy function. A parallel reader might only see parts of this
update. In worst case, the reader is transporting the half updated MAC
address over the network or is creating an ARP response using it -
poisoning the ARP cache.
atomic64_t can be used to store the 48 bit of a mac address. A reader will
then either see the old mac address or the new one - never a mixture of
both.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Fixes: f6badf9eb582 ("batman-adv: Distributed ARP Table - implement local storage")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=f22cdaf1f4df73442504e428be94300fac7dfc12
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -375,18 +375,19 @@ batadv_dat_entry_hash_find(struct batadv
static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
u8 *mac_addr, unsigned short vid)
{
+ u64 u64_mac = ether_addr_to_u64(mac_addr);
struct batadv_dat_entry *dat_entry;
int hash_added;
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid);
/* if this entry is already known, just update it */
if (dat_entry) {
- if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr))
- ether_addr_copy(dat_entry->mac_addr, mac_addr);
+ atomic64_set(&dat_entry->mac_addr, u64_mac);
+
dat_entry->last_update = jiffies;
batadv_dbg(BATADV_DBG_DAT, bat_priv,
"Entry updated: %pI4 %pM (vid: %d)\n",
- &dat_entry->ip, dat_entry->mac_addr,
+ &dat_entry->ip, mac_addr,
batadv_print_vid(vid));
goto out;
}
@@ -397,7 +398,7 @@ static void batadv_dat_entry_add(struct
dat_entry->ip = ip;
dat_entry->vid = vid;
- ether_addr_copy(dat_entry->mac_addr, mac_addr);
+ atomic64_set(&dat_entry->mac_addr, u64_mac);
dat_entry->last_update = jiffies;
kref_init(&dat_entry->refcount);
@@ -413,7 +414,7 @@ static void batadv_dat_entry_add(struct
}
batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %d)\n",
- &dat_entry->ip, dat_entry->mac_addr, batadv_print_vid(vid));
+ &dat_entry->ip, mac_addr, batadv_print_vid(vid));
out:
batadv_dat_entry_put(dat_entry);
@@ -868,6 +869,8 @@ batadv_dat_cache_dump_entry(struct sk_bu
struct netlink_callback *cb,
struct batadv_dat_entry *dat_entry)
{
+ u8 mac[ETH_ALEN];
+ u64 u64_mac;
int msecs;
void *hdr;
@@ -880,11 +883,12 @@ batadv_dat_cache_dump_entry(struct sk_bu
genl_dump_check_consistent(cb, hdr);
msecs = jiffies_to_msecs(jiffies - dat_entry->last_update);
+ u64_mac = atomic64_read(&dat_entry->mac_addr);
+ u64_to_ether_addr(u64_mac, mac);
if (nla_put_in_addr(msg, BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
dat_entry->ip) ||
- nla_put(msg, BATADV_ATTR_DAT_CACHE_HWADDRESS, ETH_ALEN,
- dat_entry->mac_addr) ||
+ nla_put(msg, BATADV_ATTR_DAT_CACHE_HWADDRESS, ETH_ALEN, mac) ||
nla_put_u16(msg, BATADV_ATTR_DAT_CACHE_VID, dat_entry->vid) ||
nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, msecs)) {
genlmsg_cancel(msg, hdr);
@@ -1151,6 +1155,8 @@ bool batadv_dat_snoop_outgoing_arp_reque
struct net_device *mesh_iface = bat_priv->mesh_iface;
int hdr_size = 0;
unsigned short vid;
+ u8 mac[ETH_ALEN];
+ u64 u64_mac;
if (!atomic_read(&bat_priv->distributed_arp_table))
goto out;
@@ -1178,6 +1184,9 @@ bool batadv_dat_snoop_outgoing_arp_reque
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
if (dat_entry) {
+ u64_mac = atomic64_read(&dat_entry->mac_addr);
+ u64_to_ether_addr(u64_mac, mac);
+
/* If the ARP request is destined for a local client the local
* client will answer itself. DAT would only generate a
* duplicate packet.
@@ -1186,7 +1195,7 @@ bool batadv_dat_snoop_outgoing_arp_reque
* additional DAT answer may trigger kernel warnings about
* a packet coming from the wrong port.
*/
- if (batadv_is_my_client(bat_priv, dat_entry->mac_addr, vid)) {
+ if (batadv_is_my_client(bat_priv, mac, vid)) {
ret = true;
goto out;
}
@@ -1196,18 +1205,16 @@ bool batadv_dat_snoop_outgoing_arp_reque
* the backbone gws belonging to our backbone has claimed the
* destination.
*/
- if (!batadv_bla_check_claim(bat_priv,
- dat_entry->mac_addr, vid)) {
+ if (!batadv_bla_check_claim(bat_priv, mac, vid)) {
batadv_dbg(BATADV_DBG_DAT, bat_priv,
"Device %pM claimed by another backbone gw. Don't send ARP reply!",
- dat_entry->mac_addr);
+ mac);
ret = true;
goto out;
}
skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src,
- dat_entry->mac_addr,
- hw_src, vid);
+ mac, hw_src, vid);
if (!skb_new)
goto out;
@@ -1249,6 +1256,8 @@ bool batadv_dat_snoop_incoming_arp_reque
struct batadv_dat_entry *dat_entry = NULL;
bool ret = false;
unsigned short vid;
+ u8 mac[ETH_ALEN];
+ u64 u64_mac;
int err;
if (!atomic_read(&bat_priv->distributed_arp_table))
@@ -1276,8 +1285,11 @@ bool batadv_dat_snoop_incoming_arp_reque
if (!dat_entry)
goto out;
+ u64_mac = atomic64_read(&dat_entry->mac_addr);
+ u64_to_ether_addr(u64_mac, mac);
+
skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src,
- dat_entry->mac_addr, hw_src, vid);
+ mac, hw_src, vid);
if (!skb_new)
goto out;
@@ -1368,6 +1380,8 @@ bool batadv_dat_snoop_incoming_arp_reply
u8 *hw_src, *hw_dst;
bool dropped = false;
unsigned short vid;
+ u8 mac[ETH_ALEN];
+ u64 u64_mac;
if (!atomic_read(&bat_priv->distributed_arp_table))
goto out;
@@ -1396,11 +1410,17 @@ bool batadv_dat_snoop_incoming_arp_reply
* this frame would lead to doubled receive of an ARP reply.
*/
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_src, vid);
- if (dat_entry && batadv_compare_eth(hw_src, dat_entry->mac_addr)) {
- batadv_dbg(BATADV_DBG_DAT, bat_priv, "Doubled ARP reply removed: ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]; dat_entry: %pM-%pI4\n",
- hw_src, &ip_src, hw_dst, &ip_dst,
- dat_entry->mac_addr, &dat_entry->ip);
- dropped = true;
+ if (dat_entry) {
+ u64_mac = atomic64_read(&dat_entry->mac_addr);
+ u64_to_ether_addr(u64_mac, mac);
+
+ if (batadv_compare_eth(hw_src, mac)) {
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "Doubled ARP reply removed: ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]; dat_entry: %pM-%pI4\n",
+ hw_src, &ip_src, hw_dst, &ip_dst,
+ mac, &dat_entry->ip);
+ dropped = true;
+ }
}
/* Update our internal cache with both the IP addresses the node got
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -2127,7 +2127,7 @@ struct batadv_dat_entry {
__be32 ip;
/** @mac_addr: the MAC address associated to the stored IPv4 */
- u8 mac_addr[ETH_ALEN];
+ atomic64_t mac_addr;
/** @vid: the vlan ID associated to this entry */
unsigned short vid;
@@ -0,0 +1,48 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Thu, 9 Jul 2026 20:44:24 +0200
Subject: batman-adv: fix TX priority extraction for BATADV_FORW_MCAST
batadv_mcast_forw_mode_by_count() pushs the skb->data for BATADV_FORW_MCAST
forwarding via batadv_mcast_forw_mcsend(). But the
batadv_skb_set_priority() expects the ethernet header directly before
(skb->data + offset). With the moved skb->data, just some random data would
be accessed to get the priority data.
Move the batadv_skb_set_priority() before the decision about the handling
multicast packets and potential header modifications.
Fixes: be9b0169c840 ("batman-adv: mcast: implement multicast packet generation")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: backported, https://git.open-mesh.org/batman-adv.git/commit/?id=b2d7dfd897e1f8005bb610cf14c77238e488bea8
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -258,6 +258,8 @@ static netdev_tx_t batadv_interface_tx(s
if (batadv_compare_eth(ethhdr->h_dest, ectp_addr))
goto dropped;
+ batadv_skb_set_priority(skb, 0);
+
gw_mode = atomic_read(&bat_priv->gw.mode);
if (is_multicast_ether_addr(ethhdr->h_dest)) {
/* if gw mode is off, broadcast every packet */
@@ -291,6 +293,9 @@ static netdev_tx_t batadv_interface_tx(s
send:
if (do_bcast && !is_broadcast_ether_addr(ethhdr->h_dest)) {
+ /* WARNING batadv_mcast_forw_mode might add more headers
+ * in front of the skb. and might even reallocate the skb
+ */
forw_mode = batadv_mcast_forw_mode(bat_priv, skb, vid,
&mcast_is_routable);
switch (forw_mode) {
@@ -308,8 +313,6 @@ send:
}
}
- batadv_skb_set_priority(skb, 0);
-
/* ethernet packet should be broadcasted */
if (do_bcast) {
primary_if = batadv_primary_if_get_selected(bat_priv);
@@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Thu, 9 Jul 2026 21:17:08 +0200
Subject: batman-adv: mcast: ensure unshared skb for multicast packets
When a packet is transmitted via a batman-adv interface and has already
enough room for the header then the nothing will make sure that the skbuff
is unshared. But it is now allowed to modify a currently shared skbuff.
Always make sure that the pskb_expand_head() is not only called for a too
small header but also for shared skbuffs.
Fixes: be9b0169c840 ("batman-adv: mcast: implement multicast packet generation")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=e30382561093d4adefc64bd46d2273c4921543ac
--- a/net/batman-adv/multicast_forw.c
+++ b/net/batman-adv/multicast_forw.c
@@ -1100,8 +1100,7 @@ static int batadv_mcast_forw_expand_head
return -EINVAL;
}
- if (skb_headroom(skb) < hdr_size &&
- pskb_expand_head(skb, hdr_size, 0, GFP_ATOMIC) < 0)
+ if (skb_cow(skb, hdr_size) < 0)
return -ENOMEM;
return 0;
@@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Thu, 9 Jul 2026 21:26:13 +0200
Subject: batman-adv: mcast: linearize skbuff for packet generation
batadv_mcast_forw_packet() and batadv_mcast_forw_packet() is not only
called by the unsharing+linearizing batadv_recv_mcast_packet() handler.
When it is called by batadv_mcast_forw_mcsend() then it will be unshared
but not linearized. The SKB_LINEAR_ASSERT() can therefore cause a fatal
BUG().
The linearization should happen during the expansion of the head because
the call chain because the scrape function can be hit already during the
initial batadv_mcast_forw_mode() selection code:
* batadv_interface_tx
* batadv_mcast_forw_mode
* batadv_mcast_forw_mode_by_count()
* batadv_mcast_forw_push()
-> calls batadv_mcast_forw_expand_head() before everything else
* batadv_mcast_forw_push_tvlvs()
* batadv_mcast_forw_push_dests()
* batadv_mcast_forw_push_adjust_padding()
* batadv_mcast_forw_scrape()
Reported-by: Sashiko <sashiko-bot@kernel.org>
Fixes: 8ed36122d709 ("batman-adv: mcast: implement multicast packet reception and forwarding")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=426e36bbbc8497bcef8a7ea3fee5f93a563efe16
--- a/net/batman-adv/multicast_forw.c
+++ b/net/batman-adv/multicast_forw.c
@@ -1103,6 +1103,10 @@ static int batadv_mcast_forw_expand_head
if (skb_cow(skb, hdr_size) < 0)
return -ENOMEM;
+ /* batadv_mcast_forw_scrape() + batadv_mcast_forw_packet() require linearized skb */
+ if (skb_linearize(skb) < 0)
+ return -ENOMEM;
+
return 0;
}
@@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zhiling Zou <zhilinz@nebusec.ai>
Date: Fri, 31 Jul 2026 11:18:49 +0800
Subject: batman-adv: fix stale receive device on merged fragments
Fragment reassembly reuses the skb from the highest-numbered buffered
fragment as the merged packet. When that fragment was received on a hard
interface which is deleted before the chain completes, the merged skb can
re-enter the receive path with a stale skb->dev and skb_iif.
batadv_batman_skb_recv() passes such merged packets through the normal
receive handlers again. DAT and bridge loop avoidance both derive the ARP
header length from skb->dev, so they can dereference the freed net_device
before the packet reaches the local mesh interface.
Refresh the receive device metadata from the current receive device before
running the packet handlers. This keeps internally reinjected merged
fragments consistent with the normal receive path after hard interface
teardown.
Fixes: 9b3eab61754d ("batman-adv: Receive fragmented packets and merge")
Reported-by: Vega <vega@nebusec.ai>
Signed-off-by: Zhiling Zou <zhilinz@nebusec.ai>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=dc92cfae652a109fca4d0db084328395ce88d38e
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -436,6 +436,10 @@ int batadv_batman_skb_recv(struct sk_buf
if (!skb)
goto err_put;
+ /* Merged fragments re-enter here with reused skb metadata. */
+ skb->dev = dev;
+ skb->skb_iif = dev->ifindex;
+
/* packet should hold at least type and version */
if (unlikely(!pskb_may_pull(skb, 2)))
goto err_free;
@@ -0,0 +1,92 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Thu, 30 Jul 2026 10:57:53 +0200
Subject: batman-adv: tvlv: handle negative tvlv processing return codes
batadv_tvlv_containers_process() was implemented with only two return codes
from the handlers in mind:
* NET_RX_SUCCESS (0)
* NET_RX_DROP (1)
The multicast handlers broke this convention and are also returning
negative return codes. But the processing code was never updated to
correctly aggregate them.
To handle negative return codes for non-OGM(2) handlers, they are now
aggregated to:
* NET_RX_SUCCESS when no handlers returned a different return code
* the last negative return code when at least one handler returned a
negative return code
* NET_RX_DROP otherwise
Fixes: 8ed36122d709 ("batman-adv: mcast: implement multicast packet reception and forwarding")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=fb5afe23d039f942f109abed8e4581c4bef5da68
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -1334,7 +1334,9 @@ out:
* contents of its TVLV forwards it and/or decapsulates it to hand it to the
* mesh interface.
*
- * Return: NET_RX_DROP if the skb is not consumed, NET_RX_SUCCESS otherwise.
+ * Return: NET_RX_SUCCESS if the skb was locally received, NET_RX_DROP otherwise
+ * or a negative errno code when the multicast tracker TVLV could not be
+ * processed
*/
int batadv_recv_mcast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
--- a/net/batman-adv/tvlv.c
+++ b/net/batman-adv/tvlv.c
@@ -370,8 +370,9 @@ end:
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
*
- * Return: success if the handler was not found or the return value of the
- * handler callback.
+ * Return: NET_RX_SUCCESS if the handler was not found or the return value of
+ * the handler callback. The latter is NET_RX_SUCCESS or NET_RX_DROP for the
+ * unicast handler and additionally a negative errno code for the mcast handler.
*/
static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
struct batadv_tvlv_handler *tvlv_handler,
@@ -481,8 +482,9 @@ static bool batadv_tvlv_containers_conta
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
*
- * Return: success when processing an OGM or the return value of all called
- * handler callbacks.
+ * Return: NET_RX_SUCCESS when processing an OGM or the combined return value of
+ * all called handler callbacks. The latter is NET_RX_SUCCESS, NET_RX_DROP or,
+ * for BATADV_MCAST packets, a negative errno code.
*/
int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
u8 packet_type,
@@ -497,6 +499,7 @@ int batadv_tvlv_containers_process(struc
u16 tvlv_value_cont_len;
u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
int ret = NET_RX_SUCCESS;
+ int res;
while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
tvlv_hdr = tvlv_value;
@@ -517,10 +520,13 @@ int batadv_tvlv_containers_process(struc
tvlv_hdr->type,
tvlv_hdr->version);
- ret |= batadv_tvlv_call_handler(bat_priv, tvlv_handler,
- packet_type, orig_node, skb,
- tvlv_value,
- tvlv_value_cont_len);
+ res = batadv_tvlv_call_handler(bat_priv, tvlv_handler,
+ packet_type, orig_node, skb,
+ tvlv_value,
+ tvlv_value_cont_len);
+ if (ret == NET_RX_SUCCESS || res < 0)
+ ret = res;
+
batadv_tvlv_handler_put(tvlv_handler);
tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
tvlv_value_len -= tvlv_value_cont_len;
@@ -0,0 +1,59 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <sven@narfation.org>
Date: Wed, 22 Jul 2026 12:08:09 +0200
Subject: batman-adv: bla: fix freeing of claims on meshif deletion
When the mesh interface is getting deleted, then
batadv_bla_del_backbone_claims() (via batadv_bla_purge_backbone_gw()) could
make sure that all claims gets removed. But this function is only executed
when bat_priv->bla.claim_hash is not NULL. And since batadv_bla_free() is
always setting it to NULL before it is (indirectly) called, it was never
actually executed.
But the batadv_bla_purge_claims() -> batadv_handle_unclaim() is at the
moment too fragile because the BLA code is not handling the rehashing in
batadv_bla_update_orig_address(). The stored backbone address doesn't have
to be the one actually used for the hash bucket selection during the
initial adding of the backbone. The batadv_handle_unclaim() can therefore
fail to find the respective backbone for the unclaim and then stop the
deletion.
But the actual backbone_gw object is not needed for the unclaim because all
relevant information is always provided by the caller. And the check for
the existence of the backbone_gw doesn't provide any additional security
check for the deletion of a claim.
Fixes: a9ce0dc43e2c ("batman-adv: add basic bridge loop avoidance code")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=e48f421ba209b24e51f25126ca06cfa50de1139e
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -951,26 +951,18 @@ static bool batadv_handle_unclaim(struct
const u8 *backbone_addr, const u8 *claim_addr,
unsigned short vid)
{
- struct batadv_bla_backbone_gw *backbone_gw;
-
/* unclaim in any case if it is our own */
if (primary_if && batadv_compare_eth(backbone_addr,
primary_if->net_dev->dev_addr))
batadv_bla_send_claim(bat_priv, claim_addr, vid,
BATADV_CLAIM_TYPE_UNCLAIM);
- backbone_gw = batadv_backbone_hash_find(bat_priv, backbone_addr, vid);
-
- if (!backbone_gw)
- return true;
-
/* this must be an UNCLAIM frame */
batadv_dbg(BATADV_DBG_BLA, bat_priv,
"%s(): UNCLAIM %pM on vid %d (sent by %pM)...\n", __func__,
- claim_addr, batadv_print_vid(vid), backbone_gw->orig);
+ claim_addr, batadv_print_vid(vid), backbone_addr);
batadv_bla_del_claim(bat_priv, claim_addr, vid);
- batadv_backbone_gw_put(backbone_gw);
return true;
}
@@ -0,0 +1,46 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Kyle Zeng <kylebot@openai.com>
Date: Mon, 17 Aug 2026 08:49:54 +0000
Subject: batman-adv: reject unrepresentable multicast TVLV offsets
The network and transport header fields in struct sk_buff are 16-bit
offsets from skb->head, and U16_MAX is reserved as the unset transport
header value. batadv_tvlv_call_handler() sets both fields from a received
multicast TVLV without checking whether the TVLV end is representable.
If the end offset exceeds the field's range, skb_set_transport_header()
truncates it so that the transport header precedes the network header.
The negative difference is then returned by skb_network_header_len() as
a large u32. batadv_mcast_forw_packet() consequently accepts an oversized
multicast tracker and accesses memory beyond the skb data.
Add skb_set_transport_header_careful(), an offset-aware counterpart to
skb_reset_transport_header_careful(), which validates the final
head-relative offset before assigning it. Use the new helper in
batadv_tvlv_call_handler() and reject unrepresentable TVLVs before
setting the network header.
Fixes: 8ed36122d709 ("batman-adv: mcast: implement multicast packet reception and forwarding")
Suggested-by: Sven Eckelmann <sven@narfation.org>
Assisted-by: Codex:gpt-5.6-sol Codex:gpt-5.5-cyber
Signed-off-by: Kyle Zeng <kylebot@openai.com>
Co-developed-by: David Lee <david.lee@trailofbits.com>
Signed-off-by: David Lee <david.lee@trailofbits.com>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Origin: backported, https://git.open-mesh.org/batman-adv.git/commit/?id=1a89187fb01aefde94abf5ec65ec594b900257b6
--- a/net/batman-adv/tvlv.c
+++ b/net/batman-adv/tvlv.c
@@ -421,8 +421,11 @@ static int batadv_tvlv_call_handler(stru
return NET_RX_SUCCESS;
tvlv_offset = (unsigned char *)tvlv_value - skb->data;
+ if (!skb_set_transport_header_careful(skb,
+ tvlv_offset + tvlv_value_len))
+ return -EINVAL;
+
skb_set_network_header(skb, tvlv_offset);
- skb_set_transport_header(skb, tvlv_offset + tvlv_value_len);
return tvlv_handler->mcast_handler(bat_priv, skb);
}
+23
View File
@@ -22,6 +22,29 @@
#endif /* LINUX_VERSION_IS_LESS(6, 16, 0) */
#if LINUX_VERSION_IS_LESS(7, 3, 0)
#include <linux/skbuff.h>
static inline bool __must_check
batadv_skb_set_transport_header_careful(struct sk_buff *skb, const int offset)
{
long thoff = skb->data - skb->head + offset;
if (unlikely(thoff != (typeof(skb->transport_header))thoff))
return false;
if (unlikely(thoff == (typeof(skb->transport_header))~0U))
return false;
skb->transport_header = thoff;
return true;
}
#define skb_set_transport_header_careful batadv_skb_set_transport_header_careful
#endif /* LINUX_VERSION_IS_LESS(7, 3, 0) */
/* <DECLARE_EWMA> */
#include <linux/version.h>