[PATCH batadv v3 3/5] batman-adv: limit number of parallel learned DAT entries
Sven Eckelmann <[email protected]> Fri, 24 Jul 2026 08:51:40 +0200
| Newsgroups | org.open-mesh.lists.batman |
|---|---|
| Message-ID | <[email protected]> |
A malicious actor behind one bridge port may spam the kernel with ARP/DHCP using a random MAC address, causing the creation of a DAT entry, a dynamic allocation in the kernel. This will at some point exhaust the available memory. Mitigate this by maintaining a per meshif count of those automatically generated entries in dat.learned, and a limit in dat.max_learned. If the limit is hit, new entries are not learned anymore. For backwards compatibility, the default setting of 0 disables the limit. Signed-off-by: Sven Eckelmann <[email protected]> --- include/uapi/linux/batman_adv.h | 6 ++++++ net/batman-adv/distributed-arp-table.c | 17 +++++++++++++++-- net/batman-adv/mesh-interface.c | 3 +++ net/batman-adv/netlink.c | 10 ++++++++++ net/batman-adv/types.h | 6 ++++++ 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h index 325cd687..184f2bc8 100644 --- a/include/uapi/linux/batman_adv.h +++ b/include/uapi/linux/batman_adv.h @@ -493,6 +493,12 @@ enum batadv_nl_attrs { */ BATADV_ATTR_ORIG_MAX_LEARNED, + /** + * @BATADV_ATTR_DAT_MAX_LEARNED: defines the maximum number of DAT entries + * which can be learned in parallel + */ + BATADV_ATTR_DAT_MAX_LEARNED, + /* add attributes above here, update the policy in netlink.c */ /** diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index bedd0cc7..9820a783 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -248,6 +248,7 @@ static void __batadv_dat_purge(struct batadv_priv *bat_priv, hlist_del_rcu(&dat_entry->hash_entry); batadv_dat_entry_put(dat_entry); + atomic_dec(&bat_priv->dat.learned); } spin_unlock_bh(list_lock); } @@ -446,6 +447,8 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip, { u64 u64_mac = ether_addr_to_u64(mac_addr); struct batadv_dat_entry *dat_entry; + u32 dat_max_learned; + int dat_learned; int hash_added; dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid); @@ -461,9 +464,18 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip, goto out; } - dat_entry = kmalloc_obj(*dat_entry, GFP_ATOMIC); - if (!dat_entry) + dat_max_learned = READ_ONCE(bat_priv->dat.max_learned); + dat_learned = atomic_inc_return(&bat_priv->dat.learned); + if (dat_max_learned && dat_learned > dat_max_learned) { + atomic_dec(&bat_priv->dat.learned); goto out; + } + + dat_entry = kmalloc_obj(*dat_entry, GFP_ATOMIC); + if (!dat_entry) { + atomic_dec(&bat_priv->dat.learned); + goto out; + } dat_entry->ip = ip; dat_entry->vid = vid; @@ -479,6 +491,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip, if (unlikely(hash_added != 0)) { /* remove the reference for the hash */ batadv_dat_entry_put(dat_entry); + atomic_dec(&bat_priv->dat.learned); goto out; } diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index e9155ff3..a34fdd41 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -814,6 +814,9 @@ static int batadv_meshif_init_late(struct net_device *dev) #endif #ifdef CONFIG_BATMAN_ADV_DAT WRITE_ONCE(bat_priv->distributed_arp_table, 1); + + atomic_set(&bat_priv->dat.learned, 0); + WRITE_ONCE(bat_priv->dat.max_learned, 0); #endif #ifdef CONFIG_BATMAN_ADV_MCAST WRITE_ONCE(bat_priv->multicast_mode, 1); diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index 41ed7aaf..d146d7c7 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -156,6 +156,7 @@ static const struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = { [BATADV_ATTR_THROUGHPUT_OVERRIDE] = { .type = NLA_U32 }, [BATADV_ATTR_NEIGH_MAX_LEARNED] = { .type = NLA_U32 }, [BATADV_ATTR_ORIG_MAX_LEARNED] = { .type = NLA_U32 }, + [BATADV_ATTR_DAT_MAX_LEARNED] = { .type = NLA_U32 }, }; /** @@ -305,6 +306,10 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg, if (nla_put_u8(msg, BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED, !!READ_ONCE(bat_priv->distributed_arp_table))) goto nla_put_failure; + + if (nla_put_u32(msg, BATADV_ATTR_DAT_MAX_LEARNED, + READ_ONCE(bat_priv->dat.max_learned))) + goto nla_put_failure; #endif /* CONFIG_BATMAN_ADV_DAT */ if (nla_put_u8(msg, BATADV_ATTR_FRAGMENTATION_ENABLED, @@ -495,6 +500,11 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) !!nla_get_u8(attr)); batadv_dat_status_update(bat_priv->mesh_iface); } + + if (info->attrs[BATADV_ATTR_DAT_MAX_LEARNED]) { + WRITE_ONCE(bat_priv->dat.max_learned, + nla_get_u32(info->attrs[BATADV_ATTR_DAT_MAX_LEARNED])); + } #endif /* CONFIG_BATMAN_ADV_DAT */ if (info->attrs[BATADV_ATTR_FRAGMENTATION_ENABLED]) { diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 66096bfe..6001273b 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1200,6 +1200,12 @@ struct batadv_priv_dat { /** @work: work queue callback item for cache purging */ struct delayed_work work; + + /** @max_learned: Maximum number of DAT entries */ + u32 max_learned; + + /** @learned: current number of learned DAT entries */ + atomic_t learned; }; #endif -- 2.47.3