A malicious actor behind one hardif may spam the kernel with OGMs using a
random source MAC address, each of which will create a neighbor node entry,
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        | 10 ++++++++++
 net/batman-adv/originator.c     | 19 +++++++++++++++++--
 net/batman-adv/types.h          |  9 +++++++++
 5 files changed, 45 insertions(+), 2 deletions(-)

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 f1bfe2a5..70e13150 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)
 #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 bb1ef3ee..64bec41a 100644
--- a/net/batman-adv/netlink.c
+++ b/net/batman-adv/netlink.c
@@ -154,6 +154,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 },
 };
 
 /**
@@ -355,6 +356,10 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg,
                        READ_ONCE(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);
@@ -600,6 +605,11 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, 
struct genl_info *info)
                WRITE_ONCE(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 f06583ef..d6a5bd7c 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -299,6 +299,7 @@ void batadv_neigh_node_release(struct kref *ref)
 
        batadv_hardif_put(neigh_node->if_incoming);
 
+       atomic_dec(&neigh_node->bat_priv->neigh_learned);
        kfree_rcu(neigh_node, rcu);
 }
 
@@ -675,6 +676,8 @@ batadv_neigh_node_create(struct batadv_orig_node *orig_node,
 {
        struct batadv_hardif_neigh_node *hardif_neigh = NULL;
        struct batadv_neigh_node *neigh_node;
+       u32 neigh_max_learned;
+       int neigh_learned;
 
        spin_lock_bh(&orig_node->neigh_list_lock);
 
@@ -682,14 +685,25 @@ 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_inc_return(&orig_node->bat_priv->neigh_learned);
+       if (neigh_max_learned && neigh_learned > neigh_max_learned) {
+               atomic_dec(&orig_node->bat_priv->neigh_learned);
+               goto out;
+       }
+
        hardif_neigh = batadv_hardif_neigh_get_or_create(hard_iface,
                                                         neigh_addr, orig_node);
-       if (!hardif_neigh)
+       if (!hardif_neigh) {
+               atomic_dec(&orig_node->bat_priv->neigh_learned);
                goto out;
+       }
 
        neigh_node = kzalloc_obj(*neigh_node, GFP_ATOMIC);
-       if (!neigh_node)
+       if (!neigh_node) {
+               atomic_dec(&orig_node->bat_priv->neigh_learned);
                goto out;
+       }
 
        INIT_HLIST_NODE(&neigh_node->list);
        INIT_HLIST_HEAD(&neigh_node->ifinfo_list);
@@ -699,6 +713,7 @@ batadv_neigh_node_create(struct batadv_orig_node *orig_node,
        ether_addr_copy(neigh_node->addr, neigh_addr);
        neigh_node->if_incoming = hard_iface;
        neigh_node->last_seen = jiffies;
+       neigh_node->bat_priv = orig_node->bat_priv;
 
 #ifdef CONFIG_BATMAN_ADV_BATMAN_V
        ACCESS_PRIVATE(neigh_node, orig_node_id) = orig_node;
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index d6e15d46..e404a6b4 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -679,6 +679,9 @@ struct batadv_neigh_node {
        /** @addr: the MAC address of the neighboring interface */
        u8 addr[ETH_ALEN];
 
+       /** @bat_priv: pointer to the mesh object */
+       struct batadv_priv *bat_priv;
+
        /** @ifinfo_list: list for routing metrics per outgoing interface */
        struct hlist_head ifinfo_list;
 
@@ -1700,6 +1703,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