xref: /linux/tools/testing/selftests/net/fou_mcast_encap.sh (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1*e5382133SAnton Danilov#!/bin/bash
2*e5382133SAnton Danilov# SPDX-License-Identifier: GPL-2.0
3*e5382133SAnton Danilov#
4*e5382133SAnton Danilov# Test that UDP encapsulation (FOU) correctly handles packet resubmit
5*e5382133SAnton Danilov# when packets are delivered via the multicast UDP delivery path.
6*e5382133SAnton Danilov#
7*e5382133SAnton Danilov# When a FOU-encapsulated packet arrives with a multicast destination IP,
8*e5382133SAnton Danilov# __udp4_lib_mcast_deliver() / __udp6_lib_mcast_deliver() must resubmit
9*e5382133SAnton Danilov# it to the inner protocol handler (e.g., GRE) rather than consuming it.
10*e5382133SAnton Danilov# This test verifies both IPv4 and IPv6 paths by creating a FOU/GRETAP
11*e5382133SAnton Danilov# tunnel with a multicast remote address and sending ping through it.
12*e5382133SAnton Danilov#
13*e5382133SAnton Danilov# The early demux optimization can mask this issue by routing packets via
14*e5382133SAnton Danilov# the unicast path (udp[6]_unicast_rcv_skb), so we disable it to force
15*e5382133SAnton Danilov# packets through the multicast delivery function.
16*e5382133SAnton Danilov
17*e5382133SAnton Danilovsource lib.sh
18*e5382133SAnton Danilov
19*e5382133SAnton DanilovNSENDER=""
20*e5382133SAnton DanilovNRECV=""
21*e5382133SAnton Danilov
22*e5382133SAnton DanilovFOU_PORT4=4797
23*e5382133SAnton DanilovFOU_PORT6=4798
24*e5382133SAnton DanilovMCAST4=239.0.0.1
25*e5382133SAnton DanilovMCAST6=ff0e::1
26*e5382133SAnton Danilov
27*e5382133SAnton DanilovTUN4_S=192.168.99.1
28*e5382133SAnton DanilovTUN4_R=192.168.99.2
29*e5382133SAnton DanilovTUN6_S=2001:db8:99::1
30*e5382133SAnton DanilovTUN6_R=2001:db8:99::2
31*e5382133SAnton Danilov
32*e5382133SAnton Danilovcleanup() {
33*e5382133SAnton Danilov	cleanup_all_ns
34*e5382133SAnton Danilov}
35*e5382133SAnton Danilov
36*e5382133SAnton Danilovtrap cleanup EXIT
37*e5382133SAnton Danilov
38*e5382133SAnton Danilovsetup_common() {
39*e5382133SAnton Danilov	setup_ns NSENDER NRECV
40*e5382133SAnton Danilov
41*e5382133SAnton Danilov	# Create veth pair directly inside namespaces to avoid name
42*e5382133SAnton Danilov	# collisions with devices in the root namespace.
43*e5382133SAnton Danilov	ip link add veth_s netns "$NSENDER" type veth \
44*e5382133SAnton Danilov		peer name veth_r netns "$NRECV"
45*e5382133SAnton Danilov
46*e5382133SAnton Danilov	ip -n "$NSENDER" link set veth_s up
47*e5382133SAnton Danilov	ip -n "$NRECV" link set veth_r up
48*e5382133SAnton Danilov
49*e5382133SAnton Danilov	# Same sysctl controls early demux for both IPv4 and IPv6.
50*e5382133SAnton Danilov	ip netns exec "$NRECV" sysctl -wq net.ipv4.ip_early_demux=0
51*e5382133SAnton Danilov}
52*e5382133SAnton Danilov
53*e5382133SAnton Danilovsetup_ipv4() {
54*e5382133SAnton Danilov	# IPv4 FOU (CONFIG_NET_FOU) is built in on kernels configured for
55*e5382133SAnton Danilov	# these tests, so no module load is needed here.
56*e5382133SAnton Danilov	ip -n "$NSENDER" addr add 10.0.0.1/24 dev veth_s
57*e5382133SAnton Danilov	ip -n "$NRECV" addr add 10.0.0.2/24 dev veth_r
58*e5382133SAnton Danilov
59*e5382133SAnton Danilov	# Join multicast group on receiver
60*e5382133SAnton Danilov	ip -n "$NRECV" addr add "$MCAST4/32" dev veth_r autojoin
61*e5382133SAnton Danilov
62*e5382133SAnton Danilov	ip -n "$NSENDER" route add 239.0.0.0/8 dev veth_s
63*e5382133SAnton Danilov	ip -n "$NRECV" route add 239.0.0.0/8 dev veth_r
64*e5382133SAnton Danilov
65*e5382133SAnton Danilov	# Sender: GRETAP with FOU encap (no FOU listener needed on TX side)
66*e5382133SAnton Danilov	ip -n "$NSENDER" link add eoudp4 type gretap \
67*e5382133SAnton Danilov		remote "$MCAST4" local 10.0.0.1 \
68*e5382133SAnton Danilov		encap fou encap-sport "$FOU_PORT4" encap-dport "$FOU_PORT4" \
69*e5382133SAnton Danilov		key "$MCAST4"
70*e5382133SAnton Danilov	ip -n "$NSENDER" link set eoudp4 up
71*e5382133SAnton Danilov	ip -n "$NSENDER" addr add "$TUN4_S/24" dev eoudp4
72*e5382133SAnton Danilov
73*e5382133SAnton Danilov	# Receiver: FOU listener + GRETAP
74*e5382133SAnton Danilov	ip netns exec "$NRECV" ip fou add port "$FOU_PORT4" ipproto 47
75*e5382133SAnton Danilov	ip -n "$NRECV" link add eoudp4 type gretap \
76*e5382133SAnton Danilov		remote "$MCAST4" local 10.0.0.2 \
77*e5382133SAnton Danilov		encap fou encap-sport "$FOU_PORT4" encap-dport "$FOU_PORT4" \
78*e5382133SAnton Danilov		key "$MCAST4"
79*e5382133SAnton Danilov	ip -n "$NRECV" link set eoudp4 up
80*e5382133SAnton Danilov	ip -n "$NRECV" addr add "$TUN4_R/24" dev eoudp4
81*e5382133SAnton Danilov
82*e5382133SAnton Danilov	# Static neigh on sender: ARP replies cannot traverse the
83*e5382133SAnton Danilov	# unidirectional multicast tunnel.
84*e5382133SAnton Danilov	local recv_mac
85*e5382133SAnton Danilov	recv_mac=$(ip -n "$NRECV" link show eoudp4 | awk '/ether/{print $2}')
86*e5382133SAnton Danilov	ip -n "$NSENDER" neigh add "$TUN4_R" lladdr "$recv_mac" dev eoudp4
87*e5382133SAnton Danilov}
88*e5382133SAnton Danilov
89*e5382133SAnton Danilovsetup_ipv6() {
90*e5382133SAnton Danilov	# Skip cleanly if IPv6 or the fou6 module is not available.
91*e5382133SAnton Danilov	[ -e /proc/sys/net/ipv6 ] || return "$ksft_skip"
92*e5382133SAnton Danilov	modprobe -q fou6 || return "$ksft_skip"
93*e5382133SAnton Danilov
94*e5382133SAnton Danilov	ip -n "$NSENDER" addr add 2001:db8::1/64 dev veth_s nodad
95*e5382133SAnton Danilov	ip -n "$NRECV" addr add 2001:db8::2/64 dev veth_r nodad
96*e5382133SAnton Danilov
97*e5382133SAnton Danilov	# Join multicast group on receiver
98*e5382133SAnton Danilov	ip -n "$NRECV" addr add "$MCAST6/128" dev veth_r autojoin
99*e5382133SAnton Danilov
100*e5382133SAnton Danilov	ip -n "$NSENDER" -6 route add ff00::/8 dev veth_s
101*e5382133SAnton Danilov	ip -n "$NRECV" -6 route add ff00::/8 dev veth_r
102*e5382133SAnton Danilov
103*e5382133SAnton Danilov	# Sender: ip6gretap with FOU encap
104*e5382133SAnton Danilov	ip -n "$NSENDER" link add eoudp6 type ip6gretap \
105*e5382133SAnton Danilov		remote "$MCAST6" local 2001:db8::1 \
106*e5382133SAnton Danilov		encap fou encap-sport "$FOU_PORT6" encap-dport "$FOU_PORT6" \
107*e5382133SAnton Danilov		key 42
108*e5382133SAnton Danilov	ip -n "$NSENDER" link set eoudp6 up
109*e5382133SAnton Danilov	ip -n "$NSENDER" addr add "$TUN6_S/64" dev eoudp6 nodad
110*e5382133SAnton Danilov
111*e5382133SAnton Danilov	# Receiver: FOU listener (IPv6) + ip6gretap
112*e5382133SAnton Danilov	ip netns exec "$NRECV" ip fou add port "$FOU_PORT6" ipproto 47 -6
113*e5382133SAnton Danilov	ip -n "$NRECV" link add eoudp6 type ip6gretap \
114*e5382133SAnton Danilov		remote "$MCAST6" local 2001:db8::2 \
115*e5382133SAnton Danilov		encap fou encap-sport "$FOU_PORT6" encap-dport "$FOU_PORT6" \
116*e5382133SAnton Danilov		key 42
117*e5382133SAnton Danilov	ip -n "$NRECV" link set eoudp6 up
118*e5382133SAnton Danilov	ip -n "$NRECV" addr add "$TUN6_R/64" dev eoudp6 nodad
119*e5382133SAnton Danilov
120*e5382133SAnton Danilov	# Static neigh on sender: neighbor discovery cannot traverse the
121*e5382133SAnton Danilov	# unidirectional multicast tunnel.
122*e5382133SAnton Danilov	local recv_mac
123*e5382133SAnton Danilov	recv_mac=$(ip -n "$NRECV" link show eoudp6 | awk '/ether/{print $2}')
124*e5382133SAnton Danilov	ip -n "$NSENDER" neigh add "$TUN6_R" lladdr "$recv_mac" dev eoudp6
125*e5382133SAnton Danilov}
126*e5382133SAnton Danilov
127*e5382133SAnton Danilovget_rx_packets() {
128*e5382133SAnton Danilov	local dev="$1"
129*e5382133SAnton Danilov
130*e5382133SAnton Danilov	ip -n "$NRECV" -s link show "$dev" | awk '/RX:/{getline; print $2}'
131*e5382133SAnton Danilov}
132*e5382133SAnton Danilov
133*e5382133SAnton Danilovrun_ping_test() {
134*e5382133SAnton Danilov	local family="$1"
135*e5382133SAnton Danilov	local dev="$2"
136*e5382133SAnton Danilov	local dst="$3"
137*e5382133SAnton Danilov	local name="$4"
138*e5382133SAnton Danilov	local count=100
139*e5382133SAnton Danilov	local rx_before rx_after rx_delta
140*e5382133SAnton Danilov
141*e5382133SAnton Danilov	# Warmup: let any initial broadcast/ND traffic settle
142*e5382133SAnton Danilov	ip netns exec "$NSENDER" ping "$family" -c 1 -W 1 "$dst" \
143*e5382133SAnton Danilov		>/dev/null 2>&1
144*e5382133SAnton Danilov	sleep 1
145*e5382133SAnton Danilov
146*e5382133SAnton Danilov	rx_before=$(get_rx_packets "$dev")
147*e5382133SAnton Danilov	ip netns exec "$NSENDER" ping "$family" -i 0.01 -c $count -W 1 "$dst" \
148*e5382133SAnton Danilov		>/dev/null 2>&1
149*e5382133SAnton Danilov	sleep 1
150*e5382133SAnton Danilov	rx_after=$(get_rx_packets "$dev")
151*e5382133SAnton Danilov
152*e5382133SAnton Danilov	rx_delta=$((rx_after - rx_before))
153*e5382133SAnton Danilov
154*e5382133SAnton Danilov	if [ "$rx_delta" -ge "$count" ]; then
155*e5382133SAnton Danilov		RET=$ksft_pass
156*e5382133SAnton Danilov	else
157*e5382133SAnton Danilov		RET=$ksft_fail
158*e5382133SAnton Danilov	fi
159*e5382133SAnton Danilov	log_test "$name (received $rx_delta/$count)"
160*e5382133SAnton Danilov}
161*e5382133SAnton Danilov
162*e5382133SAnton Danilovsetup_common
163*e5382133SAnton Danilovsetup_ipv4
164*e5382133SAnton Danilovrun_ping_test -4 eoudp4 "$TUN4_R" "FOU/GRETAP IPv4 multicast encap resubmit"
165*e5382133SAnton Danilov
166*e5382133SAnton Danilovif setup_ipv6; then
167*e5382133SAnton Danilov	run_ping_test -6 eoudp6 "$TUN6_R" "FOU/ip6gretap IPv6 multicast encap resubmit"
168*e5382133SAnton Danilovelse
169*e5382133SAnton Danilov	log_test_skip "FOU/ip6gretap IPv6 multicast encap resubmit"
170*e5382133SAnton Danilovfi
171*e5382133SAnton Danilov
172*e5382133SAnton Danilovexit "$EXIT_STATUS"
173