A malicious actor behind one bridge port may spam the kernel with OGMs with
a random source MAC address, each of which will create an originator node
entry, 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/bat_iv_ogm.c     | 13 ++++++++++++-
 net/batman-adv/bat_v_ogm.c      | 11 +++++++++++
 net/batman-adv/mesh-interface.c |  3 +++
 net/batman-adv/netlink.c        | 10 ++++++++++
 net/batman-adv/originator.c     |  2 ++
 net/batman-adv/types.h          |  6 ++++++
 7 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h
index addb6092..b3c04ab0 100644
--- a/include/uapi/linux/batman_adv.h
+++ b/include/uapi/linux/batman_adv.h
@@ -487,6 +487,12 @@ enum batadv_nl_attrs {
         */
        BATADV_ATTR_NEIGH_MAX_LEARNED,
 
+       /**
+        * @BATADV_ATTR_ORIG_MAX_LEARNED: defines the maximum number of 
originator
+        * which can be learned in parallel
+        */
+       BATADV_ATTR_ORIG_MAX_LEARNED,
+
        /* add attributes above here, update the policy in netlink.c */
 
        /**
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index 74ef7dc2..cc289f55 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -13,6 +13,7 @@
 #include <linux/bug.h>
 #include <linux/byteorder/generic.h>
 #include <linux/cache.h>
+#include <linux/compiler.h>
 #include <linux/container_of.h>
 #include <linux/errno.h>
 #include <linux/etherdevice.h>
@@ -140,24 +141,34 @@ static struct batadv_orig_node *
 batadv_iv_ogm_orig_get(struct batadv_priv *bat_priv, const u8 *addr)
 {
        struct batadv_orig_node *orig_node;
+       u32 orig_max_learned;
+       int orig_learned;
        int hash_added;
 
        orig_node = batadv_orig_hash_find(bat_priv, addr);
        if (orig_node)
                return orig_node;
 
+       orig_max_learned = READ_ONCE(bat_priv->orig_max_learned);
+       orig_learned = atomic_read(&bat_priv->orig_learned);
+       if (orig_max_learned && orig_learned >= orig_max_learned)
+               return NULL;
+
        orig_node = batadv_orig_node_new(bat_priv, addr);
        if (!orig_node)
                return NULL;
 
        spin_lock_init(&orig_node->bat_iv.ogm_cnt_lock);
+       atomic_inc(&bat_priv->orig_learned);
 
        kref_get(&orig_node->refcount);
        hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig,
                                     batadv_choose_orig, orig_node,
                                     &orig_node->hash_entry);
-       if (hash_added != 0)
+       if (hash_added != 0) {
+               atomic_dec(&bat_priv->orig_learned);
                goto free_orig_node_hash;
+       }
 
        return orig_node;
 
diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
index e3870492..dd57f4a7 100644
--- a/net/batman-adv/bat_v_ogm.c
+++ b/net/batman-adv/bat_v_ogm.c
@@ -9,6 +9,7 @@
 
 #include <linux/atomic.h>
 #include <linux/byteorder/generic.h>
+#include <linux/compiler.h>
 #include <linux/container_of.h>
 #include <linux/errno.h>
 #include <linux/etherdevice.h>
@@ -54,16 +55,25 @@ struct batadv_orig_node *batadv_v_ogm_orig_get(struct 
batadv_priv *bat_priv,
                                               const u8 *addr)
 {
        struct batadv_orig_node *orig_node;
+       u32 orig_max_learned;
+       int orig_learned;
        int hash_added;
 
        orig_node = batadv_orig_hash_find(bat_priv, addr);
        if (orig_node)
                return orig_node;
 
+       orig_max_learned = READ_ONCE(bat_priv->orig_max_learned);
+       orig_learned = atomic_read(&bat_priv->orig_learned);
+       if (orig_max_learned && orig_learned >= orig_max_learned)
+               return NULL;
+
        orig_node = batadv_orig_node_new(bat_priv, addr);
        if (!orig_node)
                return NULL;
 
+       atomic_inc(&bat_priv->orig_learned);
+
        kref_get(&orig_node->refcount);
        hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig,
                                     batadv_choose_orig, orig_node,
@@ -73,6 +83,7 @@ struct batadv_orig_node *batadv_v_ogm_orig_get(struct 
batadv_priv *bat_priv,
                batadv_orig_node_put(orig_node);
                batadv_orig_node_put(orig_node);
                orig_node = NULL;
+               atomic_dec(&bat_priv->orig_learned);
        }
 
        return orig_node;
diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c
index b9302c58..fad788cc 100644
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -793,6 +793,9 @@ static int batadv_meshif_init_late(struct net_device *dev)
        atomic_set(&bat_priv->neigh_learned, 0);
        WRITE_ONCE(bat_priv->neigh_max_learned, 0);
 
+       atomic_set(&bat_priv->orig_learned, 0);
+       WRITE_ONCE(bat_priv->orig_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 915a7d74..2b5fb1d6 100644
--- a/net/batman-adv/netlink.c
+++ b/net/batman-adv/netlink.c
@@ -148,6 +148,7 @@ static const struct nla_policy 
batadv_netlink_policy[NUM_BATADV_ATTR] = {
        [BATADV_ATTR_ELP_INTERVAL]              = { .type = NLA_U32 },
        [BATADV_ATTR_THROUGHPUT_OVERRIDE]       = { .type = NLA_U32 },
        [BATADV_ATTR_NEIGH_MAX_LEARNED]         = { .type = NLA_U32 },
+       [BATADV_ATTR_ORIG_MAX_LEARNED]          = { .type = NLA_U32 },
 };
 
 /**
@@ -353,6 +354,10 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg,
                        READ_ONCE(bat_priv->neigh_max_learned)))
                goto nla_put_failure;
 
+       if (nla_put_u32(msg, BATADV_ATTR_ORIG_MAX_LEARNED,
+                       READ_ONCE(bat_priv->orig_max_learned)))
+               goto nla_put_failure;
+
        batadv_hardif_put(primary_if);
 
        genlmsg_end(msg, hdr);
@@ -603,6 +608,11 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, 
struct genl_info *info)
                           
nla_get_u32(info->attrs[BATADV_ATTR_NEIGH_MAX_LEARNED]));
        }
 
+       if (info->attrs[BATADV_ATTR_ORIG_MAX_LEARNED]) {
+               WRITE_ONCE(bat_priv->orig_max_learned,
+                          
nla_get_u32(info->attrs[BATADV_ATTR_ORIG_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 90f553fc..3c48e006 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -930,6 +930,7 @@ void batadv_originator_free(struct batadv_priv *bat_priv)
                                          head, hash_entry) {
                        hlist_del_rcu(&orig_node->hash_entry);
                        batadv_orig_node_put(orig_node);
+                       atomic_dec(&bat_priv->orig_learned);
                }
                spin_unlock_bh(list_lock);
        }
@@ -1297,6 +1298,7 @@ void batadv_purge_orig_ref(struct batadv_priv *bat_priv)
                                                          orig_node, -1,
                                                          "originator timed 
out");
                                batadv_orig_node_put(orig_node);
+                               atomic_dec(&bat_priv->orig_learned);
                                continue;
                        }
 
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 13349350..49398aeb 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1643,6 +1643,12 @@ struct batadv_priv {
        /** @neigh_learned: current number of learned neighbors */
        atomic_t neigh_learned;
 
+       /** @orig_max_learned: Maximum number of originators */
+       u32 orig_max_learned;
+
+       /** @orig_learned: current number of learned originators */
+       atomic_t orig_learned;
+
 #ifdef CONFIG_BATMAN_ADV_BLA
        /** @bla: bridge loop avoidance data */
        struct batadv_priv_bla bla;

-- 
2.47.3

Reply via email to