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