B.A.T.M.A.N Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] fix: net/batman-adv: batadv_interface_kill_vid: extra batadv_meshif_vlan_put after destroy
From: Sven Eckelmann @ 2026-06-27  7:07 UTC (permalink / raw)
  To: WenTao Liang
  Cc: marek.lindner, sw, antonio, davem, edumazet, kuba, pabeni, horms,
	b.a.t.m.a.n, netdev, linux-kernel, stable
In-Reply-To: <178254092045.4739.1497464106445743950.b4-review@b4>

[-- Attachment #1: Type: text/plain, Size: 1251 bytes --]

On Saturday, 27 June 2026 08:15:20 CEST Sven Eckelmann wrote:
> On Sat, 27 Jun 2026 11:46:36 +0800, WenTao Liang <vulab@iscas.ac.cn> wrote:
> 
> Hi,
> 
> not-acked

Just noticed that we already have another odd patch from you [1] (and you 
never answered after my reply). Could it be that you just try to spread AI/
LLM(?) generated patches in stable@vger.kernel.org and hope that something 
sticks?

I see a lot more patch bombs and complains all over the place when searching 
the whole lore.kernel.org [2] and only checking the last couple of days.

If this is really the case - please don't do this. We already stress them (and 
other maintainers) enough by dumping large amounts of legitimate patches on 
them. Sending patches shutgun-style all over the place without any 
recognizable QA or oversight might just cause an overload. And when you then 
don't even take the time to react to the review of the patches or apply the 
requests they had to you (and instead invent new things to annoy them)... At 
least I will not spend an hour writing a reply to you anymore but directly 
reject your patch.

Regards,
	Sven

[1] https://lore.kernel.org/batman/20250401083901.2261-1-vulab@iscas.ac.cn/
[2] https://lore.kernel.org/all/?q=vulab@iscas.ac.cn

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH] fix: net/batman-adv: batadv_interface_kill_vid: extra batadv_meshif_vlan_put after destroy
From: Sven Eckelmann @ 2026-06-27  6:15 UTC (permalink / raw)
  To: WenTao Liang
  Cc: marek.lindner, sw, antonio, sven, davem, edumazet, kuba, pabeni,
	horms, b.a.t.m.a.n, netdev, linux-kernel, stable
In-Reply-To: <20260627034636.59693-1-vulab@iscas.ac.cn>

On Sat, 27 Jun 2026 11:46:36 +0800, WenTao Liang <vulab@iscas.ac.cn> wrote:

Hi,

not-acked

1. please don't send patches to netdev directly. See (from any recent
   batadv.git, netdev/net.git netdev/net-next.git or torvalds/linux.git):

    $ ./scripts/get_maintainer.pl 20260627034636.59693-1-vulab@iscas.ac.cn.mbx 
    Marek Lindner <marek.lindner@mailbox.org> (maintainer:BATMAN ADVANCED,blamed_fixes:1/1=100%)
    Simon Wunderlich <sw@simonwunderlich.de> (maintainer:BATMAN ADVANCED)
    Antonio Quartulli <antonio@mandelbit.com> (maintainer:BATMAN ADVANCED,blamed_fixes:1/1=100%)
    Sven Eckelmann <sven@narfation.org> (maintainer:BATMAN ADVANCED)
    b.a.t.m.a.n@lists.open-mesh.org (moderated list:BATMAN ADVANCED)
    linux-kernel@vger.kernel.org (open list)

2. please add after the "PATCH" the tree which it should enter (in this case
   "[PATCH batadv]". See: 

    ./scripts/get_maintainer.pl --scm 20260627034636.59693-1-vulab@iscas.ac.cn.mbx|grep '^git'           
    git https://git.open-mesh.org/batadv.git
    git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

3. Please use a subject line which follows the kernel style. See
   https://docs.kernel.org/process/submitting-patches.html#the-canonical-patch-formatA

   - no "fix: "
   - "batman-adv: " instead of "net/batman-adv: "
   - most likely no "batadv_interface_kill_vid: "
   - an actual summary of your change (because right now it says it adds(?) an extra put)

> In batadv_interface_kill_vid(), batadv_meshif_vlan_get() acquires a
> reference on the vlan object. batadv_meshif_destroy_vlan() internally
> calls batadv_meshif_vlan_put() which balances that reference. However, an

No, this doesn't balance the reference. The reference put in this function is
for the reference acquired by this function. The batadv_meshif_destroy_vlan()
put is for the reference for its "from .ndo_vlan_rx_add_vid till 
.ndo_vlan_rx_kill_vid" lifetime.

You can see exactly the same approach also in batadv_meshif_destroy_netlink()
for its "untagged" vlan. A function which you didn't touch.

> additional batadv_meshif_vlan_put(vlan) is called after
> batadv_meshif_destroy_vlan(), causing a refcount underflow and potential
> use-after-free of the vlan object.

No, doesn't cause an underflow in my setup. Please explain exactly how you
tested this and came the conclusion that this would cause a use-after-free.
Because I can't reproduce this and the patch in this form is causing a memory
leak for me.

> 
> Remove the extra batadv_meshif_vlan_put(vlan) call.

No, this can't be the correct solution.

>
>
> diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c
> index e5a55d24..7a1aeeca 100644
> --- a/net/batman-adv/mesh-interface.c
> +++ b/net/batman-adv/mesh-interface.c
> @@ -693,9 +693,6 @@ static int batadv_interface_kill_vid(struct net_device *dev, __be16 proto,
>  
>  	batadv_meshif_destroy_vlan(bat_priv, vlan);
>  
> -	/* finally free the vlan object */
> -	batadv_meshif_vlan_put(vlan);
> -

This looks wrong to me. Now it leaks the VLAN which was acquired at the
beginning of the function. When I add a kref_get-printk right before the
batadv_meshif_destroy_vlan() and in batadv_tt_local_entry_release() before the
puts:

    refcnt before batadv_meshif_destroy_vlan: 3
    refcnt after batadv_meshif_destroy_vlan: 2
    refcnt before batadv_tt_local_entry_release: 2
    refcnt after batadv_tt_local_entry_release: 1

As you can see, now the VLAN never reaches the 0 and thus isn't free'd. You can
also directly see the memory leak (which didn't happen before):

    root@node01:~# ip l del dev bat0.10
    [   18.127153][  T368] refcnt before batadv_meshif_destroy_vlan: 3
    [   18.128792][  T368] refcnt after batadv_meshif_destroy_vlan: 2
    [   18.649318][   T12] refcnt before batadv_tt_local_entry_release: 2
    [   18.650220][   T12] refcnt after batadv_tt_local_entry_release: 1
    root@node01:~# rmmod batman-adv
    [   27.033891][  T374] batman_adv: bat0: Interface deactivated: dummy0
    [   27.034522][  T374] batman_adv: bat0: Removing interface: dummy0
    [   27.038340][  T374] batman_adv: bat0: Interface deactivated: enp0s1
    [   27.038973][  T374] batman_adv: bat0: Removing interface: enp0s1
    [   27.044439][  T374] br0: port 1(bat0) entered disabled state
    [   27.049110][  T374] bat0 (unregistering): left allmulticast mode
    [   27.049486][  T374] bat0 (unregistering): left promiscuous mode
    [   27.049804][  T374] br0: port 1(bat0) entered disabled state
    [   27.096326][  T374] refcnt before batadv_tt_local_entry_release: 1
    [   27.096851][  T374] refcnt after batadv_tt_local_entry_release: 0
    root@node01:~# modprobe batman-adv 
    root@node01:~# echo scan > /sys/kernel/debug/kmemleak
    root@node01:~# echo scan > /sys/kernel/debug/kmemleak
    [   41.460324][  T361] kmemleak: 1 new suspected memory leaks (see /sys/kernel/debug/kmemleak)
    root@node01:~# cat /sys/kernel/debug/kmemleak
    unreferenced object 0xffff88800ab1bd00 (size 64):
      comm "ip", pid 300, jiffies 4294893634
      hex dump (first 32 bytes):
        c0 cb c7 13 80 88 ff ff 0a 80 00 00 00 00 00 00  ................
        00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
      backtrace (crc 552e6e51):
        kmemleak_alloc+0x55/0xa0
        __kmalloc_cache_noprof+0x2f4/0x540
        batadv_meshif_create_vlan+0x7c/0x450 [batman_adv]
        batadv_interface_add_vid+0xb6/0xd0 [batman_adv]
        vlan_add_rx_filter_info+0xee/0x160
        vlan_vid_add+0x2f6/0x910
        register_vlan_dev+0xc5/0x6f0
        vlan_newlink+0x40e/0x6f0
        rtnl_newlink_create+0x2e1/0x770
        __rtnl_newlink+0x20b/0x9d0
        rtnl_newlink+0x7f7/0xf90
        rtnetlink_rcv_msg+0x811/0xbf0
        netlink_rcv_skb+0x148/0x3f0
        rtnetlink_rcv+0x19/0x20
        netlink_unicast+0x5fc/0xa50
        netlink_sendmsg+0x82b/0xd70

Because of the errors this patch introduces and the form of the patch: will not
be applied in batadv.git

We can discuss an actual fix when you can explain us how this problem can
actually be reproduced.

-- 
Sven Eckelmann <sven@narfation.org>

^ permalink raw reply

* [PATCH] fix: net/batman-adv: batadv_interface_kill_vid: extra batadv_meshif_vlan_put after destroy
From: WenTao Liang @ 2026-06-27  3:46 UTC (permalink / raw)
  To: marek.lindner, sw, antonio, sven, davem, edumazet, kuba, pabeni
  Cc: horms, b.a.t.m.a.n, netdev, linux-kernel, WenTao Liang, stable

In batadv_interface_kill_vid(), batadv_meshif_vlan_get() acquires a
reference on the vlan object. batadv_meshif_destroy_vlan() internally
calls batadv_meshif_vlan_put() which balances that reference. However, an
additional batadv_meshif_vlan_put(vlan) is called after
batadv_meshif_destroy_vlan(), causing a refcount underflow and potential
use-after-free of the vlan object.

Remove the extra batadv_meshif_vlan_put(vlan) call.

Cc: stable@vger.kernel.org
Fixes: 5d2c05b21337 ("batman-adv: add per VLAN interface attribute framework")
Signed-off-by: WenTao Liang <vulab@iscas.ac.cn>
---
 net/batman-adv/mesh-interface.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c
index e7aa45bc6b7a..cc974f243200 100644
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -691,9 +691,6 @@ static int batadv_interface_kill_vid(struct net_device *dev, __be16 proto,
 
 	batadv_meshif_destroy_vlan(bat_priv, vlan);
 
-	/* finally free the vlan object */
-	batadv_meshif_vlan_put(vlan);
-
 	return 0;
 }
 
-- 
2.39.5 (Apple Git-154)


^ permalink raw reply related

* [PATCH batadv v2 2/2] batman-adv: tt: simplify NEW flag transition code
From: Sven Eckelmann @ 2026-06-26 16:28 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260626-tt-atomic-refactor-v2-0-b6b9c99d3bfb@narfation.org>

The function batadv_tt_local_set_flags() implements support for various
flags and to set/unset them. It is also possible to decide whether this
change should increase the TT size or not.

But in reality, this function is only used to transition tt local entries
from the NEW state and count these entries. Remove the rest of the code to
simplify the function.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/translation-table.c | 37 +++++++++----------------------------
 1 file changed, 9 insertions(+), 28 deletions(-)

diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 00873f75..fbc37b22 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -3908,15 +3908,11 @@ void batadv_tt_free(struct batadv_priv *bat_priv)
 }
 
 /**
- * batadv_tt_local_set_flags() - set or unset the specified flags on the local
- *  table and possibly count them in the TT size
+ * batadv_tt_local_transition_new() - unset the NEW flag on the local
+ *  table and count them in the TT size
  * @bat_priv: the bat priv with all the mesh interface information
- * @flags: the flag to switch
- * @enable: whether to set or unset the flag
- * @count: whether to increase the TT size by the number of changed entries
  */
-static void batadv_tt_local_set_flags(struct batadv_priv *bat_priv, u16 flags,
-				      bool enable, bool count)
+static void batadv_tt_local_transition_new(struct batadv_priv *bat_priv)
 {
 	struct batadv_hashtable *hash = bat_priv->tt.local_hash;
 	struct batadv_tt_common_entry *tt_common_entry;
@@ -3937,27 +3933,12 @@ static void batadv_tt_local_set_flags(struct batadv_priv *bat_priv, u16 flags,
 			entry_flags = &tt_common_entry->flags;
 			old_flags = atomic_read(entry_flags);
 
-			/* the old_flags value of the atomic test-and-
-			 * set/clear decides whether this entry counts as
-			 * changed.
-			 */
-			if (enable) {
-				if ((old_flags & flags) == flags)
-					continue;
+			if (!(old_flags & BATADV_TT_CLIENT_NEW))
+				continue;
 
-				old_flags = atomic_fetch_or(flags, entry_flags);
-				if ((old_flags & flags) == flags)
-					continue;
-			} else {
-				if (!(old_flags & flags))
-					continue;
-
-				old_flags = atomic_fetch_andnot(flags, entry_flags);
-				if (!(old_flags & flags))
-					continue;
-			}
-
-			if (!count)
+			old_flags = atomic_fetch_andnot(BATADV_TT_CLIENT_NEW,
+							entry_flags);
+			if (!(old_flags & BATADV_TT_CLIENT_NEW))
 				continue;
 
 			batadv_tt_local_size_inc(bat_priv,
@@ -4032,7 +4013,7 @@ static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv)
 		return;
 	}
 
-	batadv_tt_local_set_flags(bat_priv, BATADV_TT_CLIENT_NEW, false, true);
+	batadv_tt_local_transition_new(bat_priv);
 
 	batadv_tt_local_purge_pending_clients(bat_priv);
 	batadv_tt_local_update_crc(bat_priv);

-- 
2.47.3


^ permalink raw reply related

* [PATCH batadv v2 1/2] batman-adv: tt: use atomic flag modifications
From: Sven Eckelmann @ 2026-06-26 16:28 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260626-tt-atomic-refactor-v2-0-b6b9c99d3bfb@narfation.org>

The flags of translation table entries are modified in various places using
RMW operations like:

* read flags + add flag + store
* read flags + remove flag + store

These were done without making sure that no other context is doing a
similar operation at the same time. If another context does modify the
flags then it could happen that a store of the flag modifications is simply
lost. This problem can usually be fixed at a later point when the flags are
tried to be adjusted again.

To reduce the time the wrong flags are used, it is better to change the u16
flags type to use an atomic_t with its atomic helper to perform these
adjustments.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/translation-table.c | 246 +++++++++++++++++++++++++------------
 net/batman-adv/types.h             |   2 +-
 2 files changed, 167 insertions(+), 81 deletions(-)

diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index e891b7c4..00873f75 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -477,8 +477,8 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
 				  u8 event_flags)
 {
 	struct batadv_tt_common_entry *common = &tt_local_entry->common;
+	u8 flags = atomic_read(&common->flags) | event_flags;
 	struct batadv_tt_change_node *tt_change_node;
-	u8 flags = common->flags | event_flags;
 	struct batadv_tt_change_node *entry;
 	struct batadv_tt_change_node *safe;
 	bool del_op_requested;
@@ -677,11 +677,16 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,
 	bool iif_is_wifi = false;
 	struct hlist_head *head;
 	int packet_size_max;
+	u8 new_remote_flags;
 	bool ret = false;
 	u8 remote_flags;
 	int hash_added;
 	int table_size;
 	u32 match_mark;
+	u8 clear_flags;
+	u16 old_flags;
+	u8 set_flags;
+	u16 flags;
 
 	if (ifindex != BATADV_NULL_IFINDEX)
 		in_dev = dev_get_by_index(net, ifindex);
@@ -699,7 +704,9 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,
 
 	if (tt_local) {
 		tt_local->last_seen = jiffies;
-		if (tt_local->common.flags & BATADV_TT_CLIENT_PENDING) {
+
+		flags = atomic_read(&tt_local->common.flags);
+		if (flags & BATADV_TT_CLIENT_PENDING) {
 			batadv_dbg(BATADV_DBG_TT, bat_priv,
 				   "Re-adding pending client %pM (vid: %d)\n",
 				   addr, batadv_print_vid(vid));
@@ -708,11 +715,11 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,
 			 * this orig_interval. Since it popped up again, the
 			 * flag can be reset like it was never enqueued
 			 */
-			tt_local->common.flags &= ~BATADV_TT_CLIENT_PENDING;
+			atomic_andnot(BATADV_TT_CLIENT_PENDING, &tt_local->common.flags);
 			goto add_event;
 		}
 
-		if (tt_local->common.flags & BATADV_TT_CLIENT_ROAM) {
+		if (flags & BATADV_TT_CLIENT_ROAM) {
 			batadv_dbg(BATADV_DBG_TT, bat_priv,
 				   "Roaming client %pM (vid: %d) came back to its original location\n",
 				   addr, batadv_print_vid(vid));
@@ -721,7 +728,7 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,
 			 * that the client popped up again at its original
 			 * location such flag can be unset
 			 */
-			tt_local->common.flags &= ~BATADV_TT_CLIENT_ROAM;
+			atomic_andnot(BATADV_TT_CLIENT_ROAM, &tt_local->common.flags);
 			roamed_back = true;
 		}
 		goto check_roaming;
@@ -758,26 +765,29 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,
 		   addr, batadv_print_vid(vid),
 		   (u8)atomic_read(&bat_priv->tt.vn));
 
+	tt_local->common.vid = vid;
+	kref_init(&tt_local->common.refcount);
+	tt_local->last_seen = jiffies;
+	tt_local->common.added_at = tt_local->last_seen;
+	tt_local->vlan = vlan;
+
 	ether_addr_copy(tt_local->common.addr, addr);
 	/* The local entry has to be marked as NEW to avoid to send it in
 	 * a full table response going out before the next ttvn increment
 	 * (consistency check)
 	 */
-	tt_local->common.flags = BATADV_TT_CLIENT_NEW;
-	tt_local->common.vid = vid;
+	flags = BATADV_TT_CLIENT_NEW;
 	if (iif_is_wifi)
-		tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
-	kref_init(&tt_local->common.refcount);
-	tt_local->last_seen = jiffies;
-	tt_local->common.added_at = tt_local->last_seen;
-	tt_local->vlan = vlan;
+		flags |= BATADV_TT_CLIENT_WIFI;
 
 	/* the batman interface mac and multicast addresses should never be
 	 * purged
 	 */
 	if (batadv_compare_eth(addr, mesh_iface->dev_addr) ||
 	    is_multicast_ether_addr(addr))
-		tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE;
+		flags |= BATADV_TT_CLIENT_NOPURGE;
+
+	atomic_set(&tt_local->common.flags, flags);
 
 	kref_get(&tt_local->common.refcount);
 	hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt,
@@ -795,9 +805,17 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,
 
 check_roaming:
 	/* Check whether it is a roaming, but don't do anything if the roaming
-	 * process has already been handled
+	 * process has already been handled. The ROAM flag is claimed via an
+	 * atomic test-and-set to make sure that only a single context handles
+	 * the advertisement for a client
 	 */
-	if (tt_global && !(tt_global->common.flags & BATADV_TT_CLIENT_ROAM)) {
+	old_flags = BATADV_TT_CLIENT_ROAM;
+	if (tt_global &&
+	    !(atomic_read(&tt_global->common.flags) & BATADV_TT_CLIENT_ROAM))
+		old_flags = atomic_fetch_or(BATADV_TT_CLIENT_ROAM,
+					    &tt_global->common.flags);
+
+	if (tt_global && !(old_flags & BATADV_TT_CLIENT_ROAM)) {
 		/* These node are probably going to update their tt table */
 		head = &tt_global->orig_list;
 		rcu_read_lock();
@@ -807,27 +825,30 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,
 					     orig_entry->orig_node);
 		}
 		rcu_read_unlock();
-		if (roamed_back) {
-			batadv_tt_global_free(bat_priv, tt_global,
-					      "Roaming canceled");
-		} else {
-			/* The global entry has to be marked as ROAMING and
+
+		if (!roamed_back) {
+			/* The global entry was marked as ROAMING and
 			 * has to be kept for consistency purpose
 			 */
-			tt_global->common.flags |= BATADV_TT_CLIENT_ROAM;
 			tt_global->roam_at = jiffies;
 		}
 	}
 
+	/* clean up independent of the roaming advertisement handler */
+	if (tt_global && roamed_back)
+		batadv_tt_global_free(bat_priv, tt_global, "Roaming canceled");
+
 	/* store the current remote flags before altering them. This helps
 	 * understanding is flags are changing or not
 	 */
-	remote_flags = tt_local->common.flags & BATADV_TT_REMOTE_MASK;
+	remote_flags = atomic_read(&tt_local->common.flags) & BATADV_TT_REMOTE_MASK;
+
+	new_remote_flags = remote_flags;
 
 	if (iif_is_wifi)
-		tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
+		new_remote_flags |= BATADV_TT_CLIENT_WIFI;
 	else
-		tt_local->common.flags &= ~BATADV_TT_CLIENT_WIFI;
+		new_remote_flags &= ~BATADV_TT_CLIENT_WIFI;
 
 	/* check the mark in the skb: if it's equal to the configured
 	 * isolation_mark, it means the packet is coming from an isolated
@@ -836,14 +857,22 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,
 	match_mark = (mark & bat_priv->isolation_mark_mask);
 	if (bat_priv->isolation_mark_mask &&
 	    match_mark == bat_priv->isolation_mark)
-		tt_local->common.flags |= BATADV_TT_CLIENT_ISOLA;
+		new_remote_flags |= BATADV_TT_CLIENT_ISOLA;
 	else
-		tt_local->common.flags &= ~BATADV_TT_CLIENT_ISOLA;
+		new_remote_flags &= ~BATADV_TT_CLIENT_ISOLA;
+
+	set_flags = new_remote_flags & ~remote_flags;
+	if (set_flags)
+		atomic_or(set_flags, &tt_local->common.flags);
+
+	clear_flags = remote_flags & ~new_remote_flags;
+	if (clear_flags)
+		atomic_andnot(clear_flags, &tt_local->common.flags);
 
 	/* if any "dynamic" flag has been modified, resend an ADD event for this
 	 * entry so that all the nodes can get the new flags
 	 */
-	if (remote_flags ^ (tt_local->common.flags & BATADV_TT_REMOTE_MASK))
+	if (set_flags || clear_flags)
 		batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS);
 
 	ret = true;
@@ -1168,10 +1197,12 @@ batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid,
 	struct batadv_meshif_vlan *vlan;
 	unsigned int last_seen_msecs;
 	void *hdr;
+	u16 flags;
 	u32 crc;
 
 	local = container_of(common, struct batadv_tt_local_entry, common);
 	last_seen_msecs = jiffies_to_msecs(jiffies - local->last_seen);
+	flags = atomic_read(&common->flags);
 
 	vlan = batadv_meshif_vlan_get(bat_priv, common->vid);
 	if (!vlan)
@@ -1192,10 +1223,10 @@ batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid,
 	if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) ||
 	    nla_put_u32(msg, BATADV_ATTR_TT_CRC32, crc) ||
 	    nla_put_u16(msg, BATADV_ATTR_TT_VID, common->vid) ||
-	    nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, common->flags))
+	    nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, flags))
 		goto nla_put_failure;
 
-	if (!(common->flags & BATADV_TT_CLIENT_NOPURGE) &&
+	if (!(flags & BATADV_TT_CLIENT_NOPURGE) &&
 	    nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, last_seen_msecs))
 		goto nla_put_failure;
 
@@ -1323,7 +1354,7 @@ batadv_tt_local_set_pending(struct batadv_priv *bat_priv,
 	 * to be kept in the table in order to send it in a full table
 	 * response issued before the net ttvn increment (consistency check)
 	 */
-	tt_local_entry->common.flags |= BATADV_TT_CLIENT_PENDING;
+	atomic_or(BATADV_TT_CLIENT_PENDING, &tt_local_entry->common.flags);
 
 	batadv_dbg(BATADV_DBG_TT, bat_priv,
 		   "Local tt entry (%pM, vid: %d) pending to be removed: %s\n",
@@ -1349,13 +1380,14 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
 	struct batadv_tt_local_entry *tt_local_entry;
 	struct hlist_node *tt_removed_node;
 	u16 curr_flags = BATADV_NO_FLAGS;
+	u16 old_flags;
 	u16 flags;
 
 	tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
 	if (!tt_local_entry)
 		goto out;
 
-	curr_flags = tt_local_entry->common.flags;
+	curr_flags = atomic_read(&tt_local_entry->common.flags);
 
 	flags = BATADV_TT_CLIENT_DEL;
 	/* if this global entry addition is due to a roaming, the node has to
@@ -1365,10 +1397,12 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
 	if (roaming) {
 		flags |= BATADV_TT_CLIENT_ROAM;
 		/* mark the local client as ROAMed */
-		tt_local_entry->common.flags |= BATADV_TT_CLIENT_ROAM;
+		atomic_or(BATADV_TT_CLIENT_ROAM, &tt_local_entry->common.flags);
 	}
 
-	if (!(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW)) {
+	old_flags = atomic_fetch_andnot(BATADV_TT_CLIENT_NEW,
+					&tt_local_entry->common.flags);
+	if (!(old_flags & BATADV_TT_CLIENT_NEW)) {
 		batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags,
 					    message);
 		goto out;
@@ -1411,17 +1445,21 @@ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
 	struct batadv_tt_common_entry *tt_common_entry;
 	struct batadv_tt_local_entry *tt_local_entry;
 	struct hlist_node *node_tmp;
+	u16 flags;
 
 	hlist_for_each_entry_safe(tt_common_entry, node_tmp, head,
 				  hash_entry) {
 		tt_local_entry = container_of(tt_common_entry,
 					      struct batadv_tt_local_entry,
 					      common);
-		if (tt_local_entry->common.flags & BATADV_TT_CLIENT_NOPURGE)
+
+		flags = atomic_read(&tt_local_entry->common.flags);
+
+		if (flags & BATADV_TT_CLIENT_NOPURGE)
 			continue;
 
 		/* entry already marked for deletion */
-		if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)
+		if (flags & BATADV_TT_CLIENT_PENDING)
 			continue;
 
 		if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout))
@@ -1627,6 +1665,8 @@ batadv_tt_global_sync_flags(struct batadv_tt_global_entry *tt_global)
 	struct batadv_tt_orig_list_entry *orig_entry;
 	const struct hlist_head *head;
 	u16 flags = BATADV_NO_FLAGS;
+	int new_flags;
+	int old_flags;
 
 	rcu_read_lock();
 	head = &tt_global->orig_list;
@@ -1634,8 +1674,15 @@ batadv_tt_global_sync_flags(struct batadv_tt_global_entry *tt_global)
 		flags |= orig_entry->flags;
 	rcu_read_unlock();
 
-	flags |= tt_global->common.flags & (~BATADV_TT_SYNC_MASK);
-	tt_global->common.flags = flags;
+	/* replace the sync flags without dropping concurrent modifications of
+	 * the non-sync flags
+	 */
+	old_flags = atomic_read(&tt_global->common.flags);
+	do {
+		new_flags = (old_flags & ~BATADV_TT_SYNC_MASK) | flags;
+		if (new_flags == old_flags)
+			break;
+	} while (!atomic_try_cmpxchg(&tt_global->common.flags, &old_flags, new_flags));
 }
 
 /**
@@ -1718,7 +1765,9 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
 	struct batadv_tt_common_entry *common;
 	bool ret = false;
 	u16 local_flags;
+	u16 clear_flags;
 	int hash_added;
+	u16 old_flags;
 
 	/* ignore global entries from backbone nodes */
 	if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig, vid))
@@ -1732,7 +1781,7 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
 	 * table
 	 */
 	if ((flags & BATADV_TT_CLIENT_TEMP) && tt_local_entry &&
-	    !(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW))
+	    !(atomic_read(&tt_local_entry->common.flags) & BATADV_TT_CLIENT_NEW))
 		goto out;
 
 	if (!tt_global_entry) {
@@ -1746,7 +1795,7 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
 		common->vid = vid;
 
 		if (!is_multicast_ether_addr(common->addr))
-			common->flags = flags & (~BATADV_TT_SYNC_MASK);
+			atomic_set(&common->flags, flags & (~BATADV_TT_SYNC_MASK));
 
 		tt_global_entry->roam_at = 0;
 		/* node must store current time in case of roaming. This is
@@ -1775,6 +1824,8 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
 		}
 	} else {
 		common = &tt_global_entry->common;
+		clear_flags = 0;
+
 		/* If there is already a global entry, we can use this one for
 		 * our processing.
 		 * But if we are trying to add a temporary client then here are
@@ -1786,7 +1837,7 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
 		 *    originator list and add the new one orig_entry
 		 */
 		if (flags & BATADV_TT_CLIENT_TEMP) {
-			if (!(common->flags & BATADV_TT_CLIENT_TEMP))
+			if (!(atomic_read(&common->flags) & BATADV_TT_CLIENT_TEMP))
 				goto out;
 			if (batadv_tt_global_entry_has_orig(tt_global_entry,
 							    orig_node, NULL))
@@ -1795,24 +1846,26 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
 			goto add_orig_entry;
 		}
 
+		/* the change can carry possible "attribute" flags like the
+		 * TT_CLIENT_TEMP, therefore they have to be copied in the
+		 * client entry
+		 */
+		if (!is_multicast_ether_addr(common->addr))
+			atomic_or(flags & (~BATADV_TT_SYNC_MASK), &common->flags);
+
+		old_flags = atomic_read(&common->flags);
+
 		/* if the client was temporary added before receiving the first
 		 * OGM announcing it, we have to clear the TEMP flag. Also,
 		 * remove the previous temporary orig node and re-add it
 		 * if required. If the orig entry changed, the new one which
 		 * is a non-temporary entry is preferred.
 		 */
-		if (common->flags & BATADV_TT_CLIENT_TEMP) {
+		if (old_flags & BATADV_TT_CLIENT_TEMP) {
 			batadv_tt_global_del_orig_list(tt_global_entry);
-			common->flags &= ~BATADV_TT_CLIENT_TEMP;
+			clear_flags |= BATADV_TT_CLIENT_TEMP;
 		}
 
-		/* the change can carry possible "attribute" flags like the
-		 * TT_CLIENT_TEMP, therefore they have to be copied in the
-		 * client entry
-		 */
-		if (!is_multicast_ether_addr(common->addr))
-			common->flags |= flags & (~BATADV_TT_SYNC_MASK);
-
 		/* If there is the BATADV_TT_CLIENT_ROAM flag set, there is only
 		 * one originator left in the list and we previously received a
 		 * delete + roaming change for this originator.
@@ -1820,11 +1873,14 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
 		 * We should first delete the old originator before adding the
 		 * new one.
 		 */
-		if (common->flags & BATADV_TT_CLIENT_ROAM) {
+		if (old_flags & BATADV_TT_CLIENT_ROAM) {
 			batadv_tt_global_del_orig_list(tt_global_entry);
-			common->flags &= ~BATADV_TT_CLIENT_ROAM;
+			clear_flags |= BATADV_TT_CLIENT_ROAM;
 			tt_global_entry->roam_at = 0;
 		}
+
+		if (clear_flags)
+			atomic_andnot(clear_flags, &common->flags);
 	}
 add_orig_entry:
 	/* add the new orig_entry (if needed) or update it */
@@ -1848,13 +1904,15 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
 	local_flags = batadv_tt_local_remove(bat_priv, tt_addr, vid,
 					     "global tt received",
 					     flags & BATADV_TT_CLIENT_ROAM);
-	tt_global_entry->common.flags |= local_flags & BATADV_TT_CLIENT_WIFI;
+	if (local_flags & BATADV_TT_CLIENT_WIFI)
+		atomic_or(local_flags & BATADV_TT_CLIENT_WIFI,
+			  &tt_global_entry->common.flags);
 
 	if (!(flags & BATADV_TT_CLIENT_ROAM))
 		/* this is a normal global add. Therefore the client is not in a
 		 * roaming state anymore.
 		 */
-		tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_ROAM;
+		atomic_andnot(BATADV_TT_CLIENT_ROAM, &tt_global_entry->common.flags);
 
 out:
 	batadv_tt_global_entry_put(tt_global_entry);
@@ -1924,7 +1982,7 @@ batadv_tt_global_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq,
 			       struct batadv_tt_orig_list_entry *orig,
 			       bool best)
 {
-	u16 flags = (common->flags & (~BATADV_TT_SYNC_MASK)) | orig->flags;
+	u16 flags = (atomic_read(&common->flags) & (~BATADV_TT_SYNC_MASK)) | orig->flags;
 	struct batadv_orig_node_vlan *vlan;
 	u8 last_ttvn;
 	void *hdr;
@@ -2233,7 +2291,7 @@ batadv_tt_global_del_roaming(struct batadv_priv *bat_priv,
 
 	if (last_entry) {
 		/* its the last one, mark for roaming. */
-		tt_global_entry->common.flags |= BATADV_TT_CLIENT_ROAM;
+		atomic_or(BATADV_TT_CLIENT_ROAM, &tt_global_entry->common.flags);
 		tt_global_entry->roam_at = jiffies;
 	} else {
 		/* there is another entry, we can simply delete this
@@ -2381,15 +2439,16 @@ static bool batadv_tt_global_to_purge(struct batadv_tt_global_entry *tt_global,
 {
 	unsigned long roam_timeout = BATADV_TT_CLIENT_ROAM_TIMEOUT;
 	unsigned long temp_timeout = BATADV_TT_CLIENT_TEMP_TIMEOUT;
+	u16 flags = atomic_read(&tt_global->common.flags);
 	bool purge = false;
 
-	if ((tt_global->common.flags & BATADV_TT_CLIENT_ROAM) &&
+	if ((flags & BATADV_TT_CLIENT_ROAM) &&
 	    batadv_has_timed_out(tt_global->roam_at, roam_timeout)) {
 		purge = true;
 		*msg = "Roaming timeout\n";
 	}
 
-	if ((tt_global->common.flags & BATADV_TT_CLIENT_TEMP) &&
+	if ((flags & BATADV_TT_CLIENT_TEMP) &&
 	    batadv_has_timed_out(tt_global->common.added_at, temp_timeout)) {
 		purge = true;
 		*msg = "Temporary client timeout\n";
@@ -2501,13 +2560,16 @@ static bool
 _batadv_is_ap_isolated(struct batadv_tt_local_entry *tt_local_entry,
 		       struct batadv_tt_global_entry *tt_global_entry)
 {
-	if (tt_local_entry->common.flags & BATADV_TT_CLIENT_WIFI &&
-	    tt_global_entry->common.flags & BATADV_TT_CLIENT_WIFI)
+	u16 global_flags = atomic_read(&tt_global_entry->common.flags);
+	u16 local_flags = atomic_read(&tt_local_entry->common.flags);
+
+	if (local_flags & BATADV_TT_CLIENT_WIFI &&
+	    global_flags & BATADV_TT_CLIENT_WIFI)
 		return true;
 
 	/* check if the two clients are marked as isolated */
-	if (tt_local_entry->common.flags & BATADV_TT_CLIENT_ISOLA &&
-	    tt_global_entry->common.flags & BATADV_TT_CLIENT_ISOLA)
+	if (local_flags & BATADV_TT_CLIENT_ISOLA &&
+	    global_flags & BATADV_TT_CLIENT_ISOLA)
 		return true;
 
 	return false;
@@ -2540,7 +2602,7 @@ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
 	if (src && batadv_vlan_ap_isola_get(bat_priv, vid)) {
 		tt_local_entry = batadv_tt_local_hash_find(bat_priv, src, vid);
 		if (!tt_local_entry ||
-		    (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING))
+		    (atomic_read(&tt_local_entry->common.flags) & BATADV_TT_CLIENT_PENDING))
 			goto out;
 	}
 
@@ -2604,6 +2666,7 @@ static u32 batadv_tt_global_crc(struct batadv_priv *bat_priv,
 	struct batadv_tt_common_entry *tt_common;
 	struct batadv_tt_global_entry *tt_global;
 	struct hlist_head *head;
+	u16 entry_flags;
 	__be16 tmp_vid;
 	u32 crc_tmp;
 	u32 crc = 0;
@@ -2624,18 +2687,20 @@ static u32 batadv_tt_global_crc(struct batadv_priv *bat_priv,
 			if (tt_common->vid != vid)
 				continue;
 
+			entry_flags = atomic_read(&tt_common->flags);
+
 			/* Roaming clients are in the global table for
 			 * consistency only. They don't have to be
 			 * taken into account while computing the
 			 * global crc
 			 */
-			if (tt_common->flags & BATADV_TT_CLIENT_ROAM)
+			if (entry_flags & BATADV_TT_CLIENT_ROAM)
 				continue;
 			/* Temporary clients have not been announced yet, so
 			 * they have to be skipped while computing the global
 			 * crc
 			 */
-			if (tt_common->flags & BATADV_TT_CLIENT_TEMP)
+			if (entry_flags & BATADV_TT_CLIENT_TEMP)
 				continue;
 
 			/* find out if this global entry is announced by this
@@ -2684,6 +2749,7 @@ static u32 batadv_tt_local_crc(struct batadv_priv *bat_priv,
 	struct batadv_hashtable *hash = bat_priv->tt.local_hash;
 	struct batadv_tt_common_entry *tt_common;
 	struct hlist_head *head;
+	u16 entry_flags;
 	__be16 tmp_vid;
 	u32 crc_tmp;
 	u32 crc = 0;
@@ -2701,10 +2767,12 @@ static u32 batadv_tt_local_crc(struct batadv_priv *bat_priv,
 			if (tt_common->vid != vid)
 				continue;
 
+			entry_flags = atomic_read(&tt_common->flags);
+
 			/* not yet committed clients have not to be taken into
 			 * account while computing the CRC
 			 */
-			if (tt_common->flags & BATADV_TT_CLIENT_NEW)
+			if (entry_flags & BATADV_TT_CLIENT_NEW)
 				continue;
 
 			/* use network order to read the VID: this ensures that
@@ -2716,7 +2784,7 @@ static u32 batadv_tt_local_crc(struct batadv_priv *bat_priv,
 			/* compute the CRC on flags that have to be kept in sync
 			 * among nodes
 			 */
-			flags = tt_common->flags & BATADV_TT_SYNC_MASK;
+			flags = entry_flags & BATADV_TT_SYNC_MASK;
 			crc_tmp = crc32c(crc_tmp, &flags, sizeof(flags));
 
 			crc ^= crc32c(crc_tmp, tt_common->addr, ETH_ALEN);
@@ -2879,12 +2947,13 @@ static bool batadv_tt_local_valid(const void *entry_ptr,
 				  u8 *flags)
 {
 	const struct batadv_tt_common_entry *tt_common_entry = entry_ptr;
+	u16 entry_flags = atomic_read(&tt_common_entry->flags);
 
-	if (tt_common_entry->flags & BATADV_TT_CLIENT_NEW)
+	if (entry_flags & BATADV_TT_CLIENT_NEW)
 		return false;
 
 	if (flags)
-		*flags = tt_common_entry->flags;
+		*flags = entry_flags;
 
 	return true;
 }
@@ -2908,9 +2977,10 @@ static bool batadv_tt_global_valid(const void *entry_ptr,
 	const struct batadv_tt_common_entry *tt_common_entry = entry_ptr;
 	const struct batadv_tt_global_entry *tt_global_entry;
 	const struct batadv_orig_node *orig_node = data_ptr;
+	u16 entry_flags;
 
-	if (tt_common_entry->flags & BATADV_TT_CLIENT_ROAM ||
-	    tt_common_entry->flags & BATADV_TT_CLIENT_TEMP)
+	entry_flags = atomic_read(&tt_common_entry->flags);
+	if (entry_flags & (BATADV_TT_CLIENT_ROAM | BATADV_TT_CLIENT_TEMP))
 		return false;
 
 	tt_global_entry = container_of(tt_common_entry,
@@ -3567,6 +3637,7 @@ bool batadv_is_my_client(struct batadv_priv *bat_priv, const u8 *addr,
 {
 	struct batadv_tt_local_entry *tt_local_entry;
 	bool ret = false;
+	u16 entry_flags;
 
 	tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
 	if (!tt_local_entry)
@@ -3574,8 +3645,8 @@ bool batadv_is_my_client(struct batadv_priv *bat_priv, const u8 *addr,
 	/* Check if the client has been logically deleted (but is kept for
 	 * consistency purpose)
 	 */
-	if ((tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) ||
-	    (tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM))
+	entry_flags = atomic_read(&tt_local_entry->common.flags);
+	if (entry_flags & (BATADV_TT_CLIENT_PENDING | BATADV_TT_CLIENT_ROAM))
 		goto out;
 	ret = true;
 out:
@@ -3850,6 +3921,8 @@ static void batadv_tt_local_set_flags(struct batadv_priv *bat_priv, u16 flags,
 	struct batadv_hashtable *hash = bat_priv->tt.local_hash;
 	struct batadv_tt_common_entry *tt_common_entry;
 	struct hlist_head *head;
+	atomic_t *entry_flags;
+	u16 old_flags;
 	u32 i;
 
 	if (!hash)
@@ -3861,14 +3934,27 @@ static void batadv_tt_local_set_flags(struct batadv_priv *bat_priv, u16 flags,
 		rcu_read_lock();
 		hlist_for_each_entry_rcu(tt_common_entry,
 					 head, hash_entry) {
+			entry_flags = &tt_common_entry->flags;
+			old_flags = atomic_read(entry_flags);
+
+			/* the old_flags value of the atomic test-and-
+			 * set/clear decides whether this entry counts as
+			 * changed.
+			 */
 			if (enable) {
-				if ((tt_common_entry->flags & flags) == flags)
+				if ((old_flags & flags) == flags)
+					continue;
+
+				old_flags = atomic_fetch_or(flags, entry_flags);
+				if ((old_flags & flags) == flags)
 					continue;
-				tt_common_entry->flags |= flags;
 			} else {
-				if (!(tt_common_entry->flags & flags))
+				if (!(old_flags & flags))
+					continue;
+
+				old_flags = atomic_fetch_andnot(flags, entry_flags);
+				if (!(old_flags & flags))
 					continue;
-				tt_common_entry->flags &= ~flags;
 			}
 
 			if (!count)
@@ -3909,7 +3995,7 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
 		spin_lock_bh(list_lock);
 		hlist_for_each_entry_safe(tt_common, node_tmp, head,
 					  hash_entry) {
-			if (!(tt_common->flags & BATADV_TT_CLIENT_PENDING))
+			if (!(atomic_read(&tt_common->flags) & BATADV_TT_CLIENT_PENDING))
 				continue;
 
 			batadv_dbg(BATADV_DBG_TT, bat_priv,
@@ -4122,7 +4208,7 @@ bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
 	if (!tt_global_entry)
 		goto out;
 
-	ret = tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM;
+	ret = atomic_read(&tt_global_entry->common.flags) & BATADV_TT_CLIENT_ROAM;
 	batadv_tt_global_entry_put(tt_global_entry);
 out:
 	return ret;
@@ -4148,7 +4234,7 @@ bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
 	if (!tt_local_entry)
 		goto out;
 
-	ret = tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM;
+	ret = atomic_read(&tt_local_entry->common.flags) & BATADV_TT_CLIENT_ROAM;
 	batadv_tt_local_entry_put(tt_local_entry);
 out:
 	return ret;
@@ -4458,7 +4544,7 @@ bool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv,
 	if (!tt)
 		return false;
 
-	ret = tt->common.flags & BATADV_TT_CLIENT_ISOLA;
+	ret = atomic_read(&tt->common.flags) & BATADV_TT_CLIENT_ISOLA;
 
 	batadv_tt_global_entry_put(tt);
 
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index be65ef33..8114c1d3 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1852,7 +1852,7 @@ struct batadv_tt_common_entry {
 	struct hlist_node hash_entry;
 
 	/** @flags: various state handling flags (see batadv_tt_client_flags) */
-	u16 flags;
+	atomic_t flags;
 
 	/** @added_at: timestamp used for purging stale tt common entries */
 	unsigned long added_at;

-- 
2.47.3


^ permalink raw reply related

* [PATCH batadv v2 0/2] batman-adv: tt: use atomic flag modifications
From: Sven Eckelmann @ 2026-06-26 16:28 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann

The flags of translation table entries are modified in various places using
RMW operations like:

* read flags + add flag + store
* read flags + remove flag + store

These were done without making sure that no other context is doing a
similar operation at the same time. If another context does modify the
flags then it could happen that a store of the flag modifications is simply
lost. This problem can usually be fixed at a later point when the flags are
tried to be adjusted again.

To reduce the time the wrong flags are used, it is better to change the u16
flags type to use an atomic_t with its atomic helper to perform these
adjustments.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
Changes in v2:
- rebase and drop already merged first fix
- remove RFC prefix
- Link to v1: https://patch.msgid.link/20260613-tt-atomic-refactor-v1-0-9f6a762c5d24@narfation.org

---
Sven Eckelmann (2):
      batman-adv: tt: use atomic flag modifications
      batman-adv: tt: simplify NEW flag transition code

 net/batman-adv/translation-table.c | 255 +++++++++++++++++++++++--------------
 net/batman-adv/types.h             |   2 +-
 2 files changed, 162 insertions(+), 95 deletions(-)
---
base-commit: 6f34546691685542e30c7f7a3a5612821e917560
change-id: 20260613-tt-atomic-refactor-9c9b23a413f2

Best regards,
--  
Sven Eckelmann <sven@narfation.org>


^ permalink raw reply

* Re: [PATCH net 01/15] batman-adv: gw: don't deselect gateway with active hardif
From: patchwork-bot+netdevbpf @ 2026-06-21 22:00 UTC (permalink / raw)
  To: Simon Wunderlich
  Cc: netdev, davem, edumazet, kuba, pabeni, horms, b.a.t.m.a.n, sven,
	stable, neocturne
In-Reply-To: <20260619070045.438101-2-sw@simonwunderlich.de>

Hello:

This series was applied to netdev/net.git (main)
by Sven Eckelmann <sven@narfation.org>:

On Fri, 19 Jun 2026 09:00:31 +0200 you wrote:
> From: Sven Eckelmann <sven@narfation.org>
> 
> 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.
> 
> [...]

Here is the summary with links:
  - [net,01/15] batman-adv: gw: don't deselect gateway with active hardif
    https://git.kernel.org/netdev/net/c/df97a7107b16
  - [net,02/15] batman-adv: ensure bcast is writable before modifying TTL
    https://git.kernel.org/netdev/net/c/4cd6d3a4b96a
  - [net,03/15] batman-adv: fix (m|b)cast csum after decrementing TTL
    https://git.kernel.org/netdev/net/c/e728bbdf3266
  - [net,04/15] batman-adv: frag: ensure fragment is writable before modifying TTL
    https://git.kernel.org/netdev/net/c/b7293c6e8c15
  - [net,05/15] batman-adv: frag: avoid underflow of TTL
    https://git.kernel.org/netdev/net/c/493d9d2528e1
  - [net,06/15] batman-adv: v: prevent OGM aggregation on disabled hardif
    https://git.kernel.org/netdev/net/c/d11c00b95b2a
  - [net,07/15] batman-adv: tp_meter: restrict number of unacked list entries
    https://git.kernel.org/netdev/net/c/e7c775110e18
  - [net,08/15] batman-adv: tp_meter: annotate last_recv_time access with READ/WRITE_ONCE
    https://git.kernel.org/netdev/net/c/d67c728f07fc
  - [net,09/15] batman-adv: tp_meter: prevent parallel modifications of last_recv
    https://git.kernel.org/netdev/net/c/6dde0cfcb36e
  - [net,10/15] batman-adv: tp_meter: handle overlapping packets
    https://git.kernel.org/netdev/net/c/cbde75c38b21
  - [net,11/15] batman-adv: tt: don't merge change entries with different VIDs
    https://git.kernel.org/netdev/net/c/f08e06c2d5c3
  - [net,12/15] batman-adv: tt: track roam count per VID
    https://git.kernel.org/netdev/net/c/12407d5f61c2
  - [net,13/15] batman-adv: dat: prevent false sharing between VLANs
    https://git.kernel.org/netdev/net/c/20d7658b7416
  - [net,14/15] batman-adv: tvlv: enforce 2-byte alignment
    https://git.kernel.org/netdev/net/c/32a679925552
  - [net,15/15] batman-adv: tvlv: avoid race of cifsnotfound handler state
    https://git.kernel.org/netdev/net/c/edb557b2ba38

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* [PATCH 28/28] batctl: bisect_iv: drop dangling phantom event on OOM error
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

When routing_table_new() fails to allocate rt_table->entries, it might
alrady have allocated the seqno_event for RT_FLAG_DELETE. This seqno_event
is also already part of the orig_event->event_list.

To avoid this dangling DELETE event, it must also be unlinked and freed.

Fixes: ece05e1c4c1f ("[batctl] bisect (a tool to analyze logfiles) added")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 bisect_iv.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/bisect_iv.c b/bisect_iv.c
index 6c3f5d6..43b97e4 100644
--- a/bisect_iv.c
+++ b/bisect_iv.c
@@ -187,8 +187,8 @@ static void node_free(void *data)
 
 static int routing_table_new(char *orig, char *next_hop, char *old_next_hop, char rt_flag)
 {
+	struct seqno_event *seqno_event = NULL;
 	struct rt_table *prev_rt_table = NULL;
-	struct seqno_event *seqno_event;
 	struct bat_node *next_hop_node;
 	struct orig_event *orig_event;
 	struct rt_table *rt_table;
@@ -334,6 +334,10 @@ static int routing_table_new(char *orig, char *next_hop, char *old_next_hop, cha
 	if (!rt_table->entries) {
 		fprintf(stderr,
 			"Could not allocate memory for routing table entries (out of mem?) - skipping");
+		if (rt_flag == RT_FLAG_DELETE && seqno_event) {
+			list_del(&seqno_event->list);
+			free(seqno_event);
+		}
 		goto rt_hist_free;
 	}
 

-- 
2.47.3


^ permalink raw reply related

* [PATCH 27/28] batctl: bisect_iv: avoid out of bound access on short lines
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

The parse_log_file assumes that it can jump over the first 13 characters
without checking if the line is actually 13 bytes long. A short log line
would therefore cause a jump outside of the initialized buffer area.

To avoid this, ignore all files smaller than 14 bytes.

Fixes: ece05e1c4c1f ("[batctl] bisect (a tool to analyze logfiles) added")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 bisect_iv.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/bisect_iv.c b/bisect_iv.c
index 4420be9..6c3f5d6 100644
--- a/bisect_iv.c
+++ b/bisect_iv.c
@@ -501,10 +501,14 @@ static int parse_log_file(char *file_path)
 	}
 
 	while (fgets(line_buff, sizeof(line_buff), fd)) {
-		/* ignore the timestamp at the beginning of each line */
-		start_ptr = line_buff + 13;
 		line_count++;
 
+		/* ignore the timestamp at the beginning of each line */
+		if (strlen(line_buff) <= 13)
+			continue;
+
+		start_ptr = line_buff + 13;
+
 		if (strstr(start_ptr, "Received BATMAN packet via NB")) {
 			strtok_r(start_ptr, " ", &start_ptr_safe);
 			neigh = NULL;

-- 
2.47.3


^ permalink raw reply related

* [PATCH 26/28] batctl: bisect_iv: avoid write before buffer whole tokenizing log
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

When neigh and prev_sender are tokenized, their string length is removed by
2 characters. But strtok_r only guarantees 1 character. A malformed log
would therefore cause a write before the start of the actual data.

Fixes: c3b15dbec883 ("[batctl] bisect - better routing table handling due to add/update/delete detection")
Fixes: 8a3d4fed1067 ("[batctl] bisect - fix handling of large bat-host files")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 bisect_iv.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/bisect_iv.c b/bisect_iv.c
index 0e34d37..4420be9 100644
--- a/bisect_iv.c
+++ b/bisect_iv.c
@@ -593,7 +593,8 @@ static int parse_log_file(char *file_path)
 				case 4:
 					if (rt_flag == RT_FLAG_ADD) {
 						neigh = tok_ptr;
-						neigh[strlen(neigh) - 2] = 0;
+						if (strlen(neigh) >= 2)
+							neigh[strlen(neigh) - 2] = 0;
 					}
 					break;
 				case 5:
@@ -601,7 +602,8 @@ static int parse_log_file(char *file_path)
 					break;
 				case 9:
 					prev_sender = tok_ptr;
-					prev_sender[strlen(prev_sender) - 2] = 0;
+					if (strlen(prev_sender) >= 2)
+						prev_sender[strlen(prev_sender) - 2] = 0;
 					break;
 				}
 			}

-- 
2.47.3


^ permalink raw reply related

* [PATCH 25/28] batctl: bisect_iv: handle failed node_get for RT_FLAG_DELETE
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

When a node could not be found or allocated, then node_get() will return
NULL. If this is not checked then it will cause NULL-ptr dereferences in
the seqno_event->orig is accessed.

Fixes: 700749a4c2f4 ("[batctl] add recursive check for routing loops")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 bisect_iv.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/bisect_iv.c b/bisect_iv.c
index f1ba94e..0e34d37 100644
--- a/bisect_iv.c
+++ b/bisect_iv.c
@@ -311,6 +311,11 @@ static int routing_table_new(char *orig, char *next_hop, char *old_next_hop, cha
 			}
 
 			seqno_event->orig = node_get(orig);
+			if (!seqno_event->orig) {
+				free(seqno_event);
+				goto rt_hist_free;
+			}
+
 			seqno_event->neigh = NULL;
 			seqno_event->prev_sender = NULL;
 			seqno_event->seqno = -1;

-- 
2.47.3


^ permalink raw reply related

* [PATCH 24/28] batctl: bisect_iv: avoid access of uninitialized loop magic
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

The loop magic string is initialized with snprintf - which can be a partial
initialization. But the rest of the access assumes that all of the loop
magic is initialized because it is then only access with memcmp and memcpy.

Initialize everything to 0 at the beginning of the function to avoid
uninitialized bytes.

Fixes: ece05e1c4c1f ("[batctl] bisect (a tool to analyze logfiles) added")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 bisect_iv.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/bisect_iv.c b/bisect_iv.c
index 5da5bf1..f1ba94e 100644
--- a/bisect_iv.c
+++ b/bisect_iv.c
@@ -658,7 +658,7 @@ static int print_rt_path_at_seqno(struct bat_node *src_node, struct bat_node *ds
 				  struct bat_node *next_hop, long long seqno,
 				  long long seqno_rand, int read_opt)
 {
-	char curr_loop_magic[LOOP_MAGIC_LEN];
+	char curr_loop_magic[LOOP_MAGIC_LEN] = {};
 	struct bat_node *next_hop_tmp;
 	struct orig_event *orig_event;
 	struct rt_hist *rt_hist;
@@ -720,7 +720,7 @@ static int find_rt_table_change(struct bat_node *src_node, struct bat_node *dst_
 				struct bat_node *curr_node, long long seqno_min,
 				long long seqno_max, long long seqno_rand, int read_opt)
 {
-	char curr_loop_magic[LOOP_MAGIC_LEN];
+	char curr_loop_magic[LOOP_MAGIC_LEN] = {};
 	long long seqno_min_tmp = seqno_min;
 	struct orig_event *orig_event;
 	struct rt_hist *rt_hist_tmp;

-- 
2.47.3


^ permalink raw reply related

* [PATCH 23/28] batctl: mark file read only succesful on read line
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

The line_ptr is shared globally. It can happen that another function like
parse_hosts_file() allocated the buffer successfully. But the next
getline() in read_file() fails - but keeps the line_ptr valid. In this
case, the function would return a success - even when the buffer contains
stale data.

Instead only set the return value to EXIT_SUCCESS when a single line could
be read.

Fixes: deb324e65044 ("batctl: buffer based reading replaced by line-by-line reading")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 functions.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/functions.c b/functions.c
index ae1c5e2..bb0dc18 100644
--- a/functions.c
+++ b/functions.c
@@ -147,6 +147,8 @@ int read_file(const char *full_path, int read_opt)
 	}
 
 	while (getline(&line_ptr, &len, fp) != -1) {
+		res = EXIT_SUCCESS;
+
 		/* the buffer will be handled elsewhere */
 		if (read_opt & USE_READ_BUFF)
 			break;
@@ -154,9 +156,6 @@ int read_file(const char *full_path, int read_opt)
 		printf("%s", line_ptr);
 	}
 
-	if (line_ptr)
-		res = EXIT_SUCCESS;
-
 	fclose(fp);
 	return res;
 }

-- 
2.47.3


^ permalink raw reply related

* [PATCH 22/28] batct: bat-hosts: compare full path for deduplication
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

The deduplication code uses realpath to store the real path of a provided
configuration file. The code then tries to compare these paths but
accidentally only comparse only the first CONF_DIR_LEN (256) bytes of the
path.

The number of bytes must be set to the same length as the path size slot in
the normalized buffer.

Fixes: db97c2a77e81 ("batctl: fix crash in bat-hosts parser on embedded systems")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 bat-hosts.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/bat-hosts.c b/bat-hosts.c
index 57e7d69..6ad8247 100644
--- a/bat-hosts.c
+++ b/bat-hosts.c
@@ -203,7 +203,7 @@ void bat_hosts_init(int read_opt)
 		for (j = 0; j < i; j++) {
 			if (strncmp(normalized + (i * PATH_MAX),
 				    normalized + (j * PATH_MAX),
-				    CONF_DIR_LEN) == 0) {
+				    PATH_MAX) == 0) {
 				parse = 0;
 				break;
 			}

-- 
2.47.3


^ permalink raw reply related

* [PATCH 21/28] batctl: rtnl: report correct error on kernel communication problem
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

When nl_send_auto_complete() fails, an explicit error must be set or
otherwise query_rtnl_link() will return 0 (no error).

Fixes: 60e519bfeaa3 ("batctl: Use rtnl to query list of softif devices")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 functions.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/functions.c b/functions.c
index dc6bf75..ae1c5e2 100644
--- a/functions.c
+++ b/functions.c
@@ -493,8 +493,10 @@ int query_rtnl_link(int ifindex, nl_recvmsg_msg_cb_t func, void *arg)
 	}
 
 	ret = nl_send_auto_complete(sock, msg);
-	if (ret < 0)
+	if (ret < 0) {
+		err = -EIO;
 		goto err_free_msg;
+	}
 
 	nl_recvmsgs(sock, cb);
 

-- 
2.47.3


^ permalink raw reply related

* [PATCH 20/28] batctl: version: don't strip newline for empty buffer
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

When read_file() would return an empty buffer, then version() must not
strip the last byte. Otherwise it would try to access 1 byte before the
start of the buffer.

Fixes: dbc4a8c8e585 ("batctl: version also prints the kernel module version if available")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/main.c b/main.c
index 79ed4ef..94db4a1 100644
--- a/main.c
+++ b/main.c
@@ -132,7 +132,7 @@ static void version(void)
 	printf("batctl %s [batman-adv: ", SOURCE_VERSION);
 
 	ret = read_file(module_ver_path, USE_READ_BUFF | SILENCE_ERRORS);
-	if ((line_ptr) && (line_ptr[strlen(line_ptr) - 1] == '\n'))
+	if (line_ptr && strlen(line_ptr) > 0 && line_ptr[strlen(line_ptr) - 1] == '\n')
 		line_ptr[strlen(line_ptr) - 1] = '\0';
 
 	if (ret == EXIT_SUCCESS)

-- 
2.47.3


^ permalink raw reply related

* [PATCH 19/28] batctl: netlink: always 0-terminate hardif name
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

The kernel should not send non-0 terminated interface name name or an
interface name larger than IFNAMSIZ. Just to be on the safe side, still
force the last byte to the 0-delimiter.

Fixes: 426e48c8d9ca ("batctl: ping: Get outgoing ifname from netlink")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 netlink.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/netlink.c b/netlink.c
index 3a1c3a5..14100c3 100644
--- a/netlink.c
+++ b/netlink.c
@@ -822,7 +822,8 @@ static int get_nexthop_netlink_cb(struct nl_msg *msg, void *arg)
 
 	if (attrs[BATADV_ATTR_HARD_IFNAME]) {
 		ifname = nla_get_string(attrs[BATADV_ATTR_HARD_IFNAME]);
-		strncpy(opts->ifname, ifname, IFNAMSIZ);
+		strncpy(opts->ifname, ifname, IFNAMSIZ - 1);
+		opts->ifname[IFNAMSIZ - 1] = '\0';
 	} else {
 		/* compatibility for Linux < 5.14/batman-adv < 2021.2 */
 		ifname = if_indextoname(index, opts->ifname);

-- 
2.47.3


^ permalink raw reply related

* [PATCH 18/28] batctl: netlink: consume until acks even on errors
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

THe sys_simple_nlquery() function is used on a shared socket. The data
until the ack must therefore _always_ be consumed to avoid receive
confusions when another command is started on the same socket.

Fixes: 0b81e8fbaed5 ("batctl: Consume genl ACKs after setting reads")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 netlink.c | 8 +-------
 sys.c     | 2 +-
 2 files changed, 2 insertions(+), 8 deletions(-)

diff --git a/netlink.c b/netlink.c
index be92a9c..3a1c3a5 100644
--- a/netlink.c
+++ b/netlink.c
@@ -466,7 +466,6 @@ char *netlink_get_info(struct state *state, uint8_t nl_cmd, const char *header)
 	};
 	struct nl_msg *msg;
 	struct nl_cb *cb;
-	int ret;
 
 	msg = nlmsg_alloc();
 	if (!msg)
@@ -488,12 +487,7 @@ char *netlink_get_info(struct state *state, uint8_t nl_cmd, const char *header)
 	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, info_callback, &opts);
 	nl_cb_err(cb, NL_CB_CUSTOM, netlink_print_error, NULL);
 
-	ret = nl_recvmsgs(state->sock, cb);
-	if (ret < 0) {
-		nl_cb_put(cb);
-		return opts.remaining_header;
-	}
-
+	nl_recvmsgs(state->sock, cb);
 	nl_wait_for_ack(state->sock);
 
 	nl_cb_put(cb);
diff --git a/sys.c b/sys.c
index 35a026e..3018c09 100644
--- a/sys.c
+++ b/sys.c
@@ -110,7 +110,7 @@ int sys_simple_nlquery(struct state *state, enum batadv_nl_commands nl_cmd,
 	if (callback) {
 		ret = nl_recvmsgs(state->sock, state->cb);
 		if (ret < 0)
-			return ret;
+			result = ret;
 	}
 
 	nl_wait_for_ack(state->sock);

-- 
2.47.3


^ permalink raw reply related

* [PATCH 17/28] batctl: netlink: check for BATADV_ATTR_MESH_ADDRESS before accessing it
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

info_callback expects the BATADV_ATTR_MESH_ADDRESS attribute to be set. But
it never checked if it is not NULL.

Fixes: d8dd1ff1a0fe ("batctl: Use netlink to replace some of debugfs")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 netlink.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/netlink.c b/netlink.c
index 5f78602..be92a9c 100644
--- a/netlink.c
+++ b/netlink.c
@@ -325,6 +325,7 @@ int netlink_stop_callback(struct nl_msg *msg, void *arg __maybe_unused)
 static const int info_mandatory[] = {
 	BATADV_ATTR_MESH_IFINDEX,
 	BATADV_ATTR_MESH_IFNAME,
+	BATADV_ATTR_MESH_ADDRESS,
 };
 
 static const int info_hard_mandatory[] = {

-- 
2.47.3


^ permalink raw reply related

* [PATCH 16/28] batctl: handle netlink callback object on error
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

The libnl callback object is allocated and must be deallocated when it is
no longer used. This is especially important when an error happened.

Fixes: d8dd1ff1a0fe ("batctl: Use netlink to replace some of debugfs")
Fixes: f109b3473f86 ("batctl: introduce throughput meter support")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 netlink.c         |  6 +++++-
 routing_algo.c    |  1 +
 throughputmeter.c | 10 +++++++++-
 3 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/netlink.c b/netlink.c
index 1121956..5f78602 100644
--- a/netlink.c
+++ b/netlink.c
@@ -488,11 +488,15 @@ char *netlink_get_info(struct state *state, uint8_t nl_cmd, const char *header)
 	nl_cb_err(cb, NL_CB_CUSTOM, netlink_print_error, NULL);
 
 	ret = nl_recvmsgs(state->sock, cb);
-	if (ret < 0)
+	if (ret < 0) {
+		nl_cb_put(cb);
 		return opts.remaining_header;
+	}
 
 	nl_wait_for_ack(state->sock);
 
+	nl_cb_put(cb);
+
 	return opts.remaining_header;
 }
 
diff --git a/routing_algo.c b/routing_algo.c
index 1c2c2b4..167bd22 100644
--- a/routing_algo.c
+++ b/routing_algo.c
@@ -107,6 +107,7 @@ static int print_routing_algos(struct state *state)
 	nl_cb_err(cb, NL_CB_CUSTOM, netlink_print_error, NULL);
 
 	nl_recvmsgs(state->sock, cb);
+	nl_cb_put(cb);
 
 	if (!last_err) {
 		netlink_print_remaining_header(&opts);
diff --git a/throughputmeter.c b/throughputmeter.c
index a45eb08..23a6027 100644
--- a/throughputmeter.c
+++ b/throughputmeter.c
@@ -158,13 +158,18 @@ static int tp_meter_start(struct state *state, struct ether_addr *dst_mac,
 	int err = 0;
 
 	cb = nl_cb_alloc(NL_CB_DEFAULT);
+	if (!cb)
+		return -ENOMEM;
+
 	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, tp_meter_cookie_callback,
 		  cookie);
 	nl_cb_err(cb, NL_CB_CUSTOM, tpmeter_nl_print_error, cookie);
 
 	msg = nlmsg_alloc();
-	if (!msg)
+	if (!msg) {
+		nl_cb_put(cb);
 		return -ENOMEM;
+	}
 
 	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, state->batadv_family, 0,
 		    0, BATADV_CMD_TP_METER, 1);
@@ -200,6 +205,9 @@ static int tp_recv_result(struct nl_sock *sock, struct tp_result *result)
 	int err = 0;
 
 	cb = nl_cb_alloc(NL_CB_DEFAULT);
+	if (!cb)
+		return -ENOMEM;
+
 	nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
 	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, tp_meter_result_callback,
 		  result);

-- 
2.47.3


^ permalink raw reply related

* [PATCH 15/28] batctl: tpmeter: fix Gbps output
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

The formatter for the Gbps result should print 2 digits after the dot. But
the format specifier was different than in all other branches and didn't
specify the precision after the dot. Instead a minimal number of digits (or
spaces) before the dot was defined.

Fixes: f109b3473f86 ("batctl: introduce throughput meter support")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 throughputmeter.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/throughputmeter.c b/throughputmeter.c
index d670c51..a45eb08 100644
--- a/throughputmeter.c
+++ b/throughputmeter.c
@@ -431,7 +431,7 @@ static int throughputmeter(struct state *state, int argc, char **argv)
 		if (throughput == UINT64_MAX)
 			printf("inf\n");
 		else if (throughput > (1UL << 30))
-			printf("%.2f GB/s (%2.f Gbps)\n",
+			printf("%.2f GB/s (%.2f Gbps)\n",
 			       (float)throughput / (1 << 30),
 			       (float)throughput * 8 / 1000000000);
 		else if (throughput > (1UL << 20))

-- 
2.47.3


^ permalink raw reply related

* [PATCH 14/28] batctl: tcpdump: handle TCP packet with bogus data offset
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

dump_tcp() computes length as (buff_len - ip6_header_len - tcp_header_len),
but the only bounds guard ensures 20 bytes, while doff allows a higher
header length (60 bytes). With a doff of 15 (60 bytes) and only 20 bytes
available in the buffer, the calculation would underflow and show a bugus
length of the TCP payload. For now, set the payload length to zero for such
a packet.

Fixes: 35b37756f4a3 ("add IPv6 support to tcpdump parser")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 tcpdump.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tcpdump.c b/tcpdump.c
index 9c90f36..7769cbf 100644
--- a/tcpdump.c
+++ b/tcpdump.c
@@ -537,18 +537,20 @@ static void dump_tcp(const char ip_string[], unsigned char *packet_buff,
 {
 	uint16_t tcp_header_len;
 	struct tcphdr *tcphdr;
+	size_t tcp_len;
 
 	LEN_CHECK((size_t)buff_len - ip6_header_len,
 		  sizeof(struct tcphdr), "TCP");
 	tcphdr = (struct tcphdr *)(packet_buff + ip6_header_len);
 	tcp_header_len = tcphdr->doff * 4;
+	tcp_len = (size_t)buff_len - ip6_header_len;
 	printf("%s %s.%i > ", ip_string, src_addr, ntohs(tcphdr->source));
 	printf("%s.%i: TCP, Flags [%c%c%c%c%c%c], length %zu\n",
 	       dst_addr, ntohs(tcphdr->dest),
 	       (tcphdr->fin ? 'F' : '.'), (tcphdr->syn ? 'S' : '.'),
 	       (tcphdr->rst ? 'R' : '.'), (tcphdr->psh ? 'P' : '.'),
 	       (tcphdr->ack ? 'A' : '.'), (tcphdr->urg ? 'U' : '.'),
-	       (size_t)buff_len - ip6_header_len - tcp_header_len);
+	       tcp_len > tcp_header_len ? tcp_len - tcp_header_len : 0);
 }
 
 static void dump_udp(const char ip_string[], unsigned char *packet_buff,

-- 
2.47.3


^ permalink raw reply related

* [PATCH 13/28] batctl: tcpdump: fix reported length for ICMP6_TIME_EXCEEDED
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

The remaining buffer size for ICMP6_TIME_EXCEEDED is only the buffer length
minus the IPv6 header. It could now be discussed whether the output length
should now be with or without the icmp6_hdr length. The length with
icmp6_hdr was chosen because the code looks like a simple copy and paste
error and ip6_hdr's length was most likely the actual intent for the
calculation.

Fixes: 35b37756f4a3 ("add IPv6 support to tcpdump parser")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 tcpdump.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tcpdump.c b/tcpdump.c
index 0895c61..9c90f36 100644
--- a/tcpdump.c
+++ b/tcpdump.c
@@ -669,7 +669,7 @@ static void dump_ipv6(unsigned char *packet_buff, ssize_t buff_len,
 			break;
 		case ICMP6_TIME_EXCEEDED:
 			printf(" time exceeded in-transit, length %zu\n",
-			       (size_t)buff_len - sizeof(struct icmp6_hdr));
+			       (size_t)buff_len - sizeof(struct ip6_hdr));
 			break;
 		case ND_NEIGHBOR_SOLICIT:
 			LEN_CHECK((size_t)buff_len - (size_t)(sizeof(struct ip6_hdr)),

-- 
2.47.3


^ permalink raw reply related

* [PATCH 12/28] batctl: tcpdump: drop hardcoded IPv6 buffer sizes
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

The inet_ntop expects to know the dst size buffer. But instead of
specifying how to calculate the size of this buffer, just a value was
hardcoded - which might not fulfill the "INET6_ADDRSTRLEN" size
requirements.

Evaluate the size of the buffer at compile time to avoid potential
discrepancies.

Fixes: 35b37756f4a3 ("add IPv6 support to tcpdump parser")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 tcpdump.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tcpdump.c b/tcpdump.c
index e417cd1..0895c61 100644
--- a/tcpdump.c
+++ b/tcpdump.c
@@ -676,7 +676,7 @@ static void dump_ipv6(unsigned char *packet_buff, ssize_t buff_len,
 				  sizeof(*nd_neigh_sol), "ICMPv6 Neighbor Solicitation");
 			nd_neigh_sol = (struct nd_neighbor_solicit *)icmphdr;
 			inet_ntop(AF_INET6, &nd_neigh_sol->nd_ns_target,
-				  nd_nas_target, 40);
+				  nd_nas_target, sizeof(nd_nas_target));
 			printf(" neighbor solicitation, who has %s, length %zd\n",
 			       nd_nas_target, buff_len);
 			break;
@@ -685,7 +685,7 @@ static void dump_ipv6(unsigned char *packet_buff, ssize_t buff_len,
 				  sizeof(*nd_advert), "ICMPv6 Neighbor Advertisement");
 			nd_advert = (struct nd_neighbor_advert *)icmphdr;
 			inet_ntop(AF_INET6, &nd_advert->nd_na_target,
-				  nd_nas_target, 40);
+				  nd_nas_target, sizeof(nd_nas_target));
 			printf(" neighbor advertisement, tgt is %s, length %zd\n",
 			       nd_nas_target, buff_len);
 			break;

-- 
2.47.3


^ permalink raw reply related

* [PATCH 11/28] batctl: tcpdump: correct output of VLAN IDs
From: Sven Eckelmann @ 2026-06-21 14:24 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann
In-Reply-To: <20260621-bugfixes-v1-0-b09736288a4a@narfation.org>

The 16 bit TCI header for VLAN tagging (IEEE 802.1Q) stores 12 bit for the
12 bit VID, 1 bit DEI and 3 bit PCP. It is therefore unexpected to print
the 16 bit as vlan identifier without masking out the upper 4 bit.

At the same time, also split the DEI ( Drop Eligible Indicator) and the PCP
(Priority Code Point) in separate output fields.

Fixes: 7738b534fb07 ("batctl: tcpdump - add vlan support")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 tcpdump.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tcpdump.c b/tcpdump.c
index ddcee03..e417cd1 100644
--- a/tcpdump.c
+++ b/tcpdump.c
@@ -823,7 +823,8 @@ static void dump_vlan(unsigned char *packet_buff, ssize_t buff_len, int read_opt
 		time_printed = print_time();
 
 	vlanhdr->vid = ntohs(vlanhdr->vid);
-	printf("vlan %u, p %u, ", vlanhdr->vid, vlanhdr->vid >> 12);
+	printf("vlan %u, d %u, p %u, ", vlanhdr->vid & 0x0fff, (vlanhdr->vid >> 12) & 0x1,
+	       vlanhdr->vid >> 13);
 
 	/* overwrite vlan tags */
 	memmove(packet_buff + 4, packet_buff, 2 * ETH_ALEN);

-- 
2.47.3


^ permalink raw reply related


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