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 neighbor 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 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        | 11 +++++++++++
 net/batman-adv/originator.c     | 12 ++++++++++++
 net/batman-adv/types.h          |  6 ++++++
 5 files changed, 38 insertions(+)

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 50c26037..b9302c58 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)
 #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 e52f44e7..915a7d74 100644
--- a/net/batman-adv/netlink.c
+++ b/net/batman-adv/netlink.c
@@ -13,6 +13,7 @@
 #include <linux/bug.h>
 #include <linux/byteorder/generic.h>
 #include <linux/cache.h>
+#include <linux/compiler.h>
 #include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/gfp.h>
@@ -146,6 +147,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 },
 };
 
 /**
@@ -347,6 +349,10 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg,
                        atomic_read(&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);
@@ -592,6 +598,11 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, 
struct genl_info *info)
                atomic_set(&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 b3468cca..90f553fc 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -671,6 +671,8 @@ batadv_neigh_node_create(struct batadv_orig_node *orig_node,
 {
        struct batadv_neigh_node *neigh_node;
        struct batadv_hardif_neigh_node *hardif_neigh = NULL;
+       u32 neigh_max_learned;
+       int neigh_learned;
 
        spin_lock_bh(&orig_node->neigh_list_lock);
 
@@ -678,6 +680,11 @@ 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_read(&orig_node->bat_priv->neigh_learned);
+       if (neigh_max_learned && neigh_learned >= neigh_max_learned)
+               goto out;
+
        hardif_neigh = batadv_hardif_neigh_get_or_create(hard_iface,
                                                         neigh_addr, orig_node);
        if (!hardif_neigh)
@@ -701,6 +708,8 @@ batadv_neigh_node_create(struct batadv_orig_node *orig_node,
        kref_get(&hardif_neigh->refcount);
        neigh_node->hardif_neigh = hardif_neigh;
 
+       atomic_inc(&orig_node->bat_priv->neigh_learned);
+
        /* extra reference for return */
        kref_init(&neigh_node->refcount);
 
@@ -866,6 +875,8 @@ void batadv_orig_node_release(struct kref *ref)
                                  &orig_node->neigh_list, list) {
                hlist_del_rcu(&neigh_node->list);
                batadv_neigh_node_put(neigh_node);
+
+               atomic_dec(&orig_node->bat_priv->neigh_learned);
        }
 
        hlist_for_each_entry_safe(orig_ifinfo, node_tmp,
@@ -1144,6 +1155,7 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
 
                        hlist_del_rcu(&neigh_node->list);
                        batadv_neigh_node_put(neigh_node);
+                       atomic_dec(&bat_priv->neigh_learned);
                } else {
                        /* only necessary if not the whole neighbor is to be
                         * deleted, but some interface has been removed.
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index c8c3e806..13349350 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1637,6 +1637,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

Reply via email to