#!/bin/bash # SPDX-License-Identifier: GPL-2.0 # # Check that route PMTU values match expectations, and that initial device MTU # values are assigned correctly # # Tests currently implemented: # # - pmtu_ipv4 # Set up two namespaces, A and B, with two paths between them over routers # R1 and R2 (also implemented with namespaces), with different MTUs: # # segment a_r1 segment b_r1 a_r1: 2000 # .--------------R1--------------. b_r1: 1400 # A B a_r2: 2000 # '--------------R2--------------' b_r2: 1500 # segment a_r2 segment b_r2 # # Check that PMTU exceptions with the correct PMTU are created. Then # decrease and increase the MTU of the local link for one of the paths, # A to R1, checking that route exception PMTU changes accordingly over # this path. Also check that locked exceptions are created when an ICMP # message advertising a PMTU smaller than net.ipv4.route.min_pmtu is # received # # - pmtu_ipv6 # Same as pmtu_ipv4, except for locked PMTU tests, using IPv6 # # - pmtu_ipv4_dscp_icmp_exception # Set up the same network topology as pmtu_ipv4, but use non-default # routing table in A. A fib-rule is used to jump to this routing table # based on DSCP. Send ICMPv4 packets with the expected DSCP value and # verify that ECN doesn't interfere with the creation of PMTU exceptions. # # - pmtu_ipv4_dscp_udp_exception # Same as pmtu_ipv4_dscp_icmp_exception, but use UDP instead of ICMP. # # - pmtu_ipv4_vxlan4_exception # Set up the same network topology as pmtu_ipv4, create a VXLAN tunnel # over IPv4 between A and B, routed via R1. On the link between R1 and B, # set a MTU lower than the VXLAN MTU and the MTU on the link between A and # R1. Send IPv4 packets, exceeding the MTU between R1 and B, over VXLAN # from A to B and check that the PMTU exception is created with the right # value on A # # - pmtu_ipv6_vxlan4_exception # Same as pmtu_ipv4_vxlan4_exception, but send IPv6 packets from A to B # # - pmtu_ipv4_vxlan6_exception # Same as pmtu_ipv4_vxlan4_exception, but use IPv6 transport from A to B # # - pmtu_ipv6_vxlan6_exception # Same as pmtu_ipv4_vxlan6_exception, but send IPv6 packets from A to B # # - pmtu_ipv4_geneve4_exception # Same as pmtu_ipv4_vxlan4_exception, but using a GENEVE tunnel instead of # VXLAN # # - pmtu_ipv6_geneve4_exception # Same as pmtu_ipv6_vxlan4_exception, but using a GENEVE tunnel instead of # VXLAN # # - pmtu_ipv4_geneve6_exception # Same as pmtu_ipv4_vxlan6_exception, but using a GENEVE tunnel instead of # VXLAN # # - pmtu_ipv6_geneve6_exception # Same as pmtu_ipv6_vxlan6_exception, but using a GENEVE tunnel instead of # VXLAN # # - pmtu_ipv{4,6}_br_vxlan{4,6}_exception # Set up three namespaces, A, B, and C, with routing between A and B over # R1. R2 is unused in these tests. A has a veth connection to C, and is # connected to B via a VXLAN endpoint, which is directly bridged to C. # MTU on the B-R1 link is lower than other MTUs. # # Check that both C and A are able to communicate with B over the VXLAN # tunnel, and that PMTU exceptions with the correct values are created. # # segment a_r1 segment b_r1 b_r1: 4000 # .--------------R1--------------. everything # C---veth A B else: 5000 # ' bridge | # '---- - - - - - VXLAN - - - - - - - ' # # - pmtu_ipv{4,6}_br_geneve{4,6}_exception # Same as pmtu_ipv{4,6}_br_vxlan{4,6}_exception, with a GENEVE tunnel # instead. # # - pmtu_ipv{4,6}_ovs_vxlan{4,6}_exception # Set up two namespaces, B, and C, with routing between the init namespace # and B over R1. A and R2 are unused in these tests. The init namespace # has a veth connection to C, and is connected to B via a VXLAN endpoint, # which is handled by Open vSwitch and bridged to C. MTU on the B-R1 link # is lower than other MTUs. # # Check that C is able to communicate with B over the VXLAN tunnel, and # that PMTU exceptions with the correct values are created. # # segment a_r1 segment b_r1 b_r1: 4000 # .--------------R1--------------. everything # C---veth init B else: 5000 # '- ovs | # '---- - - - - - VXLAN - - - - - - - ' # # - pmtu_ipv{4,6}_ovs_geneve{4,6}_exception # Same as pmtu_ipv{4,6}_ovs_vxlan{4,6}_exception, with a GENEVE tunnel # instead. # # - pmtu_ipv{4,6}_fou{4,6}_exception # Same as pmtu_ipv4_vxlan4, but using a direct IPv4/IPv6 encapsulation # (FoU) over IPv4/IPv6, instead of VXLAN # # - pmtu_ipv{4,6}_fou{4,6}_exception # Same as pmtu_ipv4_vxlan4, but using a generic UDP IPv4/IPv6 # encapsulation (GUE) over IPv4/IPv6, instead of VXLAN # # - pmtu_ipv{4,6}_ipv{4,6}_exception # Same as pmtu_ipv4_vxlan4, but using a IPv4/IPv6 tunnel over IPv4/IPv6, # instead of VXLAN # # - pmtu_vti4_exception # Set up vti tunnel on top of veth, with xfrm states and policies, in two # namespaces with matching endpoints. Check that route exception is not # created if link layer MTU is not exceeded, then exceed it and check that # exception is created with the expected PMTU. The approach described # below for IPv6 doesn't apply here, because, on IPv4, administrative MTU # changes alone won't affect PMTU # # - pmtu_vti4_udp_exception # Same as pmtu_vti4_exception, but using ESP-in-UDP # # - pmtu_vti4_udp_routed_exception # Set up vti tunnel on top of veth connected through routing namespace and # add xfrm states and policies with ESP-in-UDP encapsulation. Check that # route exception is not created if link layer MTU is not exceeded, then # lower MTU on second part of routed environment and check that exception # is created with the expected PMTU. # # - pmtu_vti6_exception # Set up vti6 tunnel on top of veth, with xfrm states and policies, in two # namespaces with matching endpoints. Check that route exception is # created by exceeding link layer MTU with ping to other endpoint. Then # decrease and increase MTU of tunnel, checking that route exception PMTU # changes accordingly # # - pmtu_vti6_udp_exception # Same as pmtu_vti6_exception, but using ESP-in-UDP # # - pmtu_vti6_udp_routed_exception # Same as pmtu_vti6_udp_routed_exception but with routing between vti # endpoints # # - pmtu_vti4_default_mtu # Set up vti4 tunnel on top of veth, in two namespaces with matching # endpoints. Check that MTU assigned to vti interface is the MTU of the # lower layer (veth) minus additional lower layer headers (zero, for veth) # minus IPv4 header length # # - pmtu_vti6_default_mtu # Same as above, for IPv6 # # - pmtu_vti4_link_add_mtu # Set up vti4 interface passing MTU value at link creation, check MTU is # configured, and that link is not created with invalid MTU values # # - pmtu_vti6_link_add_mtu # Same as above, for IPv6 # # - pmtu_vti6_link_change_mtu # Set up two dummy interfaces with different MTUs, create a vti6 tunnel # and check that configured MTU is used on link creation and changes, and # that MTU is properly calculated instead when MTU is not configured from # userspace # # - cleanup_ipv4_exception # Similar to pmtu_ipv4_vxlan4_exception, but explicitly generate PMTU # exceptions on multiple CPUs and check that the veth device tear-down # happens in a timely manner # # - cleanup_ipv6_exception # Same as above, but use IPv6 transport from A to B # # - list_flush_ipv4_exception # Using the same topology as in pmtu_ipv4, create exceptions, and check # they are shown when listing exception caches, gone after flushing them # # - list_flush_ipv6_exception # Using the same topology as in pmtu_ipv6, create exceptions, and check # they are shown when listing exception caches, gone after flushing them # # - pmtu_ipv4_route_change # Use the same topology as in pmtu_ipv4, but issue a route replacement # command and delete the corresponding device afterward. This tests for # proper cleanup of the PMTU exceptions by the route replacement path. # Device unregistration should complete successfully # # - pmtu_ipv6_route_change # Same as above but with IPv6 # # - pmtu_ipv4_mp_exceptions # Use the same topology as in pmtu_ipv4, but add routeable addresses # on host A and B on lo reachable via both routers. Host A and B # addresses have multipath routes to each other, b_r1 mtu = 1500. # Check that PMTU exceptions are created for both paths.
source lib.sh
PAUSE_ON_FAIL=no
VERBOSE=0
TRACING=0
# Some systems don't have a ping6 binary anymore
which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
# Name Description re-run with nh
tests="
pmtu_ipv4_exception ipv4: PMTU exceptions 1
pmtu_ipv6_exception ipv6: PMTU exceptions 1
pmtu_ipv4_dscp_icmp_exception ICMPv4 with DSCP and ECN: PMTU exceptions 1
pmtu_ipv4_dscp_udp_exception UDPv4 with DSCP and ECN: PMTU exceptions 1
pmtu_ipv4_vxlan4_exception IPv4 over vxlan4: PMTU exceptions 1
pmtu_ipv6_vxlan4_exception IPv6 over vxlan4: PMTU exceptions 1
pmtu_ipv4_vxlan6_exception IPv4 over vxlan6: PMTU exceptions 1
pmtu_ipv6_vxlan6_exception IPv6 over vxlan6: PMTU exceptions 1
pmtu_ipv4_geneve4_exception IPv4 over geneve4: PMTU exceptions 1
pmtu_ipv6_geneve4_exception IPv6 over geneve4: PMTU exceptions 1
pmtu_ipv4_geneve6_exception IPv4 over geneve6: PMTU exceptions 1
pmtu_ipv6_geneve6_exception IPv6 over geneve6: PMTU exceptions 1
pmtu_ipv4_br_vxlan4_exception IPv4, bridged vxlan4: PMTU exceptions 1
pmtu_ipv6_br_vxlan4_exception IPv6, bridged vxlan4: PMTU exceptions 1
pmtu_ipv4_br_vxlan6_exception IPv4, bridged vxlan6: PMTU exceptions 1
pmtu_ipv6_br_vxlan6_exception IPv6, bridged vxlan6: PMTU exceptions 1
pmtu_ipv4_br_geneve4_exception IPv4, bridged geneve4: PMTU exceptions 1
pmtu_ipv6_br_geneve4_exception IPv6, bridged geneve4: PMTU exceptions 1
pmtu_ipv4_br_geneve6_exception IPv4, bridged geneve6: PMTU exceptions 1
pmtu_ipv6_br_geneve6_exception IPv6, bridged geneve6: PMTU exceptions 1
pmtu_ipv4_ovs_vxlan4_exception IPv4, OVS vxlan4: PMTU exceptions 1
pmtu_ipv6_ovs_vxlan4_exception IPv6, OVS vxlan4: PMTU exceptions 1
pmtu_ipv4_ovs_vxlan6_exception IPv4, OVS vxlan6: PMTU exceptions 1
pmtu_ipv6_ovs_vxlan6_exception IPv6, OVS vxlan6: PMTU exceptions 1
pmtu_ipv4_ovs_geneve4_exception IPv4, OVS geneve4: PMTU exceptions 1
pmtu_ipv6_ovs_geneve4_exception IPv6, OVS geneve4: PMTU exceptions 1
pmtu_ipv4_ovs_geneve6_exception IPv4, OVS geneve6: PMTU exceptions 1
pmtu_ipv6_ovs_geneve6_exception IPv6, OVS geneve6: PMTU exceptions 1
pmtu_ipv4_fou4_exception IPv4 over fou4: PMTU exceptions 1
pmtu_ipv6_fou4_exception IPv6 over fou4: PMTU exceptions 1
pmtu_ipv4_fou6_exception IPv4 over fou6: PMTU exceptions 1
pmtu_ipv6_fou6_exception IPv6 over fou6: PMTU exceptions 1
pmtu_ipv4_gue4_exception IPv4 over gue4: PMTU exceptions 1
pmtu_ipv6_gue4_exception IPv6 over gue4: PMTU exceptions 1
pmtu_ipv4_gue6_exception IPv4 over gue6: PMTU exceptions 1
pmtu_ipv6_gue6_exception IPv6 over gue6: PMTU exceptions 1
pmtu_ipv4_ipv4_exception IPv4 over IPv4: PMTU exceptions 1
pmtu_ipv6_ipv4_exception IPv6 over IPv4: PMTU exceptions 1
pmtu_ipv4_ipv6_exception IPv4 over IPv6: PMTU exceptions 1
pmtu_ipv6_ipv6_exception IPv6 over IPv6: PMTU exceptions 1
pmtu_vti6_exception vti6: PMTU exceptions 0
pmtu_vti4_exception vti4: PMTU exceptions 0
pmtu_vti6_udp_exception vti6: PMTU exceptions (ESP-in-UDP) 0
pmtu_vti4_udp_exception vti4: PMTU exceptions (ESP-in-UDP) 0
pmtu_vti6_udp_routed_exception vti6: PMTU exceptions, routed (ESP-in-UDP) 0
pmtu_vti4_udp_routed_exception vti4: PMTU exceptions, routed (ESP-in-UDP) 0
pmtu_vti4_default_mtu vti4: default MTU assignment 0
pmtu_vti6_default_mtu vti6: default MTU assignment 0
pmtu_vti4_link_add_mtu vti4: MTU setting on link creation 0
pmtu_vti6_link_add_mtu vti6: MTU setting on link creation 0
pmtu_vti6_link_change_mtu vti6: MTU changes on link changes 0
cleanup_ipv4_exception ipv4: cleanup of cached exceptions 1
cleanup_ipv6_exception ipv6: cleanup of cached exceptions 1
list_flush_ipv4_exception ipv4: list and flush cached exceptions 1
list_flush_ipv6_exception ipv6: list and flush cached exceptions 1
pmtu_ipv4_route_change ipv4: PMTU exception w/route replace 1
pmtu_ipv6_route_change ipv6: PMTU exception w/route replace 1
pmtu_ipv4_mp_exceptions ipv4: PMTU multipath nh exceptions 1"
# Addressing and routing for tests with routers: four network segments, with # index SEGMENT between 1 and 4, a common prefix (PREFIX4 or PREFIX6) and an # identifier ID, which is 1 for hosts (A and B), 2 for routers (R1 and R2). # Addresses are: # - IPv4: PREFIX4.SEGMENT.ID (/24) # - IPv6: PREFIX6:SEGMENT::ID (/64)
prefix4="10.0"
prefix6="fc00"
a_r1=1
a_r2=2
b_r1=3
b_r2=4 # ns peer segment
routing_addrs="
A R1 ${a_r1}
A R2 ${a_r2}
B R1 ${b_r1}
B R2 ${b_r2} " # Traffic from A to B goes through R1 by default, and through R2, if destined to # B's address on the b_r2 segment. # Traffic from B to A goes through R1. # ns destination gateway
routes="
A default ${prefix4}.${a_r1}.2
A ${prefix4}.${b_r2}.1 ${prefix4}.${a_r2}.2
B default ${prefix4}.${b_r1}.2
A default ${prefix6}:${a_r1}::2
A ${prefix6}:${b_r2}::1 ${prefix6}:${a_r2}::2
B default ${prefix6}:${b_r1}::2 "
USE_NH="no" # ns family nh id destination gateway
nexthops="
A 4 41 ${prefix4}.${a_r1}.2 veth_A-R1
A 4 42 ${prefix4}.${a_r2}.2 veth_A-R2
B 4 41 ${prefix4}.${b_r1}.2 veth_B-R1
A 6 61 ${prefix6}:${a_r1}::2 veth_A-R1
A 6 62 ${prefix6}:${a_r2}::2 veth_A-R2
B 6 61 ${prefix6}:${b_r1}::2 veth_B-R1 "
# nexthop id correlates to id in nexthops config above # ns family prefix nh id
routes_nh="
A 4 default 41
A 4 ${prefix4}.${b_r2}.1 42
B 4 default 41
A 6 default 61
A 6 ${prefix6}:${b_r2}::1 62
B 6 default 61 "
if [ "${outer}" = "4" ]; then
modprobe fou || return $ksft_skip
a_addr="${prefix4}.${a_r1}.1"
b_addr="${prefix4}.${b_r1}.1" if [ "${inner}" = "4" ]; then
type="ipip"
ipproto="4" else
type="sit"
ipproto="41" fi else
modprobe fou6 || return $ksft_skip
a_addr="${prefix6}:${a_r1}::1"
b_addr="${prefix6}:${b_r1}::1" if [ "${inner}" = "4" ]; then
type="ip6tnl"
mode="mode ipip6"
ipproto="4 -6" else
type="ip6tnl"
mode="mode ip6ip6"
ipproto="41 -6" fi fi
run_cmd ${ns_a} ip fou add port 5555 ipproto ${ipproto} || return $ksft_skip
run_cmd ${ns_a} ip link add ${encap}_a type ${type} ${mode} local ${a_addr} remote ${b_addr} encap ${encap} encap-sport auto encap-dport 5556 || return $ksft_skip
run_cmd ${ns_b} ip fou add port 5556 ipproto ${ipproto}
run_cmd ${ns_b} ip link add ${encap}_b type ${type} ${mode} local ${b_addr} remote ${a_addr} encap ${encap} encap-sport auto encap-dport 5555
if [ "${inner}" = "4" ]; then
run_cmd ${ns_a} ip addr add ${tunnel4_a_addr}/${tunnel4_mask} dev ${encap}_a
run_cmd ${ns_b} ip addr add ${tunnel4_b_addr}/${tunnel4_mask} dev ${encap}_b else
run_cmd ${ns_a} ip addr add ${tunnel6_a_addr}/${tunnel6_mask} dev ${encap}_a
run_cmd ${ns_b} ip addr add ${tunnel6_b_addr}/${tunnel6_mask} dev ${encap}_b fi
run_cmd ${ns_a} ip link set ${encap}_a up
run_cmd ${ns_b} ip link set ${encap}_b up
}
setup_fou44() {
setup_fou_or_gue 4 4 fou
}
setup_fou46() {
setup_fou_or_gue 4 6 fou
}
setup_fou64() {
setup_fou_or_gue 6 4 fou
}
setup_fou66() {
setup_fou_or_gue 6 6 fou
}
setup_gue44() {
setup_fou_or_gue 4 4 gue
}
setup_gue46() {
setup_fou_or_gue 4 6 gue
}
setup_gue64() {
setup_fou_or_gue 6 4 gue
}
setup_gue66() {
setup_fou_or_gue 6 6 gue
}
setup_ipvX_over_ipvY() {
inner=${1}
outer=${2}
if [ "${outer}" -eq 4 ]; then
a_addr="${prefix4}.${a_r1}.1"
b_addr="${prefix4}.${b_r1}.1" if [ "${inner}" -eq 4 ]; then
type="ipip"
mode="ipip" else
type="sit"
mode="ip6ip" fi else
a_addr="${prefix6}:${a_r1}::1"
b_addr="${prefix6}:${b_r1}::1"
type="ip6tnl" if [ "${inner}" -eq 4 ]; then
mode="ipip6" else
mode="ip6ip6" fi fi
run_cmd ${ns_a} ip link add ip_a type ${type} local ${a_addr} remote ${b_addr} mode ${mode} || return $ksft_skip
run_cmd ${ns_b} ip link add ip_b type ${type} local ${b_addr} remote ${a_addr} mode ${mode}
run_cmd ${ns_a} ip link set ip_a up
run_cmd ${ns_b} ip link set ip_b up
if [ "${inner}" = "4" ]; then
run_cmd ${ns_a} ip addr add ${tunnel4_a_addr}/${tunnel4_mask} dev ip_a
run_cmd ${ns_b} ip addr add ${tunnel4_b_addr}/${tunnel4_mask} dev ip_b else
run_cmd ${ns_a} ip addr add ${tunnel6_a_addr}/${tunnel6_mask} dev ip_a
run_cmd ${ns_b} ip addr add ${tunnel6_b_addr}/${tunnel6_mask} dev ip_b fi
}
setup_ip4ip4() {
setup_ipvX_over_ipvY 4 4
}
setup_ip6ip4() {
setup_ipvX_over_ipvY 6 4
}
setup_ip4ip6() {
setup_ipvX_over_ipvY 4 6
}
setup_ip6ip6() {
setup_ipvX_over_ipvY 6 6
}
setup_namespaces() {
setup_ns NS_A NS_B NS_C NS_R1 NS_R2 for n in ${NS_A} ${NS_B} ${NS_C} ${NS_R1} ${NS_R2}; do # Disable DAD, so that we don't have to wait to use the # configured IPv6 addresses
ip netns exec ${n} sysctl -q net/ipv6/conf/default/accept_dad=0 done
ns_a="ip netns exec ${NS_A}"
ns_b="ip netns exec ${NS_B}"
ns_c="ip netns exec ${NS_C}"
ns_r1="ip netns exec ${NS_R1}"
ns_r2="ip netns exec ${NS_R2}"
}
setup_veth() {
run_cmd ${ns_a} ip link add veth_a type veth peer name veth_b || return 1
run_cmd ${ns_a} ip link set veth_b netns ${NS_B}
run_cmd ${ns_a} ip addr add ${veth4_a_addr}/${veth4_mask} dev veth_a
run_cmd ${ns_b} ip addr add ${veth4_b_addr}/${veth4_mask} dev veth_b
run_cmd ${ns_a} ip addr add ${veth6_a_addr}/${veth6_mask} dev veth_a
run_cmd ${ns_b} ip addr add ${veth6_b_addr}/${veth6_mask} dev veth_b
run_cmd ${ns_a} ip link set veth_a up
run_cmd ${ns_b} ip link set veth_b up
}
run_cmd ${ns_a} ip link add vti${proto}_a type ${vti_type} local ${veth_a_addr} remote ${veth_b_addr} key 10 || return 1
run_cmd ${ns_b} ip link add vti${proto}_b type ${vti_type} local ${veth_b_addr} remote ${veth_a_addr} key 10
run_cmd ${ns_a} ip addr add ${vti_a_addr}/${vti_mask} dev vti${proto}_a
run_cmd ${ns_b} ip addr add ${vti_b_addr}/${vti_mask} dev vti${proto}_b
run_cmd ${ns_a} ip link set vti${proto}_a up
run_cmd ${ns_b} ip link set vti${proto}_b up
}
if [ "${type}" = "vxlan" ]; then
opts="${opts} ttl 64 dstport 4789"
opts_a="local ${a_addr}"
opts_b="local ${b_addr}" else
opts_a=""
opts_b="" fi
run_cmd ${ns_a} ip link add ${type}_a type ${type} id 1 ${opts_a} remote ${b_addr} ${opts} || return 1
run_cmd ${ns_b} ip link add ${type}_b type ${type} id 1 ${opts_b} remote ${a_addr} ${opts}
if [ -n "${br_if_a}" ]; then
run_cmd ${ns_a} ip addr add ${tunnel4_a_addr}/${tunnel4_mask} dev ${br_if_a}
run_cmd ${ns_a} ip addr add ${tunnel6_a_addr}/${tunnel6_mask} dev ${br_if_a}
run_cmd ${ns_a} ip link set ${type}_a master ${br_if_a} else
run_cmd ${ns_a} ip addr add ${tunnel4_a_addr}/${tunnel4_mask} dev ${type}_a
run_cmd ${ns_a} ip addr add ${tunnel6_a_addr}/${tunnel6_mask} dev ${type}_a fi
run_cmd ${ns_b} ip addr add ${tunnel4_b_addr}/${tunnel4_mask} dev ${type}_b
run_cmd ${ns_b} ip addr add ${tunnel6_b_addr}/${tunnel6_mask} dev ${type}_b
run_cmd ${ns_a} ip link set ${type}_a up
run_cmd ${ns_b} ip link set ${type}_b up
}
setup_routing() { for i in ${NS_R1} ${NS_R2}; do
ip netns exec ${i} sysctl -q net/ipv4/ip_forward=1
ip netns exec ${i} sysctl -q net/ipv6/conf/all/forwarding=1 done
for i in ${routing_addrs}; do
[ "${ns}" = "" ] && ns="${i}" && continue
[ "${peer}" = "" ] && peer="${i}" && continue
[ "${segment}" = "" ] && segment="${i}"
# Create veth links
ip link add ${if} up netns ${ns_name} type veth peer name ${ifpeer} netns ${peer_name} || return 1
ip -n ${peer_name} link set dev ${ifpeer} up
# Add addresses
ip -n ${ns_name} addr add ${prefix4}.${segment}.1/24 dev ${if}
ip -n ${ns_name} addr add ${prefix6}:${segment}::1/64 dev ${if}
ip -n ${peer_name} addr add ${prefix4}.${segment}.2/24 dev ${ifpeer}
ip -n ${peer_name} addr add ${prefix6}:${segment}::2/64 dev ${ifpeer}
ns=""; peer=""; segment="" done
if [ "$USE_NH" = "yes" ]; then
setup_routing_new else
setup_routing_old fi
# Set the IPv4 Don't Fragment bit with tc, since socat doesn't seem to # have an option do to it.
tc -netns "${NS_A}" qdisc replace dev veth_A-R1 root prio
tc -netns "${NS_A}" qdisc replace dev veth_A-R2 root prio
tc -netns "${NS_A}" filter add dev veth_A-R1 \
protocol ipv4 flower ip_proto udp \
action pedit ex munge ip df set 0x40 pipe csum ip and udp
tc -netns "${NS_A}" filter add dev veth_A-R2 \
protocol ipv4 flower ip_proto udp \
action pedit ex munge ip df set 0x40 pipe csum ip and udp
}
setup_bridge() {
run_cmd ${ns_a} ip link add br0 type bridge || return $ksft_skip
run_cmd ${ns_a} ip link set br0 up
run_cmd ${ns_c} ip link add veth_C-A type veth peer name veth_A-C
run_cmd ${ns_c} ip link set veth_A-C netns ${NS_A}
run_cmd ${ns_a} ip link set veth_A-C up
run_cmd ${ns_c} ip link set veth_C-A up
run_cmd ${ns_c} ip addr add ${veth4_c_addr}/${veth4_mask} dev veth_C-A
run_cmd ${ns_c} ip addr add ${veth6_c_addr}/${veth6_mask} dev veth_C-A
run_cmd ${ns_a} ip link set veth_A-C master br0
}
run_cmd ${ns_b} ip link add ${type}_b type ${type} id 1 ${opts_b} remote ${a_addr} ${opts} || return 1
run_cmd ${ns_b} ip addr add ${tunnel4_b_addr}/${tunnel4_mask} dev ${type}_b
run_cmd ${ns_b} ip addr add ${tunnel6_b_addr}/${tunnel6_mask} dev ${type}_b
run_cmd ip link set ${type}_a up
run_cmd ${ns_b} ip link set ${type}_b up
}
setup_ovs_bridge() {
setup_ovs_br_internal || setup_ovs_br_vswitchd || return $ksft_skip
run_cmd ip link set ovs_br0 up
run_cmd ${ns_c} ip link add veth_C-A type veth peer name veth_A-C
run_cmd ${ns_c} ip link set veth_A-C netns 1
run_cmd ip link set veth_A-C up
run_cmd ${ns_c} ip link set veth_C-A up
run_cmd ${ns_c} ip addr add ${veth4_c_addr}/${veth4_mask} dev veth_C-A
run_cmd ${ns_c} ip addr add ${veth6_c_addr}/${veth6_mask} dev veth_C-A
setup_ovs_add_if veth_A-C
# Move veth_A-R1 to init
run_cmd ${ns_a} ip link set veth_A-R1 netns 1
run_cmd ip addr add ${prefix4}.${a_r1}.1/${veth4_mask} dev veth_A-R1
run_cmd ip addr add ${prefix6}:${a_r1}::1/${veth6_mask} dev veth_A-R1
run_cmd ip link set veth_A-R1 up
run_cmd ip route add ${prefix4}.${b_r1}.1 via ${prefix4}.${a_r1}.2
run_cmd ip route add ${prefix6}:${b_r1}::1 via ${prefix6}:${a_r1}::2
}
setup_multipath_new() { # Set up host A with multipath routes to host B host4_b_addr
run_cmd ${ns_a} ip addr add ${host4_a_addr} dev lo
run_cmd ${ns_a} ip nexthop add id 401 via ${prefix4}.${a_r1}.2 dev veth_A-R1
run_cmd ${ns_a} ip nexthop add id 402 via ${prefix4}.${a_r2}.2 dev veth_A-R2
run_cmd ${ns_a} ip nexthop add id 403 group 401/402
run_cmd ${ns_a} ip route add ${host4_b_addr} src ${host4_a_addr} nhid 403
# Set up host B with multipath routes to host A host4_a_addr
run_cmd ${ns_b} ip addr add ${host4_b_addr} dev lo
run_cmd ${ns_b} ip nexthop add id 401 via ${prefix4}.${b_r1}.2 dev veth_B-R1
run_cmd ${ns_b} ip nexthop add id 402 via ${prefix4}.${b_r2}.2 dev veth_B-R2
run_cmd ${ns_b} ip nexthop add id 403 group 401/402
run_cmd ${ns_b} ip route add ${host4_a_addr} src ${host4_b_addr} nhid 403
}
setup_multipath_old() { # Set up host A with multipath routes to host B host4_b_addr
run_cmd ${ns_a} ip addr add ${host4_a_addr} dev lo
run_cmd ${ns_a} ip route add ${host4_b_addr} \
src ${host4_a_addr} \
nexthop via ${prefix4}.${a_r1}.2 weight 1 \
nexthop via ${prefix4}.${a_r2}.2 weight 1
# Set up host B with multipath routes to host A host4_a_addr
run_cmd ${ns_b} ip addr add ${host4_b_addr} dev lo
run_cmd ${ns_b} ip route add ${host4_a_addr} \
src ${host4_b_addr} \
nexthop via ${prefix4}.${b_r1}.2 weight 1 \
nexthop via ${prefix4}.${b_r2}.2 weight 1
}
setup_multipath() { if [ "$USE_NH" = "yes" ]; then
setup_multipath_new else
setup_multipath_old fi
# Set up routers with routes to dummies
run_cmd ${ns_r1} ip route add ${host4_a_addr} via ${prefix4}.${a_r1}.1
run_cmd ${ns_r2} ip route add ${host4_a_addr} via ${prefix4}.${a_r2}.1
run_cmd ${ns_r1} ip route add ${host4_b_addr} via ${prefix4}.${b_r1}.1
run_cmd ${ns_r2} ip route add ${host4_b_addr} via ${prefix4}.${b_r2}.1
}
setup() {
[ "$(id -u)" -ne 0 ] && echo" need to run as root" && return $ksft_skip
for arg do eval setup_${arg} || { echo" ${arg} not supported"; return 1; } done
}
if [ ${family} -eq 4 ]; then
ping=ping
dst1="${prefix4}.${b_r1}.1"
dst2="${prefix4}.${b_r2}.1" else
ping=${ping6}
dst1="${prefix6}:${b_r1}::1"
dst2="${prefix6}:${b_r2}::1" fi
# Set up initial MTU values
mtu "${ns_a}" veth_A-R1 2000
mtu "${ns_r1}" veth_R1-A 2000
mtu "${ns_r1}" veth_R1-B 1400
mtu "${ns_b}" veth_B-R1 1400
mtu "${ns_a}" veth_A-R2 2000
mtu "${ns_r2}" veth_R2-A 2000
mtu "${ns_r2}" veth_R2-B 1500
mtu "${ns_b}" veth_B-R2 1500
# Check that exceptions have been created with the correct PMTU
pmtu_1="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst1})"
check_pmtu_value "1400""${pmtu_1}""exceeding MTU" || return 1
pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst2})"
check_pmtu_value "1500""${pmtu_2}""exceeding MTU" || return 1
# Decrease local MTU below PMTU, check for PMTU decrease in route exception
mtu "${ns_a}" veth_A-R1 1300
mtu "${ns_r1}" veth_R1-A 1300
pmtu_1="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst1})"
check_pmtu_value "1300""${pmtu_1}""decreasing local MTU" || return 1 # Second exception shouldn't be modified
pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst2})"
check_pmtu_value "1500""${pmtu_2}""changing local MTU on a link not on this path"|| return 1
# Increase MTU, check for PMTU increase in route exception
mtu "${ns_a}" veth_A-R1 1700
mtu "${ns_r1}" veth_R1-A 1700
pmtu_1="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst1})"
check_pmtu_value "1700""${pmtu_1}""increasing local MTU" || return 1 # Second exception shouldn't be modified
pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst2})"
check_pmtu_value "1500""${pmtu_2}""changing local MTU on a link not on this path"|| return 1
# Decrease remote MTU on path via R2, get new exception
mtu "${ns_r2}" veth_R2-B 400
mtu "${ns_b}" veth_B-R2 400
run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -w 1 -s 1400 ${dst2}
pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst2})"
check_pmtu_value "lock 552""${pmtu_2}""exceeding MTU, with MTU < min_pmtu" || return 1
# Decrease local MTU below PMTU
mtu "${ns_a}" veth_A-R2 500
mtu "${ns_r2}" veth_R2-A 500
pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst2})"
check_pmtu_value "500""${pmtu_2}""decreasing local MTU" || return 1
# Increase local MTU
mtu "${ns_a}" veth_A-R2 1500
mtu "${ns_r2}" veth_R2-A 1500
pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst2})"
check_pmtu_value "1500""${pmtu_2}""increasing local MTU" || return 1
# Get new exception
run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -w 1 -s 1400 ${dst2}
pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst2})"
check_pmtu_value "lock 552""${pmtu_2}""exceeding MTU, with MTU < min_pmtu" || return 1
}
# Check that exceptions have been created with the correct PMTU
pmtu_1="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst1}" dsfield "${policy_mark}")"
check_pmtu_value "1400""${pmtu_1}""exceeding MTU" || return 1
dsfield=${policy_mark} # No ECN bit set (Not-ECT)
run_cmd "${ns_a}" socat OPEN:/dev/zero,rdonly=1,readbytes="${len}" \
UDP:"${dst1}":50000,tos="${dsfield}"
if [ ${family} -eq 4 ]; then
ping=ping
dst=${tunnel4_b_addr} else
ping=${ping6}
dst=${tunnel6_b_addr} fi
# Create route exception by exceeding link layer MTU
mtu "${ns_a}" veth_A-R1 $((${ll_mtu} + 1000))
mtu "${ns_r1}" veth_R1-A $((${ll_mtu} + 1000))
mtu "${ns_b}" veth_B-R1 ${ll_mtu}
mtu "${ns_r1}" veth_R1-B ${ll_mtu}
# Check that exception was created
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst})"
check_pmtu_value ${exp_mtu} "${pmtu}""exceeding link layer MTU on ${type} interface"
}
if [ ${family} -eq 4 ]; then
ping=ping
dst=${tunnel4_b_addr} else
ping=${ping6}
dst=${tunnel6_b_addr} fi
# Create route exception by exceeding link layer MTU
mtu "${ns_a}" veth_A-R1 $((${ll_mtu} + 1000))
mtu "${ns_a}" br0 $((${ll_mtu} + 1000))
mtu "${ns_a}" veth_A-C $((${ll_mtu} + 1000))
mtu "${ns_c}" veth_C-A $((${ll_mtu} + 1000))
mtu "${ns_r1}" veth_R1-A $((${ll_mtu} + 1000))
mtu "${ns_b}" veth_B-R1 ${ll_mtu}
mtu "${ns_r1}" veth_R1-B ${ll_mtu}
mtu "${ns_a}" ${type}_a $((${ll_mtu} + 1000))
mtu "${ns_b}" ${type}_b $((${ll_mtu} + 1000))
# Check that exceptions were created
pmtu="$(route_get_dst_pmtu_from_exception "${ns_c}" ${dst})"
check_pmtu_value ${exp_mtu} "${pmtu}""exceeding link layer MTU on bridged ${type} interface"
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst})"
check_pmtu_value ${exp_mtu} "${pmtu}""exceeding link layer MTU on locally bridged ${type} interface"
tmpoutfile=$(mktemp)
# Flush Exceptions, retry with TCP
run_cmd ${ns_a} ip route flush cached ${dst}
run_cmd ${ns_b} ip route flush cached ${dst}
run_cmd ${ns_c} ip route flush cached ${dst}
for target in "${ns_a}""${ns_c}" ; do if [ ${family} -eq 4 ]; then
TCPDST=TCP:${dst}:50000 else
TCPDST="TCP:[${dst}]:50000" fi
${ns_b} socat -T 3 -u -6 TCP-LISTEN:50000,reuseaddr STDOUT > $tmpoutfile &
local socat_pid=$!
# Check that exceptions were created
pmtu="$(route_get_dst_pmtu_from_exception "${ns_c}" ${dst})"
check_pmtu_value ${exp_mtu} "${pmtu}""tcp: exceeding link layer MTU on bridged ${type} interface"
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst})"
check_pmtu_value ${exp_mtu} "${pmtu}""tcp exceeding link layer MTU on locally bridged ${type} interface"
}
if [ ${family} -eq 4 ]; then
ping=ping
dst=${tunnel4_b_addr} else
ping=${ping6}
dst=${tunnel6_b_addr} fi
# Create route exception by exceeding link layer MTU
mtu "" veth_A-R1 $((${ll_mtu} + 1000))
mtu "" ovs_br0 $((${ll_mtu} + 1000))
mtu "" veth_A-C $((${ll_mtu} + 1000))
mtu "${ns_c}" veth_C-A $((${ll_mtu} + 1000))
mtu "${ns_r1}" veth_R1-A $((${ll_mtu} + 1000))
mtu "${ns_b}" veth_B-R1 ${ll_mtu}
mtu "${ns_r1}" veth_R1-B ${ll_mtu}
mtu "" ${tun_a} $((${ll_mtu} + 1000)) 2>/dev/null || \
mtu "" ${type}_a $((${ll_mtu} + 1000)) 2>/dev/null
mtu "${ns_b}" ${type}_b $((${ll_mtu} + 1000))
# Check that exceptions were created
pmtu="$(route_get_dst_pmtu_from_exception "${ns_c}" ${dst})"
check_pmtu_value ${exp_mtu} "${pmtu}""exceeding link layer MTU on Open vSwitch ${type} interface"
}
# Create route exception by exceeding link layer MTU
mtu "${ns_a}" veth_A-R1 $((${ll_mtu} + 1000))
mtu "${ns_r1}" veth_R1-A $((${ll_mtu} + 1000))
mtu "${ns_b}" veth_B-R1 ${ll_mtu}
mtu "${ns_r1}" veth_R1-B ${ll_mtu}
# Check that exception was created
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst})"
check_pmtu_value ${exp_mtu} "${pmtu}""exceeding link layer MTU on ${encap} interface"
}
test_pmtu_ipv4_fou4_exception() {
test_pmtu_ipvX_over_fouY_or_gueY 4 4 fou
}
test_pmtu_ipv6_fou4_exception() {
test_pmtu_ipvX_over_fouY_or_gueY 6 4 fou
}
test_pmtu_ipv4_fou6_exception() {
test_pmtu_ipvX_over_fouY_or_gueY 4 6 fou
}
test_pmtu_ipv6_fou6_exception() {
test_pmtu_ipvX_over_fouY_or_gueY 6 6 fou
}
if [ ${inner} -eq 4 ]; then
ping=ping
dst=${tunnel4_b_addr} else
ping=${ping6}
dst=${tunnel6_b_addr} fi
if [ ${outer} -eq 4 ]; then # IPv4 header
exp_mtu=$((${ll_mtu} - 20)) else # IPv6 header Option 4
exp_mtu=$((${ll_mtu} - 40 - 8)) fi
# Create route exception by exceeding link layer MTU
mtu "${ns_a}" veth_A-R1 $((${ll_mtu} + 1000))
mtu "${ns_r1}" veth_R1-A $((${ll_mtu} + 1000))
mtu "${ns_b}" veth_B-R1 ${ll_mtu}
mtu "${ns_r1}" veth_R1-B ${ll_mtu}
# Check that exception was created
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst})"
check_pmtu_value ${exp_mtu} "${pmtu}""exceeding link layer MTU on ip${inner}ip${outer} interface"
}
# SPI SN IV ICV pad length next header
esp_payload_rfc4106=$((vti_mtu - 4 - 4 - 8 - 16 - 1 - 1))
ping_payload=$((esp_payload_rfc4106 - 28))
mtu "${ns_a}" veth_a ${veth_mtu}
mtu "${ns_b}" veth_b ${veth_mtu}
mtu "${ns_a}" vti4_a ${vti_mtu}
mtu "${ns_b}" vti4_b ${vti_mtu}
# Send DF packet without exceeding link layer MTU, check that no # exception is created
run_cmd ${ns_a} ping -q -M want -i 0.1 -w 1 -s ${ping_payload} ${tunnel4_b_addr}
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel4_b_addr})"
check_pmtu_value """${pmtu}""sending packet smaller than PMTU (IP payload length ${esp_payload_rfc4106})" || return 1
# Now exceed link layer MTU by one byte, check that exception is created # with the right PMTU value
run_cmd ${ns_a} ping -q -M want -i 0.1 -w 1 -s $((ping_payload + 1)) ${tunnel4_b_addr}
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel4_b_addr})"
check_pmtu_value "${esp_payload_rfc4106}""${pmtu}""exceeding PMTU (IP payload length $((esp_payload_rfc4106 + 1)))"
}
# Create route exception by exceeding link layer MTU
mtu "${ns_a}" veth_a 4000
mtu "${ns_b}" veth_b 4000
mtu "${ns_a}" vti6_a 5000
mtu "${ns_b}" vti6_b 5000
run_cmd ${ns_a} ${ping6} -q -i 0.1 -w 1 -s 60000 ${tunnel6_b_addr}
# Check that exception was created
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel6_b_addr})"
check_pmtu_value any "${pmtu}""creating tunnel exceeding link layer MTU" || return 1
# Decrease tunnel MTU, check for PMTU decrease in route exception
mtu "${ns_a}" vti6_a 3000
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel6_b_addr})"
check_pmtu_value "3000""${pmtu}""decreasing tunnel MTU" || fail=1
# Increase tunnel MTU, check for PMTU increase in route exception
mtu "${ns_a}" vti6_a 9000
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel6_b_addr})"
check_pmtu_value "9000""${pmtu}""increasing tunnel MTU" || fail=1
# UDP SPI SN IV ICV pad length next header
esp_payload_rfc4106=$((vti_mtu - 8 - 4 - 4 - 8 - 16 - 1 - 1))
ping_payload=$((esp_payload_rfc4106 - 28))
mtu "${ns_a}" veth_a ${veth_mtu}
mtu "${ns_b}" veth_b ${veth_mtu}
mtu "${ns_a}" vti4_a ${vti_mtu}
mtu "${ns_b}" vti4_b ${vti_mtu}
# Send DF packet without exceeding link layer MTU, check that no # exception is created
run_cmd ${ns_a} ping -q -M want -i 0.1 -w 1 -s ${ping_payload} ${tunnel4_b_addr}
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel4_b_addr})"
check_pmtu_value """${pmtu}""sending packet smaller than PMTU (IP payload length ${esp_payload_rfc4106})" || return 1
# Now exceed link layer MTU by one byte, check that exception is created # with the right PMTU value
run_cmd ${ns_a} ping -q -M want -i 0.1 -w 1 -s $((ping_payload + 1)) ${tunnel4_b_addr}
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel4_b_addr})"
check_pmtu_value "${esp_payload_rfc4106}""${pmtu}""exceeding PMTU (IP payload length $((esp_payload_rfc4106 + 1)))"
}
# Create route exception by exceeding link layer MTU
mtu "${ns_a}" veth_a 4000
mtu "${ns_b}" veth_b 4000
mtu "${ns_a}" vti6_a 5000
mtu "${ns_b}" vti6_b 5000
run_cmd ${ns_a} ${ping6} -q -i 0.1 -w 1 -s 60000 ${tunnel6_b_addr}
# Check that exception was created
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel6_b_addr})"
check_pmtu_value any "${pmtu}""creating tunnel exceeding link layer MTU" || return 1
# Decrease tunnel MTU, check for PMTU decrease in route exception
mtu "${ns_a}" vti6_a 3000
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel6_b_addr})"
check_pmtu_value "3000""${pmtu}""decreasing tunnel MTU" || fail=1
# Increase tunnel MTU, check for PMTU increase in route exception
mtu "${ns_a}" vti6_a 9000
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel6_b_addr})"
check_pmtu_value "9000""${pmtu}""increasing tunnel MTU" || fail=1
# UDP SPI SN IV ICV pad length next header
esp_payload_rfc4106=$((vti_mtu - 8 - 4 - 4 - 8 - 16 - 1 - 1))
ping_payload=$((esp_payload_rfc4106 - 28))
mtu "${ns_a}" veth_A-R1 ${veth_mtu}
mtu "${ns_r1}" veth_R1-A ${veth_mtu}
mtu "${ns_b}" veth_B-R1 ${veth_mtu}
mtu "${ns_r1}" veth_R1-B ${veth_mtu}
mtu "${ns_a}" vti4_a ${vti_mtu}
mtu "${ns_b}" vti4_b ${vti_mtu}
# Send DF packet without exceeding link layer MTU, check that no # exception is created
run_cmd ${ns_a} ping -q -M want -i 0.1 -w 1 -s ${ping_payload} ${tunnel4_b_addr}
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel4_b_addr})"
check_pmtu_value """${pmtu}""sending packet smaller than PMTU (IP payload length ${esp_payload_rfc4106})" || return 1
# Now decrease link layer MTU by 8 bytes on R1, check that exception is created # with the right PMTU value
mtu "${ns_r1}" veth_R1-B $((veth_mtu - 8))
run_cmd ${ns_a} ping -q -M want -i 0.1 -w 1 -s $((ping_payload)) ${tunnel4_b_addr}
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel4_b_addr})"
check_pmtu_value "$((esp_payload_rfc4106 - 8))""${pmtu}""exceeding PMTU (IP payload length $((esp_payload_rfc4106)))"
}
# Check that exception was not created
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel6_b_addr})"
check_pmtu_value """${pmtu}""sending packet smaller than PMTU (IP payload length ${esp_payload_rfc4106})" || return 1
# Now decrease link layer MTU by 8 bytes on R1, check that exception is created # with the right PMTU value
mtu "${ns_r1}" veth_R1-B $((veth_mtu - 8))
run_cmd ${ns_a} ${ping6} -q -M want -i 0.1 -w 1 -s $((ping_payload)) ${tunnel6_b_addr}
pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel6_b_addr})"
check_pmtu_value "$((esp_payload_rfc4106 - 8))""${pmtu}""exceeding PMTU (IP payload length $((esp_payload_rfc4106)))"
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.