Skip to content

Commit f31b6fb

Browse files
author
Paolo Abeni
committed
Merge branch 'net-fix-lwtunnel-reentry-loops'
Justin Iurman says: ==================== net: fix lwtunnel reentry loops When the destination is the same after the transformation, we enter a lwtunnel loop. This is true for most of lwt users: ioam6, rpl, seg6, seg6_local, ila_lwt, and lwt_bpf. It can happen in their input() and output() handlers respectively, where either dst_input() or dst_output() is called at the end. It can also happen in xmit() handlers. Here is an example for rpl_input(): dump_stack_lvl+0x60/0x80 rpl_input+0x9d/0x320 lwtunnel_input+0x64/0xa0 lwtunnel_input+0x64/0xa0 lwtunnel_input+0x64/0xa0 lwtunnel_input+0x64/0xa0 lwtunnel_input+0x64/0xa0 [...] lwtunnel_input+0x64/0xa0 lwtunnel_input+0x64/0xa0 lwtunnel_input+0x64/0xa0 lwtunnel_input+0x64/0xa0 lwtunnel_input+0x64/0xa0 ip6_sublist_rcv_finish+0x85/0x90 ip6_sublist_rcv+0x236/0x2f0 ... until rpl_do_srh() fails, which means skb_cow_head() failed. This series provides a fix at the core level of lwtunnel to catch such loops when they're not caught by the respective lwtunnel users, and handle the loop case in ioam6 which is one of the users. This series also comes with a new selftest to detect some dst cache reference loops in lwtunnel users. ==================== Link: https://patch.msgid.link/20250314120048.12569-1-justin.iurman@uliege.be Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2 parents 47a9b5e + 3ed61b8 commit f31b6fb

File tree

5 files changed

+306
-16
lines changed

5 files changed

+306
-16
lines changed

net/core/lwtunnel.c

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include <net/ip6_fib.h>
2424
#include <net/rtnh.h>
2525

26+
#include "dev.h"
27+
2628
DEFINE_STATIC_KEY_FALSE(nf_hooks_lwtunnel_enabled);
2729
EXPORT_SYMBOL_GPL(nf_hooks_lwtunnel_enabled);
2830

@@ -325,13 +327,23 @@ EXPORT_SYMBOL_GPL(lwtunnel_cmp_encap);
325327

326328
int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
327329
{
328-
struct dst_entry *dst = skb_dst(skb);
329330
const struct lwtunnel_encap_ops *ops;
330331
struct lwtunnel_state *lwtstate;
331-
int ret = -EINVAL;
332+
struct dst_entry *dst;
333+
int ret;
334+
335+
if (dev_xmit_recursion()) {
336+
net_crit_ratelimited("%s(): recursion limit reached on datapath\n",
337+
__func__);
338+
ret = -ENETDOWN;
339+
goto drop;
340+
}
332341

333-
if (!dst)
342+
dst = skb_dst(skb);
343+
if (!dst) {
344+
ret = -EINVAL;
334345
goto drop;
346+
}
335347
lwtstate = dst->lwtstate;
336348

337349
if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
@@ -341,8 +353,11 @@ int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
341353
ret = -EOPNOTSUPP;
342354
rcu_read_lock();
343355
ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
344-
if (likely(ops && ops->output))
356+
if (likely(ops && ops->output)) {
357+
dev_xmit_recursion_inc();
345358
ret = ops->output(net, sk, skb);
359+
dev_xmit_recursion_dec();
360+
}
346361
rcu_read_unlock();
347362

348363
if (ret == -EOPNOTSUPP)
@@ -359,13 +374,23 @@ EXPORT_SYMBOL_GPL(lwtunnel_output);
359374

360375
int lwtunnel_xmit(struct sk_buff *skb)
361376
{
362-
struct dst_entry *dst = skb_dst(skb);
363377
const struct lwtunnel_encap_ops *ops;
364378
struct lwtunnel_state *lwtstate;
365-
int ret = -EINVAL;
379+
struct dst_entry *dst;
380+
int ret;
381+
382+
if (dev_xmit_recursion()) {
383+
net_crit_ratelimited("%s(): recursion limit reached on datapath\n",
384+
__func__);
385+
ret = -ENETDOWN;
386+
goto drop;
387+
}
366388

367-
if (!dst)
389+
dst = skb_dst(skb);
390+
if (!dst) {
391+
ret = -EINVAL;
368392
goto drop;
393+
}
369394

370395
lwtstate = dst->lwtstate;
371396

@@ -376,8 +401,11 @@ int lwtunnel_xmit(struct sk_buff *skb)
376401
ret = -EOPNOTSUPP;
377402
rcu_read_lock();
378403
ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
379-
if (likely(ops && ops->xmit))
404+
if (likely(ops && ops->xmit)) {
405+
dev_xmit_recursion_inc();
380406
ret = ops->xmit(skb);
407+
dev_xmit_recursion_dec();
408+
}
381409
rcu_read_unlock();
382410

383411
if (ret == -EOPNOTSUPP)
@@ -394,13 +422,23 @@ EXPORT_SYMBOL_GPL(lwtunnel_xmit);
394422

395423
int lwtunnel_input(struct sk_buff *skb)
396424
{
397-
struct dst_entry *dst = skb_dst(skb);
398425
const struct lwtunnel_encap_ops *ops;
399426
struct lwtunnel_state *lwtstate;
400-
int ret = -EINVAL;
427+
struct dst_entry *dst;
428+
int ret;
401429

402-
if (!dst)
430+
if (dev_xmit_recursion()) {
431+
net_crit_ratelimited("%s(): recursion limit reached on datapath\n",
432+
__func__);
433+
ret = -ENETDOWN;
403434
goto drop;
435+
}
436+
437+
dst = skb_dst(skb);
438+
if (!dst) {
439+
ret = -EINVAL;
440+
goto drop;
441+
}
404442
lwtstate = dst->lwtstate;
405443

406444
if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
@@ -410,8 +448,11 @@ int lwtunnel_input(struct sk_buff *skb)
410448
ret = -EOPNOTSUPP;
411449
rcu_read_lock();
412450
ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
413-
if (likely(ops && ops->input))
451+
if (likely(ops && ops->input)) {
452+
dev_xmit_recursion_inc();
414453
ret = ops->input(skb);
454+
dev_xmit_recursion_dec();
455+
}
415456
rcu_read_unlock();
416457

417458
if (ret == -EOPNOTSUPP)

net/ipv6/ioam6_iptunnel.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,6 @@ static int ioam6_do_encap(struct net *net, struct sk_buff *skb,
337337
static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
338338
{
339339
struct dst_entry *dst = skb_dst(skb), *cache_dst = NULL;
340-
struct in6_addr orig_daddr;
341340
struct ioam6_lwt *ilwt;
342341
int err = -EINVAL;
343342
u32 pkt_cnt;
@@ -352,8 +351,6 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
352351
if (pkt_cnt % ilwt->freq.n >= ilwt->freq.k)
353352
goto out;
354353

355-
orig_daddr = ipv6_hdr(skb)->daddr;
356-
357354
local_bh_disable();
358355
cache_dst = dst_cache_get(&ilwt->cache);
359356
local_bh_enable();
@@ -422,7 +419,10 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
422419
goto drop;
423420
}
424421

425-
if (!ipv6_addr_equal(&orig_daddr, &ipv6_hdr(skb)->daddr)) {
422+
/* avoid lwtunnel_output() reentry loop when destination is the same
423+
* after transformation (e.g., with the inline mode)
424+
*/
425+
if (dst->lwtstate != cache_dst->lwtstate) {
426426
skb_dst_drop(skb);
427427
skb_dst_set(skb, cache_dst);
428428
return dst_output(net, sk, skb);

tools/testing/selftests/net/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ TEST_PROGS += vlan_bridge_binding.sh
101101
TEST_PROGS += bpf_offload.py
102102
TEST_PROGS += ipv6_route_update_soft_lockup.sh
103103
TEST_PROGS += busy_poll_test.sh
104+
TEST_PROGS += lwt_dst_cache_ref_loop.sh
104105

105106
# YNL files, must be before "include ..lib.mk"
106107
YNL_GEN_FILES := busy_poller netlink-dumps

tools/testing/selftests/net/config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,5 @@ CONFIG_XFRM_INTERFACE=m
107107
CONFIG_XFRM_USER=m
108108
CONFIG_IP_NF_MATCH_RPFILTER=m
109109
CONFIG_IP6_NF_MATCH_RPFILTER=m
110+
CONFIG_IPV6_ILA=m
111+
CONFIG_IPV6_RPL_LWTUNNEL=y

0 commit comments

Comments
 (0)