[PATCH RFC batadv v2 2/5] batman-adv: limit numbers of parallel learned originators
Sven Eckelmann <[email protected]>
| Newsgroups | org.open-mesh.lists.batman |
|---|---|
| Message-ID | <[email protected]> |
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