[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250312103246.16206-1-justin.iurman@uliege.be>
Date: Wed, 12 Mar 2025 11:32:46 +0100
From: Justin Iurman <justin.iurman@...ege.be>
To: netdev@...r.kernel.org
Cc: davem@...emloft.net,
edumazet@...gle.com,
kuba@...nel.org,
pabeni@...hat.com,
horms@...nel.org,
justin.iurman@...ege.be,
Roopa Prabhu <roopa@...dia.com>,
Andrea Mayer <andrea.mayer@...roma2.it>,
Stefano Salsano <stefano.salsano@...roma2.it>,
Ahmed Abdelsalam <ahabdels.dev@...il.com>,
Ido Schimmel <idosch@...dia.com>
Subject: [PATCH net] net: lwtunnel: fix recursion loops
Different kind of loops in most of lwtunnel users were fixed by some
recent patches. This patch acts as a parachute, catch all solution, by
detecting any use cases with recursion and taking care of them (e.g., a
loop between routes). This is applied to lwtunnel_input(),
lwtunnel_output(), and lwtunnel_xmit().
Fixes: ffce41962ef6 ("lwtunnel: support dst output redirect function")
Fixes: 2536862311d2 ("lwt: Add support to redirect dst.input")
Fixes: 14972cbd34ff ("net: lwtunnel: Handle fragmentation")
Closes: https://lore.kernel.org/netdev/2bc9e2079e864a9290561894d2a602d6@akamai.com/
Cc: Roopa Prabhu <roopa@...dia.com>
Cc: Andrea Mayer <andrea.mayer@...roma2.it>
Cc: Stefano Salsano <stefano.salsano@...roma2.it>
Cc: Ahmed Abdelsalam <ahabdels.dev@...il.com>
Cc: Ido Schimmel <idosch@...dia.com>
Signed-off-by: Justin Iurman <justin.iurman@...ege.be>
---
net/core/lwtunnel.c | 65 ++++++++++++++++++++++++++++++++++++---------
net/core/lwtunnel.h | 42 +++++++++++++++++++++++++++++
2 files changed, 95 insertions(+), 12 deletions(-)
create mode 100644 net/core/lwtunnel.h
diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c
index 711cd3b4347a..0954783e36ce 100644
--- a/net/core/lwtunnel.c
+++ b/net/core/lwtunnel.c
@@ -23,6 +23,8 @@
#include <net/ip6_fib.h>
#include <net/rtnh.h>
+#include "lwtunnel.h"
+
DEFINE_STATIC_KEY_FALSE(nf_hooks_lwtunnel_enabled);
EXPORT_SYMBOL_GPL(nf_hooks_lwtunnel_enabled);
@@ -325,13 +327,23 @@ EXPORT_SYMBOL_GPL(lwtunnel_cmp_encap);
int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
- struct dst_entry *dst = skb_dst(skb);
const struct lwtunnel_encap_ops *ops;
struct lwtunnel_state *lwtstate;
- int ret = -EINVAL;
+ struct dst_entry *dst;
+ int ret;
+
+ if (lwtunnel_recursion()) {
+ net_crit_ratelimited("%s(): recursion limit reached on datapath\n",
+ __func__);
+ ret = -ENETDOWN;
+ goto drop;
+ }
- if (!dst)
+ dst = skb_dst(skb);
+ if (!dst) {
+ ret = -EINVAL;
goto drop;
+ }
lwtstate = dst->lwtstate;
if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
@@ -341,8 +353,11 @@ int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
ret = -EOPNOTSUPP;
rcu_read_lock();
ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
- if (likely(ops && ops->output))
+ if (likely(ops && ops->output)) {
+ lwtunnel_recursion_inc();
ret = ops->output(net, sk, skb);
+ lwtunnel_recursion_dec();
+ }
rcu_read_unlock();
if (ret == -EOPNOTSUPP)
@@ -359,13 +374,23 @@ EXPORT_SYMBOL_GPL(lwtunnel_output);
int lwtunnel_xmit(struct sk_buff *skb)
{
- struct dst_entry *dst = skb_dst(skb);
const struct lwtunnel_encap_ops *ops;
struct lwtunnel_state *lwtstate;
- int ret = -EINVAL;
+ struct dst_entry *dst;
+ int ret;
+
+ if (lwtunnel_recursion()) {
+ net_crit_ratelimited("%s(): recursion limit reached on datapath\n",
+ __func__);
+ ret = -ENETDOWN;
+ goto drop;
+ }
- if (!dst)
+ dst = skb_dst(skb);
+ if (!dst) {
+ ret = -EINVAL;
goto drop;
+ }
lwtstate = dst->lwtstate;
@@ -376,8 +401,11 @@ int lwtunnel_xmit(struct sk_buff *skb)
ret = -EOPNOTSUPP;
rcu_read_lock();
ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
- if (likely(ops && ops->xmit))
+ if (likely(ops && ops->xmit)) {
+ lwtunnel_recursion_inc();
ret = ops->xmit(skb);
+ lwtunnel_recursion_dec();
+ }
rcu_read_unlock();
if (ret == -EOPNOTSUPP)
@@ -394,13 +422,23 @@ EXPORT_SYMBOL_GPL(lwtunnel_xmit);
int lwtunnel_input(struct sk_buff *skb)
{
- struct dst_entry *dst = skb_dst(skb);
const struct lwtunnel_encap_ops *ops;
struct lwtunnel_state *lwtstate;
- int ret = -EINVAL;
+ struct dst_entry *dst;
+ int ret;
- if (!dst)
+ if (lwtunnel_recursion()) {
+ net_crit_ratelimited("%s(): recursion limit reached on datapath\n",
+ __func__);
+ ret = -ENETDOWN;
goto drop;
+ }
+
+ dst = skb_dst(skb);
+ if (!dst) {
+ ret = -EINVAL;
+ goto drop;
+ }
lwtstate = dst->lwtstate;
if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
@@ -410,8 +448,11 @@ int lwtunnel_input(struct sk_buff *skb)
ret = -EOPNOTSUPP;
rcu_read_lock();
ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
- if (likely(ops && ops->input))
+ if (likely(ops && ops->input)) {
+ lwtunnel_recursion_inc();
ret = ops->input(skb);
+ lwtunnel_recursion_dec();
+ }
rcu_read_unlock();
if (ret == -EOPNOTSUPP)
diff --git a/net/core/lwtunnel.h b/net/core/lwtunnel.h
new file mode 100644
index 000000000000..32880ecdd8bb
--- /dev/null
+++ b/net/core/lwtunnel.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef _NET_CORE_LWTUNNEL_H
+#define _NET_CORE_LWTUNNEL_H
+
+#include <linux/netdevice.h>
+
+#define LWTUNNEL_RECURSION_LIMIT 8
+
+#ifndef CONFIG_PREEMPT_RT
+static inline bool lwtunnel_recursion(void)
+{
+ return unlikely(__this_cpu_read(softnet_data.xmit.recursion) >
+ LWTUNNEL_RECURSION_LIMIT);
+}
+
+static inline void lwtunnel_recursion_inc(void)
+{
+ __this_cpu_inc(softnet_data.xmit.recursion);
+}
+
+static inline void lwtunnel_recursion_dec(void)
+{
+ __this_cpu_dec(softnet_data.xmit.recursion);
+}
+#else
+static inline bool lwtunnel_recursion(void)
+{
+ return unlikely(current->net_xmit.recursion > LWTUNNEL_RECURSION_LIMIT);
+}
+
+static inline void lwtunnel_recursion_inc(void)
+{
+ current->net_xmit.recursion++;
+}
+
+static inline void lwtunnel_recursion_dec(void)
+{
+ current->net_xmit.recursion--;
+}
+#endif
+
+#endif /* _NET_CORE_LWTUNNEL_H */
--
2.34.1
Powered by blists - more mailing lists