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 DAT 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/distributed-arp-table.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 b3c04ab0..cca87d42 100644
--- a/include/uapi/linux/batman_adv.h
+++ b/include/uapi/linux/batman_adv.h
@@ -493,6 +493,12 @@ enum batadv_nl_attrs {
         */
        BATADV_ATTR_ORIG_MAX_LEARNED,
 
+       /**
+        * @BATADV_ATTR_DAT_MAX_LEARNED: defines the maximum number of DAT 
entries
+        * which can be learned in parallel
+        */
+       BATADV_ATTR_DAT_MAX_LEARNED,
+
        /* add attributes above here, update the policy in netlink.c */
 
        /**
diff --git a/net/batman-adv/distributed-arp-table.c 
b/net/batman-adv/distributed-arp-table.c
index 0a8bd95e..99569a2f 100644
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -181,6 +181,7 @@ static void __batadv_dat_purge(struct batadv_priv *bat_priv,
 
                        hlist_del_rcu(&dat_entry->hash_entry);
                        batadv_dat_entry_put(dat_entry);
+                       atomic_dec(&bat_priv->dat_learned);
                }
                spin_unlock_bh(list_lock);
        }
@@ -366,6 +367,8 @@ static void batadv_dat_entry_add(struct batadv_priv 
*bat_priv, __be32 ip,
                                 u8 *mac_addr, unsigned short vid)
 {
        struct batadv_dat_entry *dat_entry;
+       u32 dat_max_learned;
+       int dat_learned;
        int hash_added;
 
        dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid);
@@ -381,6 +384,11 @@ static void batadv_dat_entry_add(struct batadv_priv 
*bat_priv, __be32 ip,
                goto out;
        }
 
+       dat_max_learned = READ_ONCE(bat_priv->dat_max_learned);
+       dat_learned = atomic_read(&bat_priv->dat_learned);
+       if (dat_max_learned && dat_learned >= dat_max_learned)
+               goto out;
+
        dat_entry = kmalloc_obj(*dat_entry, GFP_ATOMIC);
        if (!dat_entry)
                goto out;
@@ -391,6 +399,8 @@ static void batadv_dat_entry_add(struct batadv_priv 
*bat_priv, __be32 ip,
        dat_entry->last_update = jiffies;
        kref_init(&dat_entry->refcount);
 
+       atomic_inc(&bat_priv->dat_learned);
+
        kref_get(&dat_entry->refcount);
        hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat,
                                     batadv_hash_dat, dat_entry,
@@ -399,6 +409,7 @@ static void batadv_dat_entry_add(struct batadv_priv 
*bat_priv, __be32 ip,
        if (unlikely(hash_added != 0)) {
                /* remove the reference for the hash */
                batadv_dat_entry_put(dat_entry);
+               atomic_dec(&bat_priv->dat_learned);
                goto out;
        }
 
diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c
index fad788cc..851a078d 100644
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -759,6 +759,9 @@ static int batadv_meshif_init_late(struct net_device *dev)
 #endif
 #ifdef CONFIG_BATMAN_ADV_DAT
        atomic_set(&bat_priv->distributed_arp_table, 1);
+
+       atomic_set(&bat_priv->dat_learned, 0);
+       WRITE_ONCE(bat_priv->dat_max_learned, 0);
 #endif
 #ifdef CONFIG_BATMAN_ADV_MCAST
        atomic_set(&bat_priv->multicast_mode, 1);
diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
index 2b5fb1d6..742b1e3e 100644
--- a/net/batman-adv/netlink.c
+++ b/net/batman-adv/netlink.c
@@ -149,6 +149,7 @@ static const struct nla_policy 
batadv_netlink_policy[NUM_BATADV_ATTR] = {
        [BATADV_ATTR_THROUGHPUT_OVERRIDE]       = { .type = NLA_U32 },
        [BATADV_ATTR_NEIGH_MAX_LEARNED]         = { .type = NLA_U32 },
        [BATADV_ATTR_ORIG_MAX_LEARNED]          = { .type = NLA_U32 },
+       [BATADV_ATTR_DAT_MAX_LEARNED]           = { .type = NLA_U32 },
 };
 
 /**
@@ -298,6 +299,10 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg,
        if (nla_put_u8(msg, BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED,
                       !!atomic_read(&bat_priv->distributed_arp_table)))
                goto nla_put_failure;
+
+       if (nla_put_u32(msg, BATADV_ATTR_DAT_MAX_LEARNED,
+                       READ_ONCE(bat_priv->dat_max_learned)))
+               goto nla_put_failure;
 #endif /* CONFIG_BATMAN_ADV_DAT */
 
        if (nla_put_u8(msg, BATADV_ATTR_FRAGMENTATION_ENABLED,
@@ -488,6 +493,11 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, 
struct genl_info *info)
                           !!nla_get_u8(attr));
                batadv_dat_status_update(bat_priv->mesh_iface);
        }
+
+       if (info->attrs[BATADV_ATTR_DAT_MAX_LEARNED]) {
+               WRITE_ONCE(bat_priv->dat_max_learned,
+                          
nla_get_u32(info->attrs[BATADV_ATTR_DAT_MAX_LEARNED]));
+       }
 #endif /* CONFIG_BATMAN_ADV_DAT */
 
        if (info->attrs[BATADV_ATTR_FRAGMENTATION_ENABLED]) {
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 49398aeb..c8bf2212 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1535,6 +1535,12 @@ struct batadv_priv {
         *  is enabled
         */
        atomic_t distributed_arp_table;
+
+       /** @dat_max_learned: Maximum number of DAT entries */
+       u32 dat_max_learned;
+
+       /** @dat_learned: current number of learned DAT entries */
+       atomic_t dat_learned;
 #endif
 
 #ifdef CONFIG_BATMAN_ADV_MCAST

-- 
2.47.3

Reply via email to