Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522#!/bin/bash# SPDX-License-Identifier: GPL-2.0## Test that the state of the route exception cache after an ICMP error is# processed does not depend on whether the quoted packet was matched to a# socket. Otherwise, an off-path attacker can probe the cache to discover the# ephemeral port used by a connected UDP socket.## When the quoted packet is not matched to a socket, the same exception is# created as when it is matched, so that neither its presence nor its contents# reveal the result of socket matching.## +----+# +---------| r1 |# | +----+# +----+ +--------+ | .1# | h1 |---| bridge | | 198.51.100.0/30# +----+ +--------+ | 2001:db8:2::/64# .1 | | .2# | +----+ +----+# +---------| r2 |-----------| h2 |# .2 .3 +----+ .1 .2 +----+# 203.0.113.0/24# 2001:db8:3::/64# 192.0.2.0/24# 2001:db8:1::/64## Traffic from h1 to h2 is routed via r1, which reaches h2's network via r2# over the point-to-point link. The MTU of the r2 - h2 link is lowered so that# r2 emits ICMP errors towards h1.## For the redirect tests r1's route to h2's network is replaced with one via r2# on the shared segment, so that r1 forwards the packet back to the segment it# arrived from and emits a redirect towards h1.## The packets that provoke the ICMP errors are injected with a packet socket so# that no socket is ever associated with them. A socket is created separately,# with socat, when a test needs the ICMP error to be matched.
# shellcheck disable=SC1091,SC2034,SC2154,SC2329source lib.sh
require_command jqrequire_command mausezahnrequire_command nstatrequire_command socat
ALL_TESTS=" pmtu_no_socket_ipv4 pmtu_no_socket_ipv6 pmtu_socket_ipv4 pmtu_socket_ipv6 pmtu_omit_ipv4 pmtu_omit_ipv6 redirect_no_socket_ipv4 redirect_no_socket_ipv6 redirect_socket_ipv4 redirect_socket_ipv6"
# Shared segment.H1_ADDR4=192.0.2.1R1_ADDR4=192.0.2.2R2_ADDR4=192.0.2.3H1_ADDR6=2001:db8:1::1R1_ADDR6=2001:db8:1::2R2_ADDR6=2001:db8:1::3
# r1 - r2 link.R2_R1_ADDR4=198.51.100.2R2_R1_ADDR6=2001:db8:2::2
# r2 - h2 link.H2_ADDR4=203.0.113.2H2_NET4=203.0.113.0/24H2_ADDR6=2001:db8:3::2H2_NET6=2001:db8:3::/64
SPORT=12345DPORT=54321
# The MTU of the shared segment and of the r1 - r2 link. Large enough for the# injected packets to reach r2 intact.SEGMENT_MTU=2000# Size of the injected packets. The PMTU tests need a size that exceeds every# MTU used for the r2 - h2 link, so that r2 responds with an ICMP error. The# redirect tests need a size that does not, otherwise r2 would respond with an# ICMP error in addition to the redirect emitted by r1.PMTU_PACKET_SIZE=1800REDIRECT_PACKET_SIZE=100
# The MTUs used for the r2 - h2 link. All of them must be at least# IPV6_MIN_MTU, otherwise IPv6 silently ignores the error instead of creating# an exception.MTU_MID=1400MTU_LOW=1300
# Values for the IP{,V6}_MTU_DISCOVER socket option.PMTUDISC_DONT=0PMTUDISC_OMIT=5
SOCAT_PID=
linklocal_get(){ local ns=$1; shift local dev=$1; shift
ip -n "$ns" -j -6 addr show dev "$dev" | \ jq -r '.[]["addr_info"][] | select(.scope == "link") | .local'}
linklocal_exists(){ local ns=$1; shift local dev=$1; shift
[ -n "$(linklocal_get "$ns" "$dev")" ]}
family_vars_set(){ local family=$1; shift
FAMILY=$family
if [ "$family" -eq 4 ]; then H1_ADDR=$H1_ADDR4 H2_ADDR=$H2_ADDR4 MZ_FAMILY_OPT=() # Without the Don't Fragment bit set r2 fragments the packet # instead of reporting the MTU of the next hop. MZ_IP_OPTS="df," SOCAT_DST="UDP4-CONNECT:$H2_ADDR4:$DPORT" SOCAT_BIND="bind=$H1_ADDR4:$SPORT" SOCAT_PMTUDISC="ip-mtu-discover" else H1_ADDR=$H1_ADDR6 H2_ADDR=$H2_ADDR6 MZ_FAMILY_OPT=(-6) MZ_IP_OPTS= SOCAT_DST="UDP6-CONNECT:[$H2_ADDR6]:$DPORT" SOCAT_BIND="bind=[$H1_ADDR6]:$SPORT" SOCAT_PMTUDISC="ipv6-mtu-discover" fi}
topology_setup(){ local ns
setup_ns h1 r1 r2 h2 sw defer cleanup_all_ns
# Link-local addresses are generated from the MAC address and read # back during setup, so request that generation mode explicitly and # make the addresses available as soon as the devices are brought up. for ns in "$h1" "$r1" "$r2" "$h2" "$sw"; do ip netns exec "$ns" sysctl -qw \ net.ipv6.conf.default.addr_gen_mode=0 \ net.ipv6.conf.default.accept_dad=0 \ net.ipv6.conf.all.accept_dad=0 done
ip -n "$sw" link add name br0 type bridge ip -n "$sw" link set dev br0 mtu "$SEGMENT_MTU" up
ip -n "$h1" link add name eth0 mtu "$SEGMENT_MTU" type veth \ peer name swp1 mtu "$SEGMENT_MTU" netns "$sw" ip -n "$r1" link add name eth0 mtu "$SEGMENT_MTU" type veth \ peer name swp2 mtu "$SEGMENT_MTU" netns "$sw" ip -n "$r2" link add name eth0 mtu "$SEGMENT_MTU" type veth \ peer name swp3 mtu "$SEGMENT_MTU" netns "$sw" ip -n "$r1" link add name eth1 mtu "$SEGMENT_MTU" type veth \ peer name eth1 mtu "$SEGMENT_MTU" netns "$r2" ip -n "$r2" link add name eth2 type veth peer name eth0 netns "$h2"
ip -n "$sw" link set dev swp1 master br0 up ip -n "$sw" link set dev swp2 master br0 up ip -n "$sw" link set dev swp3 master br0 up
ip -n "$h1" link set dev eth0 up ip -n "$r1" link set dev eth0 up ip -n "$r1" link set dev eth1 up ip -n "$r2" link set dev eth0 up ip -n "$r2" link set dev eth1 up ip -n "$r2" link set dev eth2 up ip -n "$h2" link set dev eth0 up
ip -n "$h1" address add "$H1_ADDR4/24" dev eth0 ip -n "$r1" address add "$R1_ADDR4/24" dev eth0 ip -n "$r2" address add "$R2_ADDR4/24" dev eth0 ip -n "$r1" address add 198.51.100.1/30 dev eth1 ip -n "$r2" address add "$R2_R1_ADDR4/30" dev eth1 ip -n "$r2" address add 203.0.113.1/24 dev eth2 ip -n "$h2" address add "$H2_ADDR4/24" dev eth0
ip -n "$h1" -6 address add "$H1_ADDR6/64" dev eth0 nodad ip -n "$r1" -6 address add "$R1_ADDR6/64" dev eth0 nodad ip -n "$r2" -6 address add "$R2_ADDR6/64" dev eth0 nodad ip -n "$r1" -6 address add 2001:db8:2::1/64 dev eth1 nodad ip -n "$r2" -6 address add "$R2_R1_ADDR6/64" dev eth1 nodad ip -n "$r2" -6 address add 2001:db8:3::1/64 dev eth2 nodad ip -n "$h2" -6 address add "$H2_ADDR6/64" dev eth0 nodad
ip netns exec "$r1" sysctl -qw net.ipv4.ip_forward=1 ip netns exec "$r1" sysctl -qw net.ipv4.conf.all.send_redirects=1 ip netns exec "$r1" sysctl -qw net.ipv6.conf.all.forwarding=1 ip netns exec "$r2" sysctl -qw net.ipv4.ip_forward=1 ip netns exec "$r2" sysctl -qw net.ipv6.conf.all.forwarding=1
ip netns exec "$h1" sysctl -qw net.ipv4.conf.all.accept_redirects=1 ip netns exec "$h1" sysctl -qw net.ipv4.conf.eth0.accept_redirects=1 ip netns exec "$h1" sysctl -qw net.ipv6.conf.all.accept_redirects=1 ip netns exec "$h1" sysctl -qw net.ipv6.conf.eth0.accept_redirects=1
slowwait 5 linklocal_exists "$r1" eth0 check_err $? "r1: link-local address was not generated" slowwait 5 linklocal_exists "$r2" eth0 check_err $? "r2: link-local address was not generated"
R1_LLADDR=$(linklocal_get "$r1" eth0) R2_LLADDR=$(linklocal_get "$r2" eth0) R1_MAC=$(ip -n "$r1" -j link show dev eth0 | jq -r '.[]["address"]') R2_MAC=$(ip -n "$r2" -j link show dev eth0 | jq -r '.[]["address"]')
ip -n "$h1" route add "$H2_NET4" via "$R1_ADDR4" dev eth0 ip -n "$h1" -6 route add "$H2_NET6" via "$R1_LLADDR" dev eth0 ip -n "$r1" route add "$H2_NET4" via "$R2_R1_ADDR4" dev eth1 ip -n "$r1" -6 route add "$H2_NET6" via "$R2_R1_ADDR6" dev eth1 ip -n "$h2" route add default via 203.0.113.1 dev eth0 ip -n "$h2" -6 route add default via 2001:db8:3::1 dev eth0
far_mtu_set "$MTU_MID"}
# Make r1 forward towards h2's network over the segment it receives the packet# from, so that it emits a redirect towards h1.redirect_route_set(){ ip -n "$r1" route replace "$H2_NET4" via "$R2_ADDR4" dev eth0 ip -n "$r1" -6 route replace "$H2_NET6" via "$R2_LLADDR" dev eth0
# __ip_do_redirect() only creates an exception if the new gateway is # already a valid neighbour. Otherwise it merely triggers address # resolution. IPv6 resolves the target itself, in rt6_do_redirect(). ip -n "$h1" neigh replace "$R2_ADDR4" lladdr "$R2_MAC" dev eth0 \ nud permanent}
far_mtu_set(){ local mtu=$1; shift
ip -n "$r2" link set dev eth2 mtu "$mtu" ip -n "$h2" link set dev eth0 mtu "$mtu"}
socket_is_open(){ ip netns exec "$h1" ss -uHn "sport = :$SPORT" | grep -q .}
socket_start(){ # Disable PMTU discovery by default so that ICMP errors are not # reported to the socket. Otherwise socat would exit when the first one # arrives and later packets in the same test would not be matched to a # socket. The exception is still created, as ip{,6}_sk_accept_pmtu() # only rejects IP{,V6}_PMTUDISC_{INTERFACE,OMIT}. local pmtudisc=${1:-$PMTUDISC_DONT}
# Send socat's diagnostics to /dev/null. It reports the ICMP errors # that reach the socket, which is exactly what the tests provoke. ip netns exec "$h1" socat -u -lf/dev/null \ "$SOCAT_DST,$SOCAT_BIND,$SOCAT_PMTUDISC=$pmtudisc" \ OPEN:/dev/null,wronly=1 & SOCAT_PID=$! defer socket_stop
slowwait 5 socket_is_open check_err $? "socket did not open"}
socket_stop(){ [ -z "$SOCAT_PID" ] && return 0
kill "$SOCAT_PID" &> /dev/null wait "$SOCAT_PID" 2> /dev/null SOCAT_PID=}
# Inject a packet towards h2 with a packet socket. No socket is associated with# it, so an ICMP error quoting it is matched to a socket only if one was# created separately with the same source port.packet_send(){ local size=$1; shift
ip netns exec "$h1" mausezahn "${MZ_FAMILY_OPT[@]}" eth0 \ -a own -b "$R1_MAC" -A "$H1_ADDR" -B "$H2_ADDR" \ -t udp "${MZ_IP_OPTS}sp=$SPORT,dp=$DPORT" \ -p "$size" -c 1 -q}
exception_show(){ if [ "$FAMILY" -eq 4 ]; then # IPv4 exceptions without a bound route are not dumped, but # "route get" reports the exception and binds a route to it. ip -n "$h1" route get "$H2_ADDR" else # IPv6 does not report a cache indication in "route get" # output, so dump the exceptions instead. ip -n "$h1" -6 route show cache | grep -F "$H2_ADDR" || true fi}
exception_mtu_get(){ exception_show | grep -o "mtu [0-9]*" | cut -d ' ' -f 2}
exception_gw_get(){ exception_show | grep -o "via [0-9a-f.:]*" | cut -d ' ' -f 2}
exception_mtu_check(){ local expected=$1; shift
[ "$(exception_mtu_get)" = "$expected" ]}
icmp_errors_get(){ local ctr=IcmpInDestUnreachs
[ "$FAMILY" -eq 6 ] && ctr=Icmp6InPktTooBigs
ip netns exec "$h1" nstat -asz "$ctr" | \ awk -v ctr="$ctr" '$1 == ctr { print $2 }'}
exception_pmtu_check(){ local mtu=$1; shift local desc=$1; shift
busywait "$BUSYWAIT_TIMEOUT" exception_mtu_check "$mtu" check_err $? "$desc: exception does not carry an MTU of $mtu"}
pmtu_no_socket(){ local family=$1; shift
RET=0 family_vars_set "$family" topology_setup
packet_send "$PMTU_PACKET_SIZE" exception_pmtu_check "$MTU_MID" "No socket"
log_test "IPv$family: PMTU: exception without a matching socket"}
pmtu_no_socket_ipv4(){ pmtu_no_socket 4}
pmtu_no_socket_ipv6(){ pmtu_no_socket 6}
pmtu_socket(){ local family=$1; shift local t0
RET=0 family_vars_set "$family" topology_setup socket_start
packet_send "$PMTU_PACKET_SIZE" exception_pmtu_check "$MTU_MID" "Matching socket"
# A lower PMTU replaces the one currently stored in the exception. far_mtu_set "$MTU_LOW" packet_send "$PMTU_PACKET_SIZE" exception_pmtu_check "$MTU_LOW" "Lower PMTU"
# A higher PMTU is ignored, so the exception is left as it is. Wait # for the error to be received, as otherwise the check below would # pass even if it never was. far_mtu_set "$MTU_MID" t0=$(icmp_errors_get) packet_send "$PMTU_PACKET_SIZE" busywait "$BUSYWAIT_TIMEOUT" until_counter_is ">= $((t0 + 1))" \ icmp_errors_get > /dev/null check_err $? "Higher PMTU: ICMP error was not received"
exception_mtu_check "$MTU_LOW" check_err $? "Higher PMTU: exception does not carry an MTU of $MTU_LOW"
log_test "IPv$family: PMTU: exception with a matching socket"}
pmtu_socket_ipv4(){ pmtu_socket 4}
pmtu_socket_ipv6(){ pmtu_socket 6}
pmtu_omit(){ local family=$1; shift
RET=0 family_vars_set "$family" topology_setup socket_start "$PMTUDISC_OMIT"
packet_send "$PMTU_PACKET_SIZE" exception_pmtu_check "$MTU_MID" "PMTU discovery disabled"
log_test "IPv$family: PMTU: exception with a socket ignoring it"}
pmtu_omit_ipv4(){ pmtu_omit 4}
pmtu_omit_ipv6(){ pmtu_omit 6}
exception_gw_check(){ local expected=$1; shift
[ -n "$expected" ] && [ "$(exception_gw_get)" = "$expected" ]}
redirect_gw_new(){ if [ "$FAMILY" -eq 4 ]; then echo "$R2_ADDR4" else echo "$R2_LLADDR" fi}
redirect_no_socket(){ local family=$1; shift
RET=0 family_vars_set "$family" topology_setup redirect_route_set
packet_send "$REDIRECT_PACKET_SIZE" busywait "$BUSYWAIT_TIMEOUT" exception_gw_check "$(redirect_gw_new)" check_err $? "No socket: exception does not carry the new gateway"
log_test "IPv$family: Redirect: exception without a matching socket"}
redirect_no_socket_ipv4(){ redirect_no_socket 4}
redirect_no_socket_ipv6(){ redirect_no_socket 6}
redirect_socket(){ local family=$1; shift
RET=0 family_vars_set "$family" topology_setup redirect_route_set socket_start
packet_send "$REDIRECT_PACKET_SIZE" busywait "$BUSYWAIT_TIMEOUT" exception_gw_check "$(redirect_gw_new)" check_err $? "Matching socket: exception does not carry the new gateway"
log_test "IPv$family: Redirect: exception with a matching socket"}
redirect_socket_ipv4(){ redirect_socket 4}
redirect_socket_ipv6(){ redirect_socket 6}
trap defer_scopes_cleanup EXITtests_run
exit "$EXIT_STATUS"