Commit db875221 authored by Jamal Hadi Salim's avatar Jamal Hadi Salim Committed by Paolo Abeni
Browse files

net/sched: Fix ethx:ingress -> ethy:egress -> ethx:ingress mirred loop



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: fe946a75 ("net/sched: act_mirred: add loop detection")
Tested-by: default avatarVictor Nogueira <victor@mojatatu.com>
Reviewed-by: default avatarStephen Hemminger <stephen@networkplumber.org>
Signed-off-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
Link: https://patch.msgid.link/20260525122556.973584-6-jhs@mojatatu.com


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 9552b11e
Loading
Loading
Loading
Loading
+30 −17
Original line number Diff line number Diff line
@@ -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)
	} 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,6 +462,10 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
		tcf_action_inc_overlimit_qstats(&m->common);
		return retval;
	}

	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;
@@ -461,14 +474,14 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
			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);
	if (!want_ingress)
		xmit->sched_mirred_nest--;

	return retval;