Merge branch 'net-sched-fix-packet-loops-in-mirred-and-netem'

Jamal Hadi Salim says:

====================
net/sched: Fix packet loops in mirred and netem

This patchset adds 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.
Patch 6 fixes the infinite loop in the mirred egress blockcast case.
Patch 7 fixes drop stats in early return error scenarios in tcf_mirred_act
for redirect (caught by Sashiko [1]).
Patches 8-9 add mirred and netem test cases.

[1] https://sashiko.dev/#/patchset/20260413082027.2244884-1-hxzene%40gmail.com
====================

Link: https://patch.msgid.link/20260525122556.973584-1-jhs@mojatatu.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-05-28 12:26:38 +02:00
commit 031f1592e5
6 changed files with 696 additions and 145 deletions

View File

@ -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

View File

@ -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;
}
@ -365,7 +372,8 @@ static int tcf_blockcast_redir(struct sk_buff *skb, struct tcf_mirred *m,
dev_is_mac_header_xmit(dev_prev),
m_eaction, retval);
return retval;
/* If the packet wasn't redirected, we have to register as a drop */
return TC_ACT_SHOT;
}
static int tcf_blockcast_mirror(struct sk_buff *skb, struct tcf_mirred *m,
@ -389,14 +397,12 @@ static int tcf_blockcast_mirror(struct sk_buff *skb, struct tcf_mirred *m,
static int tcf_blockcast(struct sk_buff *skb, struct tcf_mirred *m,
const u32 blockid, struct tcf_result *res,
int retval)
int m_eaction, int retval)
{
const u32 exception_ifindex = skb->dev->ifindex;
struct tcf_block *block;
bool is_redirect;
int m_eaction;
m_eaction = READ_ONCE(m->tcfm_eaction);
is_redirect = tcf_mirred_is_act_redirect(m_eaction);
/* we are already under rcu protection, so can call block lookup
@ -405,7 +411,7 @@ static int tcf_blockcast(struct sk_buff *skb, struct tcf_mirred *m,
block = tcf_block_lookup(dev_net(skb->dev), blockid);
if (!block || xa_empty(&block->ports)) {
tcf_action_inc_overlimit_qstats(&m->common);
return retval;
return is_redirect ? TC_ACT_SHOT : retval;
}
if (is_redirect)
@ -423,9 +429,10 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
{
struct tcf_mirred *m = to_mirred(a);
int retval = READ_ONCE(m->tcf_action);
bool m_mac_header_xmit, is_redirect;
struct netdev_xmit *xmit;
bool m_mac_header_xmit;
struct net_device *dev;
bool want_ingress;
int i, m_eaction;
u32 blockid;
@ -434,7 +441,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;
@ -444,34 +452,51 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
tcf_action_update_bstats(&m->common, skb);
blockid = READ_ONCE(m->tcfm_blockid);
if (blockid)
return tcf_blockcast(skb, m, blockid, res, retval);
m_eaction = READ_ONCE(m->tcfm_eaction);
want_ingress = tcf_mirred_act_wants_ingress(m_eaction);
if (blockid) {
if (!want_ingress)
xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = NULL;
retval = tcf_blockcast(skb, m, blockid, res, m_eaction, retval);
if (!want_ingress)
xmit->sched_mirred_nest--;
return retval;
}
is_redirect = tcf_mirred_is_act_redirect(m_eaction);
dev = rcu_dereference_bh(m->tcfm_dev);
if (unlikely(!dev)) {
pr_notice_once("tc mirred: target device is gone\n");
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;
goto err_out;
}
xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = dev;
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);
goto err_out;
}
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;
err_out:
if (is_redirect)
retval = TC_ACT_SHOT;
return retval;
}
static void tcf_stats_update(struct tc_action *a, u64 bytes, u64 packets,

View File

@ -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? */
@ -540,11 +541,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;
}
@ -1007,41 +1006,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)
@ -1118,11 +1082,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.

View File

@ -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"
]
}
]

View File

@ -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",

View File

@ -338,84 +338,34 @@
]
},
{
"id": "d34d",
"name": "NETEM test qdisc duplication restriction in qdisc tree in netem_change root",
"category": ["qdisc", "netem"],
"id": "8c17",
"name": "Test netem's recursive duplicate",
"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"
"$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 1000 duplicate 100%",
"$TC qdisc add dev $DUMMY parent 1: handle 2: netem limit 1000 duplicate 100%"
],
"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": "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
}
],
"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"
"$TC qdisc del dev $DUMMY handle 1: 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"
]
}
}
]