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

Reply via email to