]> www.infradead.org Git - users/hch/misc.git/commitdiff
net: lwtunnel: fix recursion loops
authorJustin Iurman <justin.iurman@uliege.be>
Fri, 14 Mar 2025 12:00:46 +0000 (13:00 +0100)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 20 Mar 2025 10:25:52 +0000 (11:25 +0100)
This patch acts as a parachute, catch all solution, by detecting
recursion loops in lwtunnel users and taking care of them (e.g., a loop
between routes, a loop within the same route, etc). In general, such
loops are the consequence of pathological configurations. Each lwtunnel
user is still free to catch such loops early and do whatever they want
with them. It will be the case in a separate patch for, e.g., seg6 and
seg6_local, in order to provide drop reasons and update statistics.
Another example of a lwtunnel user taking care of loops is ioam6, which
has valid use cases that include loops (e.g., inline mode), and which is
addressed by the next patch in this series. Overall, this patch acts as
a last resort to catch loops and drop packets, since we don't want to
leak something unintentionally because of a pathological configuration
in lwtunnels.

The solution in this patch reuses dev_xmit_recursion(),
dev_xmit_recursion_inc(), and dev_xmit_recursion_dec(), which seems fine
considering the context.

Closes: https://lore.kernel.org/netdev/2bc9e2079e864a9290561894d2a602d6@akamai.com/
Closes: https://lore.kernel.org/netdev/Z7NKYMY7fJT5cYWu@shredder/
Fixes: ffce41962ef6 ("lwtunnel: support dst output redirect function")
Fixes: 2536862311d2 ("lwt: Add support to redirect dst.input")
Fixes: 14972cbd34ff ("net: lwtunnel: Handle fragmentation")
Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
Link: https://patch.msgid.link/20250314120048.12569-2-justin.iurman@uliege.be
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/core/lwtunnel.c

index 711cd3b4347a7948ae3958257de1518685c7fbff..4417a18b3e951aa4bd62b6964a47c49e6fa8e919 100644 (file)
@@ -23,6 +23,8 @@
 #include <net/ip6_fib.h>
 #include <net/rtnh.h>
 
+#include "dev.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 (dev_xmit_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)) {
+               dev_xmit_recursion_inc();
                ret = ops->output(net, sk, skb);
+               dev_xmit_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 (dev_xmit_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)) {
+               dev_xmit_recursion_inc();
                ret = ops->xmit(skb);
+               dev_xmit_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 (dev_xmit_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)) {
+               dev_xmit_recursion_inc();
                ret = ops->input(skb);
+               dev_xmit_recursion_dec();
+       }
        rcu_read_unlock();
 
        if (ret == -EOPNOTSUPP)