[PATCH batadv v3 1/5] batman-adv: limit number of parallel learned neighbors
Sven Eckelmann <[email protected]> Fri, 24 Jul 2026 08:51:38 +0200
| Newsgroups | org.open-mesh.lists.batman |
|---|---|
| Message-ID | <[email protected]> |
A malicious actor behind one hardif may spam the kernel with OGMs using a random source MAC address, each of which will create a neighbor node 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 neigh_learned, and a limit in neigh_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/mesh-interface.c | 3 +++ net/batman-adv/netlink.c | 10 ++++++++++ net/batman-adv/originator.c | 19 +++++++++++++++++-- net/batman-adv/types.h | 9 +++++++++ 5 files changed, 45 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h index 936bcac2..addb6092 100644 --- a/include/uapi/linux/batman_adv.h +++ b/include/uapi/linux/batman_adv.h @@ -481,6 +481,12 @@ enum batadv_nl_attrs { */ BATADV_ATTR_MULTICAST_FANOUT, + /** + * @BATADV_ATTR_NEIGH_MAX_LEARNED: defines the maximum number of neighbors + * which can be learned in parallel + */ + BATADV_ATTR_NEIGH_MAX_LEARNED, + /* add attributes above here, update the policy in netlink.c */ /** diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index f1bfe2a5..70e13150 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -846,6 +846,9 @@ static int batadv_meshif_init_late(struct net_device *dev) #endif atomic_set(&bat_priv->tp_num, 0); + atomic_set(&bat_priv->neigh_learned, 0); + WRITE_ONCE(bat_priv->neigh_max_learned, 0); + WRITE_ONCE(bat_priv->tt.local_changes, 0); bat_priv->tt.last_changeset = NULL; bat_priv->tt.last_changeset_len = 0; diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index bb1ef3ee..64bec41a 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -154,6 +154,7 @@ static const struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = { [BATADV_ATTR_ORIG_INTERVAL] = { .type = NLA_U32 }, [BATADV_ATTR_ELP_INTERVAL] = { .type = NLA_U32 }, [BATADV_ATTR_THROUGHPUT_OVERRIDE] = { .type = NLA_U32 }, + [BATADV_ATTR_NEIGH_MAX_LEARNED] = { .type = NLA_U32 }, }; /** @@ -355,6 +356,10 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg, READ_ONCE(bat_priv->orig_interval))) goto nla_put_failure; + if (nla_put_u32(msg, BATADV_ATTR_NEIGH_MAX_LEARNED, + READ_ONCE(bat_priv->neigh_max_learned))) + goto nla_put_failure; + batadv_hardif_put(primary_if); genlmsg_end(msg, hdr); @@ -600,6 +605,11 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) WRITE_ONCE(bat_priv->orig_interval, orig_interval); } + if (info->attrs[BATADV_ATTR_NEIGH_MAX_LEARNED]) { + WRITE_ONCE(bat_priv->neigh_max_learned, + nla_get_u32(info->attrs[BATADV_ATTR_NEIGH_MAX_LEARNED])); + } + batadv_netlink_notify_mesh(bat_priv); return 0; diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index f06583ef..d6a5bd7c 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -299,6 +299,7 @@ void batadv_neigh_node_release(struct kref *ref) batadv_hardif_put(neigh_node->if_incoming); + atomic_dec(&neigh_node->bat_priv->neigh_learned); kfree_rcu(neigh_node, rcu); } @@ -675,6 +676,8 @@ batadv_neigh_node_create(struct batadv_orig_node *orig_node, { struct batadv_hardif_neigh_node *hardif_neigh = NULL; struct batadv_neigh_node *neigh_node; + u32 neigh_max_learned; + int neigh_learned; spin_lock_bh(&orig_node->neigh_list_lock); @@ -682,14 +685,25 @@ batadv_neigh_node_create(struct batadv_orig_node *orig_node, if (neigh_node) goto out; + neigh_max_learned = READ_ONCE(orig_node->bat_priv->neigh_max_learned); + neigh_learned = atomic_inc_return(&orig_node->bat_priv->neigh_learned); + if (neigh_max_learned && neigh_learned > neigh_max_learned) { + atomic_dec(&orig_node->bat_priv->neigh_learned); + goto out; + } + hardif_neigh = batadv_hardif_neigh_get_or_create(hard_iface, neigh_addr, orig_node); - if (!hardif_neigh) + if (!hardif_neigh) { + atomic_dec(&orig_node->bat_priv->neigh_learned); goto out; + } neigh_node = kzalloc_obj(*neigh_node, GFP_ATOMIC); - if (!neigh_node) + if (!neigh_node) { + atomic_dec(&orig_node->bat_priv->neigh_learned); goto out; + } INIT_HLIST_NODE(&neigh_node->list); INIT_HLIST_HEAD(&neigh_node->ifinfo_list); @@ -699,6 +713,7 @@ batadv_neigh_node_create(struct batadv_orig_node *orig_node, ether_addr_copy(neigh_node->addr, neigh_addr); neigh_node->if_incoming = hard_iface; neigh_node->last_seen = jiffies; + neigh_node->bat_priv = orig_node->bat_priv; #ifdef CONFIG_BATMAN_ADV_BATMAN_V ACCESS_PRIVATE(neigh_node, orig_node_id) = orig_node; diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index d6e15d46..e404a6b4 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -679,6 +679,9 @@ struct batadv_neigh_node { /** @addr: the MAC address of the neighboring interface */ u8 addr[ETH_ALEN]; + /** @bat_priv: pointer to the mesh object */ + struct batadv_priv *bat_priv; + /** @ifinfo_list: list for routing metrics per outgoing interface */ struct hlist_head ifinfo_list; @@ -1700,6 +1703,12 @@ struct batadv_priv { /** @meshif_vlan_list_lock: lock protecting meshif_vlan_list */ spinlock_t meshif_vlan_list_lock; + /** @neigh_max_learned: Maximum number of neighbors */ + u32 neigh_max_learned; + + /** @neigh_learned: current number of learned neighbors */ + atomic_t neigh_learned; + #ifdef CONFIG_BATMAN_ADV_BLA /** @bla: bridge loop avoidance data */ struct batadv_priv_bla bla; -- 2.47.3