selftests: pmtu: add basic IPv4 and IPv6 PMTU tests
authorSabrina Dubroca <sd@queasysnail.net>
Mon, 8 Oct 2018 12:37:05 +0000 (14:37 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 8 Oct 2018 18:00:23 +0000 (11:00 -0700)
Commit d1f1b9cbf34c ("selftests: net: Introduce first PMTU test") and
follow-ups introduced some PMTU tests, but they all rely on tunneling,
and, particularly, on VTI.

These new tests use simple routing to exercise the generation and
update of PMTU exceptions in IPv4 and IPv6.

Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
tools/testing/selftests/net/pmtu.sh

index 03e56a27f69caf51b1fc8e39537ab415b2a3da16..b9cdb68df4c5f8ef4fbed88150ca0cc467ca5e85 100755 (executable)
@@ -6,6 +6,26 @@
 #
 # 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--------------.        a_r2: 1500
+#      A                               B       a_r3: 2000
+#      '--------------R2--------------'        a_r4: 1400
+#        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_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
@@ -50,6 +70,8 @@ ksft_skip=4
 which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
 
 tests="
+       pmtu_ipv4_exception             ipv4: PMTU exceptions
+       pmtu_ipv6_exception             ipv6: PMTU exceptions
        pmtu_vti6_exception             vti6: PMTU exceptions
        pmtu_vti4_exception             vti4: PMTU exceptions
        pmtu_vti4_default_mtu           vti4: default MTU assignment
@@ -60,8 +82,45 @@ tests="
 
 NS_A="ns-$(mktemp -u XXXXXX)"
 NS_B="ns-$(mktemp -u XXXXXX)"
+NS_R1="ns-$(mktemp -u XXXXXX)"
+NS_R2="ns-$(mktemp -u XXXXXX)"
 ns_a="ip netns exec ${NS_A}"
 ns_b="ip netns exec ${NS_B}"
+ns_r1="ip netns exec ${NS_R1}"
+ns_r2="ip netns exec ${NS_R2}"
+
+# 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="192.168"
+prefix6="fd00"
+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
+"
 
 veth4_a_addr="192.168.1.1"
 veth4_b_addr="192.168.1.2"
@@ -94,9 +153,15 @@ err_flush() {
        err_buf=
 }
 
+# Find the auto-generated name for this namespace
+nsname() {
+       eval echo \$NS_$1
+}
+
 setup_namespaces() {
-       ip netns add ${NS_A} || return 1
-       ip netns add ${NS_B}
+       for n in ${NS_A} ${NS_B} ${NS_R1} ${NS_R2}; do
+               ip netns add ${n} || return 1
+       done
 }
 
 setup_veth() {
@@ -167,6 +232,49 @@ setup_xfrm6() {
        setup_xfrm 6 ${veth6_a_addr} ${veth6_b_addr}
 }
 
+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}"
+
+               ns_name="$(nsname ${ns})"
+               peer_name="$(nsname ${peer})"
+               if="veth_${ns}-${peer}"
+               ifpeer="veth_${peer}-${ns}"
+
+               # 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
+
+       for i in ${routes}; do
+               [ "${ns}" = "" ]        && ns="${i}"            && continue
+               [ "${addr}" = "" ]      && addr="${i}"          && continue
+               [ "${gw}" = "" ]        && gw="${i}"
+
+               ns_name="$(nsname ${ns})"
+
+               ip -n ${ns_name} route add ${addr} via ${gw}
+
+               ns=""; addr=""; gw=""
+       done
+}
+
 setup() {
        [ "$(id -u)" -ne 0 ] && echo "  need to run as root" && return $ksft_skip
 
@@ -178,8 +286,9 @@ setup() {
 
 cleanup() {
        [ ${cleanup_done} -eq 1 ] && return
-       ip netns del ${NS_A} 2> /dev/null
-       ip netns del ${NS_B} 2> /dev/null
+       for n in ${NS_A} ${NS_B} ${NS_R1} ${NS_R2}; do
+               ip netns del ${n} 2> /dev/null
+       done
        cleanup_done=1
 }
 
@@ -244,6 +353,96 @@ check_pmtu_value() {
        return 1
 }
 
+test_pmtu_ipvX() {
+       family=${1}
+
+       setup namespaces routing || return 2
+
+       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
+
+       # Create route exceptions
+       ${ns_a} ${ping} -q -M want -i 0.1 -w 2 -s 1800 ${dst1} > /dev/null
+       ${ns_a} ${ping} -q -M want -i 0.1 -w 2 -s 1800 ${dst2} > /dev/null
+
+       # 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
+
+       # Skip PMTU locking tests for IPv6
+       [ $family -eq 6 ] && return 0
+
+       # Decrease remote MTU on path via R2, get new exception
+       mtu "${ns_r2}" veth_R2-B 400
+       mtu "${ns_b}"  veth_B-R2 400
+       ${ns_a} ${ping} -q -M want -i 0.1 -w 2 -s 1400 ${dst2} > /dev/null
+       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
+       ${ns_a} ${ping} -q -M want -i 0.1 -w 2 -s 1400 ${dst2} > /dev/null
+       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
+}
+
+test_pmtu_ipv4_exception() {
+       test_pmtu_ipvX 4
+}
+
+test_pmtu_ipv6_exception() {
+       test_pmtu_ipvX 6
+}
+
 test_pmtu_vti4_exception() {
        setup namespaces veth vti4 xfrm4 || return 2