A malicious actor behind one bridge port may spam the kernel with OGMs with a random source MAC address, each of which will create a BLA backbone, each of which is 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 orig_learned, and a limit in orig_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 | 11 +++++++++++ net/batman-adv/mesh-interface.c | 3 +++ net/batman-adv/netlink.c | 10 ++++++++++ net/batman-adv/types.h | 6 ++++++ 5 files changed, 36 insertions(+) diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h index cca87d42..4188d83c 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 backbone + * 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 cec11f12..8bb8dec6 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -494,6 +494,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); @@ -505,6 +507,11 @@ 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)); + bla_backbone_max_learned = READ_ONCE(bat_priv->bla_backbone_max_learned); + bla_backbone_learned = atomic_read(&bat_priv->bla_backbone_learned); + if (bla_backbone_max_learned && bla_backbone_learned >= bla_backbone_max_learned) + return NULL; + entry = kzalloc_obj(*entry, GFP_ATOMIC); if (!entry) return NULL; @@ -520,6 +527,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report); kref_init(&entry->refcount); + atomic_inc(&bat_priv->bla_backbone_learned); + kref_get(&entry->refcount); hash_added = batadv_hash_add(bat_priv->bla.backbone_hash, batadv_compare_backbone_gw, @@ -529,6 +538,7 @@ 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); + atomic_dec(&bat_priv->bla_backbone_learned); return NULL; } @@ -1256,6 +1266,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) hlist_del_rcu(&backbone_gw->hash_entry); batadv_backbone_gw_put(backbone_gw); + atomic_dec(&bat_priv->bla_backbone_learned); } spin_unlock_bh(list_lock); } diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index 851a078d..1594a2d6 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -790,6 +790,9 @@ static int batadv_meshif_init_late(struct net_device *dev) atomic_set(&bat_priv->tt.ogm_append_cnt, 0); #ifdef CONFIG_BATMAN_ADV_BLA atomic_set(&bat_priv->bla.num_requests, 0); + + 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 742b1e3e..5d4bccdc 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -150,6 +150,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 }, }; /** @@ -293,6 +294,10 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg, if (nla_put_u8(msg, BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED, !!atomic_read(&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 @@ -483,6 +488,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 c8bf2212..c091b19e 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1656,6 +1656,12 @@ struct batadv_priv { atomic_t orig_learned; #ifdef CONFIG_BATMAN_ADV_BLA + /** @bla_backbone_max_learned: Maximum number of backbone_gw */ + u32 bla_backbone_max_learned; + + /** @bla_backbone_learned: current number of learned backbone_gw entries */ + atomic_t bla_backbone_learned; + /** @bla: bridge loop avoidance data */ struct batadv_priv_bla bla; #endif -- 2.47.3
