[PATCH batadv v3 4/5] batman-adv: limit number of parallel learned BLA backbones
Sven Eckelmann <[email protected]> Fri, 24 Jul 2026 08:51:41 +0200
| Newsgroups | org.open-mesh.lists.batman |
|---|---|
| Message-ID | <[email protected]> |
A malicious actor on the backbone Ethernet may spam the kernel with BLA announcements using a random source MAC address and a valid group ID (local group ID, or one that exists on the mesh), each of which will create a BLA backbone, 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 bla.backbone_learned, and a limit in bla.backbone_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/bridge_loop_avoidance.c | 25 ++++++++++++++++++++++++- net/batman-adv/mesh-interface.c | 3 +++ net/batman-adv/netlink.c | 10 ++++++++++ net/batman-adv/types.h | 9 +++++++++ 5 files changed, 52 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h index 184f2bc8..33049818 100644 --- a/include/uapi/linux/batman_adv.h +++ b/include/uapi/linux/batman_adv.h @@ -499,6 +499,12 @@ enum batadv_nl_attrs { */ BATADV_ATTR_DAT_MAX_LEARNED, + /** + * @BATADV_ATTR_BLA_BACKBONE_MAX_LEARNED: defines the maximum number of BLA backbones + * which can be learned in parallel + */ + BATADV_ATTR_BLA_BACKBONE_MAX_LEARNED, + /* add attributes above here, update the policy in netlink.c */ /** diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index f231afbe..949c887b 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -154,6 +154,9 @@ static void batadv_backbone_gw_release(struct kref *ref) backbone_gw = container_of(ref, struct batadv_bla_backbone_gw, refcount); + if (!backbone_gw->own) + atomic_dec(&backbone_gw->bat_priv->bla.backbone_learned); + kfree_rcu(backbone_gw, rcu); } @@ -488,6 +491,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, { struct batadv_bla_backbone_gw *entry; struct batadv_orig_node *orig_node; + u32 bla_backbone_max_learned; + int bla_backbone_learned; int hash_added; entry = batadv_backbone_hash_find(bat_priv, orig, vid); @@ -499,11 +504,25 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, "%s(): not found (%pM, %d), creating new entry\n", __func__, orig, batadv_print_vid(vid)); + if (!own_backbone) { + bla_backbone_max_learned = READ_ONCE(bat_priv->bla.backbone_max_learned); + bla_backbone_learned = atomic_inc_return(&bat_priv->bla.backbone_learned); + if (bla_backbone_max_learned && bla_backbone_learned > bla_backbone_max_learned) { + atomic_dec(&bat_priv->bla.backbone_learned); + return NULL; + } + } + entry = kzalloc_obj(*entry, GFP_ATOMIC); - if (!entry) + if (!entry) { + if (!own_backbone) + atomic_dec(&bat_priv->bla.backbone_learned); + return NULL; + } entry->vid = vid; + entry->own = own_backbone; WRITE_ONCE(entry->lasttime, jiffies); entry->crc = BATADV_BLA_CRC_INIT; entry->bat_priv = bat_priv; @@ -523,6 +542,10 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, if (unlikely(hash_added != 0)) { /* hash failed, free the structure */ kfree(entry); + + if (!own_backbone) + atomic_dec(&bat_priv->bla.backbone_learned); + return NULL; } diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index a34fdd41..0e313ac8 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) #ifdef CONFIG_BATMAN_ADV_BLA atomic_set(&bat_priv->bla.num_requests, 0); spin_lock_init(&bat_priv->bla.num_requests_lock); + + atomic_set(&bat_priv->bla.backbone_learned, 0); + WRITE_ONCE(bat_priv->bla.backbone_max_learned, 0); #endif atomic_set(&bat_priv->tp_num, 0); diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index d146d7c7..f49d7a5e 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -157,6 +157,7 @@ static const struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = { [BATADV_ATTR_NEIGH_MAX_LEARNED] = { .type = NLA_U32 }, [BATADV_ATTR_ORIG_MAX_LEARNED] = { .type = NLA_U32 }, [BATADV_ATTR_DAT_MAX_LEARNED] = { .type = NLA_U32 }, + [BATADV_ATTR_BLA_BACKBONE_MAX_LEARNED] = { .type = NLA_U32 }, }; /** @@ -300,6 +301,10 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg, if (nla_put_u8(msg, BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED, !!READ_ONCE(bat_priv->bridge_loop_avoidance))) goto nla_put_failure; + + if (nla_put_u32(msg, BATADV_ATTR_BLA_BACKBONE_MAX_LEARNED, + READ_ONCE(bat_priv->bla.backbone_max_learned))) + goto nla_put_failure; #endif /* CONFIG_BATMAN_ADV_BLA */ #ifdef CONFIG_BATMAN_ADV_DAT @@ -490,6 +495,11 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) !!nla_get_u8(attr)); batadv_bla_status_update(bat_priv->mesh_iface); } + + if (info->attrs[BATADV_ATTR_BLA_BACKBONE_MAX_LEARNED]) { + WRITE_ONCE(bat_priv->bla.backbone_max_learned, + nla_get_u32(info->attrs[BATADV_ATTR_BLA_BACKBONE_MAX_LEARNED])); + } #endif /* CONFIG_BATMAN_ADV_BLA */ #ifdef CONFIG_BATMAN_ADV_DAT diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 6001273b..22d47509 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1071,6 +1071,12 @@ struct batadv_priv_bla { /** @num_requests: number of bla requests in flight */ atomic_t num_requests; + /** @backbone_max_learned: Maximum number of backbone_gw */ + u32 backbone_max_learned; + + /** @backbone_learned: current number of learned backbone_gw entries */ + atomic_t backbone_learned; + /** * @num_requests_lock: locks update num_requests + * batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update @@ -1791,6 +1797,9 @@ struct batadv_bla_backbone_gw { /** @vid: vlan id this gateway was detected on */ unsigned short vid; + /** @own: it is the local backbone gateway */ + bool own:1; + /** @hash_entry: hlist node for &batadv_priv_bla.backbone_hash */ struct hlist_node hash_entry; -- 2.47.3