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