* [PATCH net v3 0/7] net/sched: Fix packet loops in mirred and netem
@ 2026-03-26 18:00 Stephen Hemminger
2026-03-26 18:01 ` [PATCH v3 1/7] net: Introduce skb tc depth field to track packet loops Stephen Hemminger
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Stephen Hemminger @ 2026-03-26 18:00 UTC (permalink / raw)
To: netdev; +Cc: Stephen Hemminger
This a minor revision of Jamal's series that
fixes packet loops caused by mirred ingress redirects
and netem duplication in stacked qdisc trees.
The core idea is a 2-bit per-skb tc_depth counter that travels with
the packet. The existing per-CPU mirred nest tracking loses state
when a packet is deferred through the backlog or moves between CPUs
via XPS/RPS. A per-skb field covers both cases.
Patch 1 adds the tc_depth field in a padding hole in sk_buff.
Patches 2-3 revert the check_netem_in_tree() fix and its tests,
which broke legitimate multi-netem configurations.
Patch 4 uses tc_depth to stop netem duplicate recursion.
Patch 5 uses tc_depth to catch mirred ingress redirect loops.
Patches 6-7 add mirred and netem test cases.
Thanks to Jamal and Victor for fixing this.
There are additional netem bug fixes in the pipeline but those
are held back until this series lands.
Changes in v3:
- Renamed skb->ttl to skb->tc_depth to avoid confusion with IP TTL
- Expanded commit messages
- Split mirred and netem test cases into separate patches
- No code changes from v2
Changes in v2:
- Do not reuse skb->from_ingress (which was moved to skb->cb)
Jamal Hadi Salim (5):
net: Introduce skb tc depth field to track packet loops
net/sched: Revert "net/sched: Restrict conditions for adding
duplicating netems to qdisc tree"
Revert "selftests/tc-testing: Add tests for restrictions on netem
duplication"
net/sched: fix packet loop on netem when duplicate is on
net/sched: Fix ethx:ingress -> ethy:egress -> ethx:ingress mirred loop
Victor Nogueira (2):
selftests/tc-testing: Add mirred test cases exercising loops
selftests/tc-testing: Add netem test case exercising loops
include/linux/skbuff.h | 2 +
net/sched/act_mirred.c | 47 +-
net/sched/sch_netem.c | 47 +-
.../tc-testing/tc-tests/actions/mirred.json | 616 +++++++++++++++++-
.../tc-testing/tc-tests/infra/qdiscs.json | 5 +-
.../tc-testing/tc-tests/qdiscs/netem.json | 96 +--
6 files changed, 676 insertions(+), 137 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v3 1/7] net: Introduce skb tc depth field to track packet loops 2026-03-26 18:00 [PATCH net v3 0/7] net/sched: Fix packet loops in mirred and netem Stephen Hemminger @ 2026-03-26 18:01 ` Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 2/7] net/sched: Revert "net/sched: Restrict conditions for adding duplicating netems to qdisc tree" Stephen Hemminger ` (5 subsequent siblings) 6 siblings, 0 replies; 9+ messages in thread From: Stephen Hemminger @ 2026-03-26 18:01 UTC (permalink / raw) To: netdev Cc: Jamal Hadi Salim, Stephen Hemminger, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, open list From: Jamal Hadi Salim <jhs@mojatatu.com> Add a 2-bit per-skb tc depth field to track packet loops across the stack. The previous per-CPU loop counters like MIRRED_NEST_LIMIT assume a single call stack and lose state in two cases: 1) When a packet is queued and reprocessed later (e.g., egress->ingress via backlog), the per-cpu state is gone by the time it is dequeued. 2) With XPS/RPS a packet may arrive on one CPU and be processed on another. A per-skb field solves both by travelling with the packet itself. The field fits in existing padding, using 2 bits that were previously a hole: pahole before(-) and after (+) diff looks like: __u8 slow_gro:1; /* 132: 3 1 */ __u8 csum_not_inet:1; /* 132: 4 1 */ __u8 unreadable:1; /* 132: 5 1 */ + __u8 tc_depth:2; /* 132: 6 1 */ - /* XXX 2 bits hole, try to pack */ /* XXX 1 byte hole, try to pack */ __u16 tc_index; /* 134 2 */ There used to be a ttl field which was removed as part of tc_verd in commit aec745e2c520 ("net-tc: remove unused tc_verd fields"). It was already unused by that time, due to remove earlier in commit c19ae86a510c ("tc: remove unused redirect ttl"). The first user of this field is netem, which increments tc_depth on duplicated packets before re-enqueueing them at the root qdisc. On re-entry, netem skips duplication for any skb with tc_depth already set, bounding recursion to a single level regardless of tree topology. The other user is mirred which increments it on each pass and limits to depth to MIRRED_DEFER_LIMIT (3). The new field was called ttl in earlier versions of this patch but renamed to tc_depth to avoid confusion with IP ttl. Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Stephen Hemminger <stephen@networkplumber.org> --- include/linux/skbuff.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index daa4e4944ce3..aba4c88a1b3f 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -821,6 +821,7 @@ enum skb_tstamp_type { * @_sk_redir: socket redirection information for skmsg * @_nfct: Associated connection, if any (with nfctinfo bits) * @skb_iif: ifindex of device we arrived on + * @tc_depth: counter for packet duplication * @tc_index: Traffic control index * @hash: the packet hash * @queue_mapping: Queue mapping for multiqueue devices @@ -1030,6 +1031,7 @@ struct sk_buff { __u8 csum_not_inet:1; #endif __u8 unreadable:1; + __u8 tc_depth:2; #if defined(CONFIG_NET_SCHED) || defined(CONFIG_NET_XGRESS) __u16 tc_index; /* traffic control index */ #endif -- 2.53.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 2/7] net/sched: Revert "net/sched: Restrict conditions for adding duplicating netems to qdisc tree" 2026-03-26 18:00 [PATCH net v3 0/7] net/sched: Fix packet loops in mirred and netem Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 1/7] net: Introduce skb tc depth field to track packet loops Stephen Hemminger @ 2026-03-26 18:01 ` Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 3/7] Revert "selftests/tc-testing: Add tests for restrictions on netem duplication" Stephen Hemminger ` (4 subsequent siblings) 6 siblings, 0 replies; 9+ messages in thread From: Stephen Hemminger @ 2026-03-26 18:01 UTC (permalink / raw) To: netdev Cc: Jamal Hadi Salim, Ji-Soo Chung, Gerlinde, zyc zyc, Manas Ghandat, Stephen Hemminger, Jiri Pirko, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, William Liu, Savino Dicanosa, open list From: Jamal Hadi Salim <jhs@mojatatu.com> This reverts commit ec8e0e3d7adef940cdf9475e2352c0680189d14e. The original patch rejects any tree containing two netems when either has duplication set, even when they sit on unrelated classes of the same classful parent. That broke configurations that have worked since netem was introduced. The re-entrancy problem the original commit was trying to solve is handled by later patch using tc_depth flag. Doing this revert will (re)expose the original bug with multiple netem duplication. When this patch is backported make sure and get the full series. Fixes: ec8e0e3d7ade ("net/sched: Restrict conditions for adding duplicating netems to qdisc tree") Reported-by: Ji-Soo Chung <jschung2@proton.me> Reported-by: Gerlinde <lrGerlinde@mailfence.com> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220774 Reported-by: zyc zyc <zyc199902@zohomail.cn> Closes: https://lore.kernel.org/all/19adda5a1e2.12410b78222774.9191120410578703463@zohomail.cn/ Reported-by: Manas Ghandat <ghandatmanas@gmail.com> Closes: https://lore.kernel.org/netdev/f69b2c8f-8325-4c2e-a011-6dbc089f30e4@gmail.com/ Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Stephen Hemminger <stephen@networkplumber.org> --- net/sched/sch_netem.c | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 5de1c932944a..0ccf74a9cb82 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -974,41 +974,6 @@ static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla, return 0; } -static const struct Qdisc_class_ops netem_class_ops; - -static int check_netem_in_tree(struct Qdisc *sch, bool duplicates, - struct netlink_ext_ack *extack) -{ - struct Qdisc *root, *q; - unsigned int i; - - root = qdisc_root_sleeping(sch); - - if (sch != root && root->ops->cl_ops == &netem_class_ops) { - if (duplicates || - ((struct netem_sched_data *)qdisc_priv(root))->duplicate) - goto err; - } - - if (!qdisc_dev(root)) - return 0; - - hash_for_each(qdisc_dev(root)->qdisc_hash, i, q, hash) { - if (sch != q && q->ops->cl_ops == &netem_class_ops) { - if (duplicates || - ((struct netem_sched_data *)qdisc_priv(q))->duplicate) - goto err; - } - } - - return 0; - -err: - NL_SET_ERR_MSG(extack, - "netem: cannot mix duplicating netems with other netems in tree"); - return -EINVAL; -} - /* Parse netlink message to set options */ static int netem_change(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack) @@ -1067,11 +1032,6 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, q->gap = qopt->gap; q->counter = 0; q->loss = qopt->loss; - - ret = check_netem_in_tree(sch, qopt->duplicate, extack); - if (ret) - goto unlock; - q->duplicate = qopt->duplicate; /* for compatibility with earlier versions. -- 2.53.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 3/7] Revert "selftests/tc-testing: Add tests for restrictions on netem duplication" 2026-03-26 18:00 [PATCH net v3 0/7] net/sched: Fix packet loops in mirred and netem Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 1/7] net: Introduce skb tc depth field to track packet loops Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 2/7] net/sched: Revert "net/sched: Restrict conditions for adding duplicating netems to qdisc tree" Stephen Hemminger @ 2026-03-26 18:01 ` Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 4/7] net/sched: fix packet loop on netem when duplicate is on Stephen Hemminger ` (3 subsequent siblings) 6 siblings, 0 replies; 9+ messages in thread From: Stephen Hemminger @ 2026-03-26 18:01 UTC (permalink / raw) To: netdev Cc: Jamal Hadi Salim, Stephen Hemminger, Jiri Pirko, Shuah Khan, Victor Nogueira, Cong Wang, Jakub Kicinski, Paolo Abeni, William Liu, Xiang Mei, Savino Dicanosa, open list:KERNEL SELFTEST FRAMEWORK, open list From: Jamal Hadi Salim <jhs@mojatatu.com> This reverts commit ecdec65ec78d67d3ebd17edc88b88312054abe0d. The tests added were related to check_netem_in_tree() which was just reverted in the previous patch. Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Stephen Hemminger <stephen@networkplumber.org> --- .../tc-testing/tc-tests/infra/qdiscs.json | 5 +- .../tc-testing/tc-tests/qdiscs/netem.json | 81 ------------------- 2 files changed, 3 insertions(+), 83 deletions(-) diff --git a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json index 6a39640aa2a8..ceb993ed04b2 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json +++ b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json @@ -702,6 +702,7 @@ "$TC qdisc add dev $DUMMY parent 1:1 handle 2:0 netem duplicate 100%", "$TC filter add dev $DUMMY parent 1:0 protocol ip prio 1 u32 match ip dst 10.10.10.1/32 flowid 1:1", "$TC class add dev $DUMMY parent 1:0 classid 1:2 hfsc ls m2 10Mbit", + "$TC qdisc add dev $DUMMY parent 1:2 handle 3:0 netem duplicate 100%", "$TC filter add dev $DUMMY parent 1:0 protocol ip prio 2 u32 match ip dst 10.10.10.2/32 flowid 1:2", "ping -c 1 10.10.10.1 -I$DUMMY > /dev/null || true", "$TC filter del dev $DUMMY parent 1:0 protocol ip prio 1", @@ -714,8 +715,8 @@ { "kind": "hfsc", "handle": "1:", - "bytes": 294, - "packets": 3 + "bytes": 392, + "packets": 4 } ], "matchCount": "1", diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json index 718d2df2aafa..3c4444961488 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json @@ -336,86 +336,5 @@ "teardown": [ "$TC qdisc del dev $DUMMY handle 1: root" ] - }, - { - "id": "d34d", - "name": "NETEM test qdisc duplication restriction in qdisc tree in netem_change root", - "category": ["qdisc", "netem"], - "plugins": { - "requires": "nsPlugin" - }, - "setup": [ - "$TC qdisc add dev $DUMMY root handle 1: netem limit 1", - "$TC qdisc add dev $DUMMY parent 1: handle 2: netem limit 1" - ], - "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: netem duplicate 50%", - "expExitCode": "2", - "verifyCmd": "$TC -s qdisc show dev $DUMMY", - "matchPattern": "qdisc netem", - "matchCount": "2", - "teardown": [ - "$TC qdisc del dev $DUMMY handle 1:0 root" - ] - }, - { - "id": "b33f", - "name": "NETEM test qdisc duplication restriction in qdisc tree in netem_change non-root", - "category": ["qdisc", "netem"], - "plugins": { - "requires": "nsPlugin" - }, - "setup": [ - "$TC qdisc add dev $DUMMY root handle 1: netem limit 1", - "$TC qdisc add dev $DUMMY parent 1: handle 2: netem limit 1" - ], - "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 2: netem duplicate 50%", - "expExitCode": "2", - "verifyCmd": "$TC -s qdisc show dev $DUMMY", - "matchPattern": "qdisc netem", - "matchCount": "2", - "teardown": [ - "$TC qdisc del dev $DUMMY handle 1:0 root" - ] - }, - { - "id": "cafe", - "name": "NETEM test qdisc duplication restriction in qdisc tree", - "category": ["qdisc", "netem"], - "plugins": { - "requires": "nsPlugin" - }, - "setup": [ - "$TC qdisc add dev $DUMMY root handle 1: netem limit 1 duplicate 100%" - ], - "cmdUnderTest": "$TC qdisc add dev $DUMMY parent 1: handle 2: netem duplicate 100%", - "expExitCode": "2", - "verifyCmd": "$TC -s qdisc show dev $DUMMY", - "matchPattern": "qdisc netem", - "matchCount": "1", - "teardown": [ - "$TC qdisc del dev $DUMMY handle 1:0 root" - ] - }, - { - "id": "1337", - "name": "NETEM test qdisc duplication restriction in qdisc tree across branches", - "category": ["qdisc", "netem"], - "plugins": { - "requires": "nsPlugin" - }, - "setup": [ - "$TC qdisc add dev $DUMMY parent root handle 1:0 hfsc", - "$TC class add dev $DUMMY parent 1:0 classid 1:1 hfsc rt m2 10Mbit", - "$TC qdisc add dev $DUMMY parent 1:1 handle 2:0 netem", - "$TC class add dev $DUMMY parent 1:0 classid 1:2 hfsc rt m2 10Mbit" - ], - "cmdUnderTest": "$TC qdisc add dev $DUMMY parent 1:2 handle 3:0 netem duplicate 100%", - "expExitCode": "2", - "verifyCmd": "$TC -s qdisc show dev $DUMMY", - "matchPattern": "qdisc netem", - "matchCount": "1", - "teardown": [ - "$TC qdisc del dev $DUMMY handle 1:0 root" - ] } ] -- 2.53.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 4/7] net/sched: fix packet loop on netem when duplicate is on 2026-03-26 18:00 [PATCH net v3 0/7] net/sched: Fix packet loops in mirred and netem Stephen Hemminger ` (2 preceding siblings ...) 2026-03-26 18:01 ` [PATCH v3 3/7] Revert "selftests/tc-testing: Add tests for restrictions on netem duplication" Stephen Hemminger @ 2026-03-26 18:01 ` Stephen Hemminger 2026-03-27 7:00 ` William Liu 2026-03-26 18:01 ` [PATCH v3 5/7] net/sched: Fix ethx:ingress -> ethy:egress -> ethx:ingress mirred loop Stephen Hemminger ` (2 subsequent siblings) 6 siblings, 1 reply; 9+ messages in thread From: Stephen Hemminger @ 2026-03-26 18:01 UTC (permalink / raw) To: netdev Cc: Jamal Hadi Salim, William Liu, Savino Dicanosa, Victor Nogueira, Stephen Hemminger, Jiri Pirko, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, open list From: Jamal Hadi Salim <jhs@mojatatu.com> When netem duplicates a packet it re-enqueues the copy at the root qdisc. If another netem sits in the tree the copy can be duplicated again, recursing until the stack or memory is exhausted. The original duplication guard temporarily zeroed q->duplicate around the re-enqueue, but that does not cover all cases because it is per-qdisc state shared across all concurrent enqueue paths and is not safe without additional locking. Use the skb tc_depth field introduced in an earlier patch: - increment it on the duplicate before re-enqueue - skip duplication for any skb whose tc_depth is already non-zero. This marks the packet itself rather than mutating qdisc state, therefore it is safe regardless of tree topology or concurrency. Fixes: 0afb51e72855 ("[PKT_SCHED]: netem: reinsert for duplication") Reported-by: William Liu <will@willsroot.io> Reported-by: Savino Dicanosa <savy@syst3mfailure.io> Closes: https://lore.kernel.org/netdev/8DuRWwfqjoRDLDmBMlIfbrsZg9Gx50DHJc1ilxsEBNe2D6NMoigR_eIRIG0LOjMc3r10nUUZtArXx4oZBIdUfZQrwjcQhdinnMis_0G7VEk=@willsroot.io/ Co-developed-by: Victor Nogueira <victor@mojatatu.com> Signed-off-by: Victor Nogueira <victor@mojatatu.com> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Stephen Hemminger <stephen@networkplumber.org> --- net/sched/sch_netem.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 0ccf74a9cb82..6086700eb1e7 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -461,7 +461,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, skb->prev = NULL; /* Random duplication */ - if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor, &q->prng)) + if (q->duplicate && skb->tc_depth == 0 && + q->duplicate >= get_crandom(&q->dup_cor, &q->prng)) ++count; /* Drop packet? */ @@ -539,11 +540,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, */ if (skb2) { struct Qdisc *rootq = qdisc_root_bh(sch); - u32 dupsave = q->duplicate; /* prevent duplicating a dup... */ - q->duplicate = 0; + skb2->tc_depth++; /* prevent duplicating a dup... */ rootq->enqueue(skb2, rootq, to_free); - q->duplicate = dupsave; skb2 = NULL; } -- 2.53.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v3 4/7] net/sched: fix packet loop on netem when duplicate is on 2026-03-26 18:01 ` [PATCH v3 4/7] net/sched: fix packet loop on netem when duplicate is on Stephen Hemminger @ 2026-03-27 7:00 ` William Liu 0 siblings, 0 replies; 9+ messages in thread From: William Liu @ 2026-03-27 7:00 UTC (permalink / raw) To: Stephen Hemminger Cc: netdev, Jamal Hadi Salim, Savino Dicanosa, Victor Nogueira, Jiri Pirko, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel Reviewed-by: William Liu <will@willsroot.io> On Thursday, March 26th, 2026 at 6:17 PM, Stephen Hemminger <stephen@networkplumber.org> wrote: > From: Jamal Hadi Salim <jhs@mojatatu.com> > > When netem duplicates a packet it re-enqueues the copy at the root qdisc. > If another netem sits in the tree the copy can be duplicated > again, recursing until the stack or memory is exhausted. > > The original duplication guard temporarily zeroed q->duplicate around > the re-enqueue, but that does not cover all cases because it is > per-qdisc state shared across all concurrent enqueue paths > and is not safe without additional locking. > > Use the skb tc_depth field introduced in an earlier patch: > - increment it on the duplicate before re-enqueue > - skip duplication for any skb whose tc_depth is already non-zero. > > This marks the packet itself rather than mutating qdisc state, > therefore it is safe regardless of tree topology or concurrency. > > Fixes: 0afb51e72855 ("[PKT_SCHED]: netem: reinsert for duplication") > Reported-by: William Liu <will@willsroot.io> > Reported-by: Savino Dicanosa <savy@syst3mfailure.io> > Closes: https://lore.kernel.org/netdev/8DuRWwfqjoRDLDmBMlIfbrsZg9Gx50DHJc1ilxsEBNe2D6NMoigR_eIRIG0LOjMc3r10nUUZtArXx4oZBIdUfZQrwjcQhdinnMis_0G7VEk=@willsroot.io/ > Co-developed-by: Victor Nogueira <victor@mojatatu.com> > Signed-off-by: Victor Nogueira <victor@mojatatu.com> > Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> > Reviewed-by: Stephen Hemminger <stephen@networkplumber.org> > --- > net/sched/sch_netem.c | 7 +++---- > 1 file changed, 3 insertions(+), 4 deletions(-) > > diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c > index 0ccf74a9cb82..6086700eb1e7 100644 > --- a/net/sched/sch_netem.c > +++ b/net/sched/sch_netem.c > @@ -461,7 +461,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, > skb->prev = NULL; > > /* Random duplication */ > - if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor, &q->prng)) > + if (q->duplicate && skb->tc_depth == 0 && > + q->duplicate >= get_crandom(&q->dup_cor, &q->prng)) > ++count; > > /* Drop packet? */ > @@ -539,11 +540,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, > */ > if (skb2) { > struct Qdisc *rootq = qdisc_root_bh(sch); > - u32 dupsave = q->duplicate; /* prevent duplicating a dup... */ > > - q->duplicate = 0; > + skb2->tc_depth++; /* prevent duplicating a dup... */ > rootq->enqueue(skb2, rootq, to_free); > - q->duplicate = dupsave; > skb2 = NULL; > } > > -- > 2.53.0 > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 5/7] net/sched: Fix ethx:ingress -> ethy:egress -> ethx:ingress mirred loop 2026-03-26 18:00 [PATCH net v3 0/7] net/sched: Fix packet loops in mirred and netem Stephen Hemminger ` (3 preceding siblings ...) 2026-03-26 18:01 ` [PATCH v3 4/7] net/sched: fix packet loop on netem when duplicate is on Stephen Hemminger @ 2026-03-26 18:01 ` Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 6/7] selftests/tc-testing: Add mirred test cases exercising loops Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 7/7] selftests/tc-testing: Add netem test case " Stephen Hemminger 6 siblings, 0 replies; 9+ messages in thread From: Stephen Hemminger @ 2026-03-26 18:01 UTC (permalink / raw) To: netdev Cc: Jamal Hadi Salim, Victor Nogueira, Stephen Hemminger, Jiri Pirko, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, Toke Høiland-Jørgensen, Kuniyuki Iwashima, open list From: Jamal Hadi Salim <jhs@mojatatu.com> When mirred redirects to ingress (from either ingress or egress) the loop state from sched_mirred_dev array dev is lost because of 1) the packet deferral into the backlog and 2) the fact the sched_mirred_dev array is cleared. In such cases, if there was a loop we won't discover it. Here's a simple test to reproduce: ip a add dev port0 10.10.10.11/24 tc qdisc add dev port0 clsact tc filter add dev port0 egress protocol ip \ prio 10 matchall action mirred ingress redirect dev port1 tc qdisc add dev port1 clsact tc filter add dev port1 ingress protocol ip \ prio 10 matchall action mirred egress redirect dev port0 ping -c 1 -W0.01 10.10.10.10 Fixes: fe946a751d9b ("net/sched: act_mirred: add loop detection") Tested-by: Victor Nogueira <victor@mojatatu.com> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Stephen Hemminger <stephen@networkplumber.org> --- net/sched/act_mirred.c | 47 +++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 05e0b14b5773..001dd9275e9b 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -26,6 +26,10 @@ #include <net/tc_act/tc_mirred.h> #include <net/tc_wrapper.h> +#define MIRRED_DEFER_LIMIT 3 +_Static_assert(MIRRED_DEFER_LIMIT <= 3, + "MIRRED_DEFER_LIMIT exceeds tc_depth bitfield width"); + static LIST_HEAD(mirred_list); static DEFINE_SPINLOCK(mirred_list_lock); @@ -234,12 +238,15 @@ tcf_mirred_forward(bool at_ingress, bool want_ingress, struct sk_buff *skb) { int err; - if (!want_ingress) + if (!want_ingress) { err = tcf_dev_queue_xmit(skb, dev_queue_xmit); - else if (!at_ingress) - err = netif_rx(skb); - else - err = netif_receive_skb(skb); + } else { + skb->tc_depth++; + if (!at_ingress) + err = netif_rx(skb); + else + err = netif_receive_skb(skb); + } return err; } @@ -426,6 +433,7 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb, struct netdev_xmit *xmit; bool m_mac_header_xmit; struct net_device *dev; + bool want_ingress; int i, m_eaction; u32 blockid; @@ -434,7 +442,8 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb, #else xmit = this_cpu_ptr(&softnet_data.xmit); #endif - if (unlikely(xmit->sched_mirred_nest >= MIRRED_NEST_LIMIT)) { + if (unlikely(xmit->sched_mirred_nest >= MIRRED_NEST_LIMIT || + skb->tc_depth >= MIRRED_DEFER_LIMIT)) { net_warn_ratelimited("Packet exceeded mirred recursion limit on dev %s\n", netdev_name(skb->dev)); return TC_ACT_SHOT; @@ -453,23 +462,27 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb, tcf_action_inc_overlimit_qstats(&m->common); return retval; } - for (i = 0; i < xmit->sched_mirred_nest; i++) { - if (xmit->sched_mirred_dev[i] != dev) - continue; - pr_notice_once("tc mirred: loop on device %s\n", - netdev_name(dev)); - tcf_action_inc_overlimit_qstats(&m->common); - return retval; - } - xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = dev; + m_eaction = READ_ONCE(m->tcfm_eaction); + want_ingress = tcf_mirred_act_wants_ingress(m_eaction); + if (!want_ingress) { + for (i = 0; i < xmit->sched_mirred_nest; i++) { + if (xmit->sched_mirred_dev[i] != dev) + continue; + pr_notice_once("tc mirred: loop on device %s\n", + netdev_name(dev)); + tcf_action_inc_overlimit_qstats(&m->common); + return retval; + } + xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = dev; + } m_mac_header_xmit = READ_ONCE(m->tcfm_mac_header_xmit); - m_eaction = READ_ONCE(m->tcfm_eaction); retval = tcf_mirred_to_dev(skb, m, dev, m_mac_header_xmit, m_eaction, retval); - xmit->sched_mirred_nest--; + if (!want_ingress) + xmit->sched_mirred_nest--; return retval; } -- 2.53.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 6/7] selftests/tc-testing: Add mirred test cases exercising loops 2026-03-26 18:00 [PATCH net v3 0/7] net/sched: Fix packet loops in mirred and netem Stephen Hemminger ` (4 preceding siblings ...) 2026-03-26 18:01 ` [PATCH v3 5/7] net/sched: Fix ethx:ingress -> ethy:egress -> ethx:ingress mirred loop Stephen Hemminger @ 2026-03-26 18:01 ` Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 7/7] selftests/tc-testing: Add netem test case " Stephen Hemminger 6 siblings, 0 replies; 9+ messages in thread From: Stephen Hemminger @ 2026-03-26 18:01 UTC (permalink / raw) To: netdev Cc: Victor Nogueira, Jamal Hadi Salim, Stephen Hemminger, Jiri Pirko, Shuah Khan, Paolo Abeni, Jakub Kicinski, open list:KERNEL SELFTEST FRAMEWORK, open list From: Victor Nogueira <victor@mojatatu.com> Add mirred loop test cases to validate that those will be caught and other test cases that were previously misinterpreted as loops by mirred. This commit adds 12 test cases: - Redirect multiport: dummy egress -> dev1 ingress -> dummy egress (Loop) - Redirect singleport: dev1 ingress -> dev1 egress -> dev1 ingress (Loop) - Redirect multiport: dev1 ingress -> dummy ingress -> dev1 egress (No Loop) - Redirect multiport: dev1 ingress -> dummy ingress -> dev1 ingress (Loop) - Redirect multiport: dev1 ingress -> dummy egress -> dev1 ingress (Loop) - Redirect multiport: dummy egress -> dev1 ingress -> dummy egress, different prios (Loop) - Redirect multiport: dev1 ingress -> dummy ingress -> dummy egress -> dev1 egress (No Loop) - Redirect multiport: dev1 ingress -> dummy egress -> dev1 egress (No Loop) - Redirect multiport: dev1 ingress -> dummy egress -> dummy ingress (No Loop) - Redirect singleport: dev1 ingress -> dev1 ingress (Loop) - Redirect singleport: dummy egress -> dummy ingress (No Loop) - Redirect multiport: dev1 ingress -> dummy ingress -> dummy egress (No Loop) Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Acked-by: Stephen Hemminger <stephen@networkplumber.org> Signed-off-by: Victor Nogueira <victor@mojatatu.com> --- .../tc-testing/tc-tests/actions/mirred.json | 616 +++++++++++++++++- 1 file changed, 615 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json index b056eb966871..d0cad6571691 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json @@ -1144,6 +1144,620 @@ "teardown": [ "$TC qdisc del dev $DUMMY clsact" ] + }, + { + "id": "531c", + "name": "Redirect multiport: dummy egress -> dev1 ingress -> dummy egress (Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin" + ] + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.10.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY clsact", + "$TC filter add dev $DUMMY egress protocol ip prio 10 matchall action mirred ingress redirect dev $DEV1 index 1", + "$TC qdisc add dev $DEV1 clsact", + "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 2" + ], + "cmdUnderTest": "ping -c1 -W0.01 -I $DUMMY 10.10.10.1", + "expExitCode": "1", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "ingress", + "index": 1, + "stats": { + "packets": 3 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY clsact", + "$TC qdisc del dev $DEV1 clsact" + ] + }, + { + "id": "b1d7", + "name": "Redirect singleport: dev1 ingress -> dev1 egress -> dev1 ingress (Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$TC qdisc add dev $DEV1 clsact", + "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DEV1 index 1" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 egress protocol ip prio 11 matchall action mirred ingress redirect dev $DEV1 index 2", + "scapy": [ + { + "iface": "$DEV0", + "count": 1, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + } + ], + "expExitCode": "0", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "egress", + "index": 1, + "stats": { + "packets": 3 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 clsact" + ] + }, + { + "id": "c66d", + "name": "Redirect multiport: dev1 ingress -> dummy ingress -> dev1 egress (No Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$TC qdisc add dev $DEV1 clsact", + "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DUMMY index 1", + "$TC qdisc add dev $DUMMY clsact" + ], + "cmdUnderTest": "$TC filter add dev $DUMMY ingress protocol ip prio 11 matchall action mirred egress redirect dev $DEV1 index 2", + "scapy": [ + { + "iface": "$DEV0", + "count": 1, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + } + ], + "expExitCode": "0", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "ingress", + "index": 1, + "stats": { + "packets": 1 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 clsact", + "$TC qdisc del dev $DUMMY clsact" + ] + }, + { + "id": "aa99", + "name": "Redirect multiport: dev1 ingress -> dummy ingress -> dev1 ingress (Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$TC qdisc add dev $DEV1 clsact", + "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DUMMY index 1", + "$TC qdisc add dev $DUMMY clsact" + ], + "cmdUnderTest": "$TC filter add dev $DUMMY ingress protocol ip prio 11 matchall action mirred ingress redirect dev $DEV1 index 2", + "scapy": [ + { + "iface": "$DEV0", + "count": 1, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + } + ], + "expExitCode": "0", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "ingress", + "index": 1, + "stats": { + "packets": 2, + "overlimits": 1 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 clsact", + "$TC qdisc del dev $DUMMY clsact" + ] + }, + { + "id": "37d7", + "name": "Redirect multiport: dev1 ingress -> dummy egress -> dev1 ingress (Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$TC qdisc add dev $DEV1 clsact", + "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 1", + "$TC qdisc add dev $DUMMY clsact" + ], + "cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 11 matchall action mirred ingress redirect dev $DEV1 index 2", + "scapy": [ + { + "iface": "$DEV0", + "count": 1, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + } + ], + "expExitCode": "0", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "egress", + "index": 1, + "stats": { + "packets": 3 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 clsact", + "$TC qdisc del dev $DUMMY clsact" + ] + }, + { + "id": "6d02", + "name": "Redirect multiport: dummy egress -> dev1 ingress -> dummy egress, different prios (Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin" + ] + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.10.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY clsact", + "$TC filter add dev $DUMMY egress protocol ip prio 10 matchall action mirred ingress redirect dev $DEV1 index 1", + "$TC qdisc add dev $DEV1 clsact", + "$TC filter add dev $DEV1 ingress protocol ip prio 11 matchall action mirred egress redirect dev $DUMMY index 2" + ], + "cmdUnderTest": "ping -c1 -W0.01 -I $DUMMY 10.10.10.1", + "expExitCode": "1", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "ingress", + "index": 1, + "stats": { + "packets": 3 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY clsact", + "$TC qdisc del dev $DEV1 clsact" + ] + }, + { + "id": "8115", + "name": "Redirect multiport: dev1 ingress -> dummy ingress -> dummy egress -> dev1 egress (No Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$TC qdisc add dev $DEV1 clsact", + "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DUMMY index 1", + "$TC qdisc add dev $DUMMY clsact", + "$TC filter add dev $DUMMY ingress protocol ip prio 11 matchall action mirred egress redirect dev $DUMMY index 2" + ], + "cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 12 matchall action mirred egress redirect dev $DEV1 index 3", + "scapy": [ + { + "iface": "$DEV0", + "count": 1, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + } + ], + "expExitCode": "0", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "ingress", + "index": 1, + "stats": { + "packets": 1 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 clsact", + "$TC qdisc del dev $DUMMY clsact" + ] + }, + { + "id": "9eb3", + "name": "Redirect multiport: dev1 ingress -> dummy egress -> dev1 egress (No Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$TC qdisc add dev $DEV1 clsact", + "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 1", + "$TC qdisc add dev $DUMMY clsact" + ], + "cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 11 matchall action mirred egress redirect dev $DEV1 index 2", + "scapy": [ + { + "iface": "$DEV0", + "count": 1, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + } + ], + "expExitCode": "0", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "egress", + "index": 1, + "stats": { + "packets": 1 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 clsact", + "$TC qdisc del dev $DUMMY clsact" + ] + }, + { + "id": "d837", + "name": "Redirect multiport: dev1 ingress -> dummy egress -> dummy ingress (No Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$TC qdisc add dev $DEV1 clsact", + "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 1", + "$TC qdisc add dev $DUMMY clsact" + ], + "cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 11 matchall action mirred ingress redirect dev $DUMMY index 2", + "scapy": [ + { + "iface": "$DEV0", + "count": 1, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + } + ], + "expExitCode": "0", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "egress", + "index": 1, + "stats": { + "packets": 1 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 clsact", + "$TC qdisc del dev $DUMMY clsact" + ] + }, + { + "id": "2071", + "name": "Redirect singleport: dev1 ingress -> dev1 ingress (Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$TC qdisc add dev $DEV1 clsact" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DEV1 index 1", + "scapy": [ + { + "iface": "$DEV0", + "count": 1, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + } + ], + "expExitCode": "0", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "ingress", + "index": 1, + "stats": { + "packets": 1, + "overlimits": 1 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 clsact" + ] + }, + { + "id": "0101", + "name": "Redirect singleport: dummy egress -> dummy ingress (No Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin" + ] + }, + "setup": [ + "$IP addr add 10.10.10.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY clsact", + "$TC filter add dev $DUMMY egress protocol ip prio 11 matchall action mirred ingress redirect dev $DUMMY index 1" + ], + "cmdUnderTest": "ping -c1 -W0.01 -I $DUMMY 10.10.10.1", + "expExitCode": "1", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "ingress", + "index": 1, + "stats": { + "packets": 1 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY clsact" + ] + }, + { + "id": "cf97", + "name": "Redirect multiport: dev1 ingress -> dummy ingress -> dummy egress (No Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$TC qdisc add dev $DEV1 clsact", + "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DUMMY index 1", + "$TC qdisc add dev $DUMMY clsact" + ], + "cmdUnderTest": "$TC filter add dev $DUMMY ingress protocol ip prio 11 matchall action mirred egress redirect dev $DUMMY index 2", + "scapy": [ + { + "iface": "$DEV0", + "count": 1, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + } + ], + "expExitCode": "0", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "ingress", + "index": 1, + "stats": { + "packets": 1 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 clsact", + "$TC qdisc del dev $DUMMY clsact" + ] } - ] -- 2.53.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 7/7] selftests/tc-testing: Add netem test case exercising loops 2026-03-26 18:00 [PATCH net v3 0/7] net/sched: Fix packet loops in mirred and netem Stephen Hemminger ` (5 preceding siblings ...) 2026-03-26 18:01 ` [PATCH v3 6/7] selftests/tc-testing: Add mirred test cases exercising loops Stephen Hemminger @ 2026-03-26 18:01 ` Stephen Hemminger 6 siblings, 0 replies; 9+ messages in thread From: Stephen Hemminger @ 2026-03-26 18:01 UTC (permalink / raw) To: netdev Cc: Victor Nogueira, Jamal Hadi Salim, Stephen Hemminger, Jiri Pirko, Shuah Khan, William Liu, Jakub Kicinski, Savino Dicanosa, open list:KERNEL SELFTEST FRAMEWORK, open list From: Victor Nogueira <victor@mojatatu.com> Add a netem nested duplicate test case to validate that it won't cause an infinite loop Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Acked-by: Stephen Hemminger <stephen@networkplumber.org> Signed-off-by: Victor Nogueira <victor@mojatatu.com> --- .../tc-testing/tc-tests/qdiscs/netem.json | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json index 3c4444961488..7c954989069d 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json @@ -336,5 +336,36 @@ "teardown": [ "$TC qdisc del dev $DUMMY handle 1: root" ] - } + }, + { + "id": "8c17", + "name": "Test netem's recursive duplicate", + "category": [ + "qdisc", + "netem" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.11.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY root handle 1: netem limit 1 duplicate 100%", + "$TC qdisc add dev $DUMMY parent 1: handle 2: netem duplicate 100%" + ], + "cmdUnderTest": "ping -c 1 10.10.11.11 -W 0.01", + "expExitCode": "1", + "verifyCmd": "$TC -s -j qdisc ls dev $DUMMY root", + "matchJSON": [ + { + "kind": "netem", + "handle": "1:", + "bytes": 294, + "packets": 3 + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + } ] -- 2.53.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-03-27 7:00 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-03-26 18:00 [PATCH net v3 0/7] net/sched: Fix packet loops in mirred and netem Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 1/7] net: Introduce skb tc depth field to track packet loops Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 2/7] net/sched: Revert "net/sched: Restrict conditions for adding duplicating netems to qdisc tree" Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 3/7] Revert "selftests/tc-testing: Add tests for restrictions on netem duplication" Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 4/7] net/sched: fix packet loop on netem when duplicate is on Stephen Hemminger 2026-03-27 7:00 ` William Liu 2026-03-26 18:01 ` [PATCH v3 5/7] net/sched: Fix ethx:ingress -> ethy:egress -> ethx:ingress mirred loop Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 6/7] selftests/tc-testing: Add mirred test cases exercising loops Stephen Hemminger 2026-03-26 18:01 ` [PATCH v3 7/7] selftests/tc-testing: Add netem test case " Stephen Hemminger
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox