Commit d961bee4 authored by Paolo Abeni's avatar Paolo Abeni
Browse files

Merge branch 'net-sched-use-the-backlog-for-nested-mirred-ingress'

Davide Caratti says:

====================
net/sched: use the backlog for nested mirred ingress

TC mirred has a protection against excessive stack growth, but that
protection doesn't really guarantee the absence of recursion, nor
it guards against loops. Patch 1/2 rewords "recursion" to "nesting" to
make this more clear.
We can leverage on this existing mechanism to prevent TCP / SCTP from doing
soft lock-up in some specific scenarios that uses mirred egress->ingress:
patch 2 changes mirred so that the networking backlog is used for nested
mirred ingress actions.
====================

Link: https://lore.kernel.org/r/cover.1674233458.git.dcaratti@redhat.com


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents 5cf6c22b ca22da2f
Loading
Loading
Loading
Loading
+15 −8
Original line number Diff line number Diff line
@@ -29,8 +29,8 @@
static LIST_HEAD(mirred_list);
static DEFINE_SPINLOCK(mirred_list_lock);

#define MIRRED_RECURSION_LIMIT    4
static DEFINE_PER_CPU(unsigned int, mirred_rec_level);
#define MIRRED_NEST_LIMIT    4
static DEFINE_PER_CPU(unsigned int, mirred_nest_level);

static bool tcf_mirred_is_act_redirect(int action)
{
@@ -206,12 +206,19 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
	return err;
}

static bool is_mirred_nested(void)
{
	return unlikely(__this_cpu_read(mirred_nest_level) > 1);
}

static int tcf_mirred_forward(bool want_ingress, struct sk_buff *skb)
{
	int err;

	if (!want_ingress)
		err = tcf_dev_queue_xmit(skb, dev_queue_xmit);
	else if (is_mirred_nested())
		err = netif_rx(skb);
	else
		err = netif_receive_skb(skb);

@@ -226,7 +233,7 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
	struct sk_buff *skb2 = skb;
	bool m_mac_header_xmit;
	struct net_device *dev;
	unsigned int rec_level;
	unsigned int nest_level;
	int retval, err = 0;
	bool use_reinsert;
	bool want_ingress;
@@ -237,11 +244,11 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
	int mac_len;
	bool at_nh;

	rec_level = __this_cpu_inc_return(mirred_rec_level);
	if (unlikely(rec_level > MIRRED_RECURSION_LIMIT)) {
	nest_level = __this_cpu_inc_return(mirred_nest_level);
	if (unlikely(nest_level > MIRRED_NEST_LIMIT)) {
		net_warn_ratelimited("Packet exceeded mirred recursion limit on dev %s\n",
				     netdev_name(skb->dev));
		__this_cpu_dec(mirred_rec_level);
		__this_cpu_dec(mirred_nest_level);
		return TC_ACT_SHOT;
	}

@@ -310,7 +317,7 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
			err = tcf_mirred_forward(want_ingress, skb);
			if (err)
				tcf_action_inc_overlimit_qstats(&m->common);
			__this_cpu_dec(mirred_rec_level);
			__this_cpu_dec(mirred_nest_level);
			return TC_ACT_CONSUMED;
		}
	}
@@ -322,7 +329,7 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
		if (tcf_mirred_is_act_redirect(m_eaction))
			retval = TC_ACT_SHOT;
	}
	__this_cpu_dec(mirred_rec_level);
	__this_cpu_dec(mirred_nest_level);

	return retval;
}
+48 −1
Original line number Diff line number Diff line
@@ -3,7 +3,8 @@

ALL_TESTS="gact_drop_and_ok_test mirred_egress_redirect_test \
	mirred_egress_mirror_test matchall_mirred_egress_mirror_test \
	gact_trap_test mirred_egress_to_ingress_test"
	gact_trap_test mirred_egress_to_ingress_test \
	mirred_egress_to_ingress_tcp_test"
NUM_NETIFS=4
source tc_common.sh
source lib.sh
@@ -198,6 +199,52 @@ mirred_egress_to_ingress_test()
	log_test "mirred_egress_to_ingress ($tcflags)"
}

mirred_egress_to_ingress_tcp_test()
{
	local tmpfile=$(mktemp) tmpfile1=$(mktemp)

	RET=0
	dd conv=sparse status=none if=/dev/zero bs=1M count=2 of=$tmpfile
	tc filter add dev $h1 protocol ip pref 100 handle 100 egress flower \
		$tcflags ip_proto tcp src_ip 192.0.2.1 dst_ip 192.0.2.2 \
			action ct commit nat src addr 192.0.2.2 pipe \
			action ct clear pipe \
			action ct commit nat dst addr 192.0.2.1 pipe \
			action ct clear pipe \
			action skbedit ptype host pipe \
			action mirred ingress redirect dev $h1
	tc filter add dev $h1 protocol ip pref 101 handle 101 egress flower \
		$tcflags ip_proto icmp \
			action mirred ingress redirect dev $h1
	tc filter add dev $h1 protocol ip pref 102 handle 102 ingress flower \
		ip_proto icmp \
			action drop

	ip vrf exec v$h1 nc --recv-only -w10 -l -p 12345 -o $tmpfile1  &
	local rpid=$!
	ip vrf exec v$h1 nc -w1 --send-only 192.0.2.2 12345 <$tmpfile
	wait -n $rpid
	cmp -s $tmpfile $tmpfile1
	check_err $? "server output check failed"

	$MZ $h1 -c 10 -p 64 -a $h1mac -b $h1mac -A 192.0.2.1 -B 192.0.2.1 \
		-t icmp "ping,id=42,seq=5" -q
	tc_check_packets "dev $h1 egress" 101 10
	check_err $? "didn't mirred redirect ICMP"
	tc_check_packets "dev $h1 ingress" 102 10
	check_err $? "didn't drop mirred ICMP"
	local overlimits=$(tc_rule_stats_get ${h1} 101 egress .overlimits)
	test ${overlimits} = 10
	check_err $? "wrong overlimits, expected 10 got ${overlimits}"

	tc filter del dev $h1 egress protocol ip pref 100 handle 100 flower
	tc filter del dev $h1 egress protocol ip pref 101 handle 101 flower
	tc filter del dev $h1 ingress protocol ip pref 102 handle 102 flower

	rm -f $tmpfile $tmpfile1
	log_test "mirred_egress_to_ingress_tcp ($tcflags)"
}

setup_prepare()
{
	h1=${NETIFS[p1]}