diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist --- a/etc/mtree/BSD.tests.dist +++ b/etc/mtree/BSD.tests.dist @@ -1273,6 +1273,8 @@ .. syslogd .. + traceroute + .. .. .. diff --git a/usr.sbin/traceroute/Makefile b/usr.sbin/traceroute/Makefile --- a/usr.sbin/traceroute/Makefile +++ b/usr.sbin/traceroute/Makefile @@ -7,6 +7,9 @@ BINOWN= root BINMODE=4555 +HAS_TESTS= +SUBDIR.${MK_TESTS}+= tests + .if !defined(TRACEROUTE_NO_IPSEC) CFLAGS+= -DIPSEC .endif diff --git a/usr.sbin/traceroute/tests/Makefile b/usr.sbin/traceroute/tests/Makefile new file mode 100644 --- /dev/null +++ b/usr.sbin/traceroute/tests/Makefile @@ -0,0 +1,7 @@ +ATF_TESTS_SH+= traceroute_test + +# Allow tests to run in parallel in their own jails +TEST_METADATA+= execenv="jail" +TEST_METADATA+= execenv_jail_params="vnet allow.raw_sockets" + +.include diff --git a/usr.sbin/traceroute/tests/traceroute_test.sh b/usr.sbin/traceroute/tests/traceroute_test.sh new file mode 100755 --- /dev/null +++ b/usr.sbin/traceroute/tests/traceroute_test.sh @@ -0,0 +1,880 @@ +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2025 Lexi Winter +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# We are missing tests for the following flags: +# +# -a (turn on ASN lookups) +# -A (specify ASN lookup server) +# -d (enable SO_DEBUG) +# -D (print the diff between our packet and the quote in the ICMP error) +# -E (detect ECN bleaching) +# -n (or rather, we enable -n by default and don't test without it) +# -S (print per-hop packet loss) +# -v (verbose output) +# -w (how long to wait for an error response) +# -x (toggle IP checksums) +# -z (how long to wait between each probe) + +. $(atf_get_srcdir)/../../sys/common/vnet.subr + +# These are the default flags we use for most test cases: +# - only send a single probe packet to reduce the risk of kernel ICMP +# rate-limiting breaking the test. +# - only trace up to 5 hops and only wait 1 second for a response so the test +# fails quicker if something goes wrong. +# - disable DNS resolution as we don't usually care about this. +TR_FLAGS="-w 1 -q 1 -m 5 -n" + +# The prefix our test networks are in. +TEST_PREFIX="192.0.2.0/24" + +# The IPv4 addresses of the first link net between trsrc and trrtr. +LINK_TRSRC_TRSRC="192.0.2.5" +LINK_TRSRC_TRRTR="192.0.2.6" +LINK_TRSRC_PREFIXLEN="30" + +# The IPv4 addresses of the second link net between trsrc and trrtr. +LINK_TRSRC2_TRSRC="192.0.2.13" +LINK_TRSRC2_TRRTR="192.0.2.14" +LINK_TRSRC2_PREFIXLEN="30" + +# The IPv4 addresses of the link net between trdst and trrtr. +LINK_TRDST_TRDST="192.0.2.9" +LINK_TRDST_TRRTR="192.0.2.10" +LINK_TRDST_PREFIXLEN="30" + +# This is an address inside $TEST_PREFIX which is not routed anywhere. +UNREACHABLE_ADDR="192.0.2.255" + +setup_network() +{ + # Create 3 jails: one to be the source host, one to be the router, + # and one to be the destination host. + + vnet_init + + # src jail + epsrc=$(vnet_mkepair) + epsrc2=$(vnet_mkepair) + vnet_mkjail trsrc ${epsrc}a ${epsrc2}a + + # dst jail + epdst=$(vnet_mkepair) + vnet_mkjail trdst ${epdst}a + + # router jail + vnet_mkjail trrtr ${epsrc}b ${epsrc2}b ${epdst}b + + # Configure IPv4 addresses and routes on each jail. + + # trsrc + jexec trsrc ifconfig ${epsrc}a inet \ + ${LINK_TRSRC_TRSRC}/${LINK_TRSRC_PREFIXLEN} + jexec trrtr ifconfig ${epsrc}b inet \ + ${LINK_TRSRC_TRRTR}/${LINK_TRSRC_PREFIXLEN} + jexec trsrc route add -inet ${TEST_PREFIX} ${LINK_TRSRC_TRRTR} + + # trsrc2 + jexec trsrc ifconfig ${epsrc2}a inet \ + ${LINK_TRSRC2_TRSRC}/${LINK_TRSRC2_PREFIXLEN} + jexec trrtr ifconfig ${epsrc2}b inet \ + ${LINK_TRSRC2_TRRTR}/${LINK_TRSRC2_PREFIXLEN} + + # trdst + jexec trdst ifconfig ${epdst}a inet \ + ${LINK_TRDST_TRDST}/${LINK_TRDST_PREFIXLEN} + jexec trrtr ifconfig ${epdst}b inet \ + ${LINK_TRDST_TRRTR}/${LINK_TRDST_PREFIXLEN} + jexec trdst route add -inet ${TEST_PREFIX} ${LINK_TRDST_TRRTR} + + # The router jail (only) needs IP forwarding enabled. + jexec trrtr sysctl net.inet.ip.forwarding=1 +} + +## +# +# start_tcpdump, stop_tcpdump: used to capture packets during the test so we +# can verify we actually sent the expected packets. + +start_tcpdump() +{ + # Run tcpdump on trrtr, either on the given interface or on + # ${epsrc}b, which is trsrc's default route interface. + + interface="$1" + if [ -z "$interface" ]; then + interface="${epsrc}b" + fi + + rm -f "${PWD}/traceroute.pcap" + + jexec trrtr daemon -p "${PWD}/tcpdump.pid" \ + tcpdump --immediate-mode -w "${PWD}/traceroute.pcap" -nv \ + -i $interface + + # Give tcpdump time to start + sleep 1 +} + +stop_tcpdump() +{ + # Sleep to give tcpdump a chance to finish flushing + jexec trrtr kill -USR2 $(cat "${PWD}/tcpdump.pid") + sleep 1 + jexec trrtr kill $(cat "${PWD}/tcpdump.pid") + + # Format the packet capture and merge continued lines (starting with + # whitespace) into a single line; this makes it easier to match in + # atf_check. Append a blank line since the N command exits on EOF. + (tcpdump -nv -r "${PWD}/traceroute.pcap"; echo) | \ + sed -E -e :a -e N -e 's/\n +/ /' -e ta -e P -e D \ + > tcpdump.output +} + +## +# test: ipv4_basic +# + +atf_test_case "ipv4_basic" "cleanup" +ipv4_basic_head() +{ + atf_set descr "Basic IPv4 traceroute across a router" + atf_set require.user root +} + +ipv4_basic_body() +{ + setup_network + + # Use a more detailed set of regexp here than the rest of the tests to + # make sure the basic output format is correct. + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST} \\(${LINK_TRDST_TRDST}\\), 5 hops max, 40 byte packets$" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR} [0-9.]+ ms$" \ + -o match:"^ 2 ${LINK_TRDST_TRDST} [0-9.]+ ms$" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS ${LINK_TRDST_TRDST} +} + +ipv4_basic_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_icmp +# + +atf_test_case "ipv4_icmp" "cleanup" +ipv4_icmp_head() +{ + atf_set descr "Basic IPv4 ICMP traceroute across a router" + atf_set require.user root +} + +ipv4_icmp_body() +{ + setup_network + + # -I and -Picmp should mean the same thing, so test both. + + for icmp_flag in -Picmp -I; do + start_tcpdump + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 ${LINK_TRDST_TRDST}" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS $icmp_flag \ + ${LINK_TRDST_TRDST} + + stop_tcpdump + + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto ICMP.*\\).* ${LINK_TRSRC_TRSRC} > ${LINK_TRDST_TRDST}: ICMP echo request" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto ICMP.*\\).* ${LINK_TRSRC_TRSRC} > ${LINK_TRDST_TRDST}: ICMP echo request" \ + cat tcpdump.output + done +} + +ipv4_icmp_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_udp +# + +atf_test_case "ipv4_udp" "cleanup" +ipv4_udp_head() +{ + atf_set descr "IPv4 UDP traceroute" + atf_set require.user root +} + +ipv4_udp_body() +{ + setup_network + + start_tcpdump + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 ${LINK_TRDST_TRDST}" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS -Pudp ${LINK_TRDST_TRDST} + + stop_tcpdump + + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto UDP .*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33435: UDP" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto UDP .*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33436: UDP" \ + cat tcpdump.output + + # Test with -e, the destination port should not increment. + + start_tcpdump + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 ${LINK_TRDST_TRDST}" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS -Pudp -e -p 40000 ${LINK_TRDST_TRDST} + + stop_tcpdump + + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto UDP .*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.40000: UDP" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto UDP .*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.40000: UDP" \ + cat tcpdump.output +} + +ipv4_udp_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_sctp +# + +atf_test_case "ipv4_sctp" "cleanup" +ipv4_sctp_head() +{ + atf_set descr "IPv4 SCTP traceroute" + atf_set require.user root +} + +ipv4_sctp_body() +{ + setup_network + + # For the default packet size, we should sent a SHUTDOWN ACK packet. + + start_tcpdump + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 ${LINK_TRDST_TRDST} .* !P" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS -Psctp ${LINK_TRDST_TRDST} + + stop_tcpdump + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto SCTP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33435: sctp \(1\) \[SHUTDOWN ACK\]" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto SCTP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33436: sctp \(1\) \[SHUTDOWN ACK\]" \ + cat tcpdump.output + + # For a larger packet size we should send INIT packets. + + start_tcpdump + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 ${LINK_TRDST_TRDST} .* !P" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS -Psctp ${LINK_TRDST_TRDST} 128 + + stop_tcpdump + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto SCTP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33435: sctp \(1\) \[INIT\]" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto SCTP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33436: sctp \(1\) \[INIT\]" \ + cat tcpdump.output + + # Test with -e, the destination port should not increment. + + start_tcpdump + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 ${LINK_TRDST_TRDST} .* !P" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS -Psctp -e -p 40000 ${LINK_TRDST_TRDST} + + stop_tcpdump + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto SCTP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.40000: sctp \(1\) \[SHUTDOWN ACK\]" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto SCTP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.40000: sctp \(1\) \[SHUTDOWN ACK\]" \ + cat tcpdump.output +} + +ipv4_sctp_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_tcp +# + +atf_test_case "ipv4_tcp" "cleanup" +ipv4_tcp_head() +{ + atf_set descr "IPv4 TCP traceroute" + atf_set require.user root +} + +ipv4_tcp_body() +{ + setup_network + + start_tcpdump + + # We expect the second hop to be a failure since traceroute doesn't + # know how to capture the RST packet. + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 \\*" \ + jexec trsrc traceroute $TR_FLAGS -Ptcp ${LINK_TRDST_TRDST} + + stop_tcpdump + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto TCP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33435: Flags \[S\]" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto TCP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33436: Flags \[S\]" \ + cat tcpdump.output + + # Test with -e, the destination port should not increment. + start_tcpdump + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 \\*" \ + jexec trsrc traceroute $TR_FLAGS -Ptcp -e -p 40000 ${LINK_TRDST_TRDST} + + stop_tcpdump + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto TCP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.40000: Flags \[S\]" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto TCP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.40000: Flags \[S\]" \ + cat tcpdump.output +} + +ipv4_tcp_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_srcaddr +# + +atf_test_case "ipv4_srcaddr" "cleanup" +ipv4_srcaddr_head() +{ + atf_set descr "IPv4 traceroute with explicit source address" + atf_set require.user root +} + +ipv4_srcaddr_body() +{ + setup_network + + start_tcpdump + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST} \\($LINK_TRDST_TRDST\\) from ${LINK_TRSRC2_TRSRC}" \ + -o match:"^ 1 ${LINK_TRSRC2_TRRTR}" \ + -o match:"^ 2 ${LINK_TRDST_TRDST}" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS \ + -s ${LINK_TRSRC2_TRSRC} ${LINK_TRDST_TRDST} + + stop_tcpdump + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto UDP.*\\).* ${LINK_TRSRC2_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33435: UDP" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto UDP.*\\).* ${LINK_TRSRC2_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33436: UDP" \ + cat tcpdump.output +} + +ipv4_srcaddr_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_srcinterface +# + +atf_test_case "ipv4_srcinterface" "cleanup" +ipv4_srcinterface_head() +{ + atf_set descr "IPv4 traceroute with explicit source interface" + atf_set require.user root +} + +ipv4_srcinterface_body() +{ + setup_network + + start_tcpdump + + # Unlike -s, traceroute doesn't print 'from ...' when using -i. + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC2_TRRTR}" \ + -o match:"^ 2 ${LINK_TRDST_TRDST}" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS \ + -i ${epsrc2}a ${LINK_TRDST_TRDST} + + stop_tcpdump + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto UDP.*\\).* ${LINK_TRSRC2_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33435: UDP" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto UDP.*\\).* ${LINK_TRSRC2_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33436: UDP" \ + cat tcpdump.output +} + +ipv4_srcinterface_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_maxhops +# + +atf_test_case "ipv4_maxhops" "cleanup" +ipv4_maxhops_head() +{ + atf_set descr "IPv4 traceroute with -m" + atf_set require.user root +} + +ipv4_maxhops_body() +{ + setup_network + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o not-match:"^ 2" \ + jexec trsrc traceroute -w1 -q1 -m1 ${LINK_TRDST_TRDST} +} + +ipv4_maxhops_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_unreachable +# + +atf_test_case "ipv4_unreachable" "cleanup" +ipv4_unreachable_head() +{ + atf_set descr "IPv4 traceroute to an unreachable destination" + atf_set require.user root +} + +ipv4_unreachable_body() +{ + setup_network + + atf_check -s exit:0 \ + -e match:"^traceroute to ${UNREACHABLE_ADDR}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 ${LINK_TRSRC_TRRTR} [0-9.]+ ms !H" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS $UNREACHABLE_ADDR +} + +ipv4_unreachable_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_hugepacket +# + +atf_test_case "ipv4_hugepacket" "cleanup" +ipv4_hugepacket_head() +{ + atf_set descr "IPv4 traceroute with a huge packet" + atf_set require.user root +} + +ipv4_hugepacket_body() +{ + setup_network + + # We expect this to fail since we specified -F (don't fragment) and the + # 2000-byte packet is too large to fit through our tiny epair. Make + # sure traceroute reports the error. + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST} \\(${LINK_TRDST_TRDST}\\), 5 hops max, 2000 byte packets$" \ + -o match:"^ 1 traceroute: wrote ${LINK_TRDST_TRDST} 2000 chars, ret=-1" \ + -e match:"^traceroute: sendto: Message too long" \ + jexec trsrc traceroute -F $TR_FLAGS ${LINK_TRDST_TRDST} 2000 +} + +ipv4_hugepacket_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_firsthop +# + +atf_test_case "ipv4_firsthop" "cleanup" +ipv4_firsthop_head() +{ + atf_set descr "IPv4 traceroute with one hop skipped" + atf_set require.user root +} + +ipv4_firsthop_body() +{ + setup_network + + # -f 2 means we skip the first hop. For backward compatibility, -M is + # the same as -f, so test that too. + + for flag in -f2 -M2; do + start_tcpdump + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o not-match:"^ 1" \ + -o match:"^ 2 ${LINK_TRDST_TRDST}" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $flag $TR_FLAGS ${LINK_TRDST_TRDST} + + stop_tcpdump + atf_check -s exit:0 -e ignore \ + -o not-match:"^..:..:..\....... IP \\(tos 0x0, ttl 1, .*, proto UDP.*\\)" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto UDP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33435: UDP" \ + cat tcpdump.output + done +} + +ipv4_firsthop_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_nprobes +# + +atf_test_case "ipv4_nprobes" "cleanup" +ipv4_nprobes_head() +{ + atf_set descr "IPv4 traceroute with varying number of probes" + atf_set require.user root +} + +ipv4_nprobes_body() +{ + setup_network + + # By default we should send 3 probes. + atf_check -s exit:0 -e ignore \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR} \(${LINK_TRSRC_TRRTR}\)( [0-9.]+ ms){3}$" \ + jexec trsrc traceroute -w1 -m1 ${LINK_TRDST_TRDST} + + # Also test 1 and 2 (below the default) and 5 (above the default) + for nprobes in 1 2 5; do + atf_check -s exit:0 -e ignore \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR} \(${LINK_TRSRC_TRRTR}\)( [0-9.]+ ms){$nprobes}$" \ + jexec trsrc traceroute -q$nprobes -w1 -m1 ${LINK_TRDST_TRDST} + done +} + +ipv4_nprobes_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_baseport +# + +atf_test_case "ipv4_baseport" "cleanup" +ipv4_baseport_head() +{ + atf_set descr "IPv4 traceroute with non-default base port" + atf_set require.user root +} + +ipv4_baseport_body() +{ + setup_network + + start_tcpdump + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 ${LINK_TRDST_TRDST}" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS -p 40000 \ + ${LINK_TRDST_TRDST} + + stop_tcpdump + + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto UDP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.40001: UDP" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto UDP.*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.40002: UDP" \ + cat tcpdump.output +} + +ipv4_baseport_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_gre +# + +atf_test_case "ipv4_gre" "cleanup" +ipv4_gre_head() +{ + atf_set descr "IPv4 GRE traceroute" + atf_set require.user root +} + +ipv4_gre_body() +{ + setup_network + + start_tcpdump + + # We expect the second hop to be a failure since the remote host will + # ignore the GRE packet. + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 \\*" \ + jexec trsrc traceroute $TR_FLAGS -Pgre ${LINK_TRDST_TRDST} + + stop_tcpdump + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto GRE .*\\).* ${LINK_TRSRC_TRSRC} > ${LINK_TRDST_TRDST}: GREv1" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto GRE .*\\).* ${LINK_TRSRC_TRSRC} > ${LINK_TRDST_TRDST}: GREv1" \ + cat tcpdump.output +} + +ipv4_gre_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_udplite +# + +atf_test_case "ipv4_udplite" "cleanup" +ipv4_udplite_head() +{ + atf_set descr "IPv4 UDP-Lite traceroute" + atf_set require.user root +} + +ipv4_udplite_body() +{ + setup_network + + start_tcpdump + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 ${LINK_TRDST_TRDST}" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS -Pudplite ${LINK_TRDST_TRDST} + + stop_tcpdump + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto unknown \(136\), .*\\).* ${LINK_TRSRC_TRSRC} > ${LINK_TRDST_TRDST}: ip-proto-136" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto unknown \(136\), .*\\).* ${LINK_TRSRC_TRSRC} > ${LINK_TRDST_TRDST}: ip-proto-136" \ + cat tcpdump.output +} + +ipv4_udplite_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_iptos +# + +atf_test_case "ipv4_iptos" "cleanup" +ipv4_iptos_head() +{ + atf_set descr "IPv4 traceroute with explicit ToS" + atf_set require.user root +} + +ipv4_iptos_body() +{ + setup_network + + start_tcpdump + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 ${LINK_TRDST_TRDST}" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS -t 4 ${LINK_TRDST_TRDST} + + stop_tcpdump + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x4, ttl 1, .*, proto UDP .*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33435: UDP" \ + -o match:"IP \\(tos 0x4, ttl 2, .*, proto UDP .*\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRDST_TRDST}.33436: UDP" \ + cat tcpdump.output +} + +ipv4_iptos_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_srcroute +# + +atf_test_case "ipv4_srcroute" "cleanup" +ipv4_srcroute_head() +{ + atf_set descr "IPv4 traceroute with explicit source routing" + atf_set require.user root +} + +ipv4_srcroute_body() +{ + setup_network + jexec trsrc sysctl net.inet.ip.sourceroute=1 + jexec trsrc sysctl net.inet.ip.accept_sourceroute=1 + jexec trrtr sysctl net.inet.ip.sourceroute=1 + + start_tcpdump + + # As we don't enable source routing on trdst, we should get an ICMP + # source routing failed error (!S). + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 2 ${LINK_TRDST_TRDST} [0-9.]+ ms !S" \ + -o not-match:"^ 3" \ + jexec trsrc traceroute $TR_FLAGS \ + -g ${LINK_TRSRC_TRRTR} ${LINK_TRDST_TRDST} + + stop_tcpdump + atf_check -s exit:0 -e ignore \ + -o match:"IP \\(tos 0x0, ttl 1, .*, proto UDP .*, options \\(NOP,LSRR ${LINK_TRDST_TRDST}\\)\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRSRC_TRRTR}.33435: UDP" \ + -o match:"IP \\(tos 0x0, ttl 2, .*, proto UDP .*, options \\(NOP,LSRR ${LINK_TRDST_TRDST}\\)\\).* ${LINK_TRSRC_TRSRC}.[0-9]+ > ${LINK_TRSRC_TRRTR}.33436: UDP" \ + cat tcpdump.output +} + +ipv4_srcroute_cleanup() +{ + vnet_cleanup +} + +## +# test: ipv4_dontroute +# + +atf_test_case "ipv4_dontroute" "cleanup" +ipv4_dontroute_head() +{ + atf_set descr "IPv4 traceroute with -r" + atf_set require.user root +} + +ipv4_dontroute_body() +{ + setup_network + + # This one should work as trrtr is directly connected. + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRSRC_TRRTR}" \ + -o match:"^ 1 ${LINK_TRSRC_TRRTR} [0-9.]+ ms$" \ + -o not-match:"^ 2" \ + jexec trsrc traceroute -r $TR_FLAGS ${LINK_TRSRC_TRRTR} + + # This one should fail. + + atf_check -s exit:0 \ + -e match:"^traceroute to ${LINK_TRDST_TRDST}" \ + -o match:"^ 1 traceroute: wrote ${LINK_TRDST_TRDST} 40 chars, ret=-1" \ + jexec trsrc traceroute -r $TR_FLAGS ${LINK_TRDST_TRDST} +} + +ipv4_dontroute_cleanup() +{ + vnet_cleanup +} + +## +# test case declarations + +atf_init_test_cases() +{ + atf_add_test_case ipv4_basic + atf_add_test_case ipv4_udp + atf_add_test_case ipv4_icmp + atf_add_test_case ipv4_tcp + atf_add_test_case ipv4_sctp + atf_add_test_case ipv4_gre + atf_add_test_case ipv4_udplite + atf_add_test_case ipv4_srcaddr + atf_add_test_case ipv4_srcinterface + atf_add_test_case ipv4_maxhops + atf_add_test_case ipv4_unreachable + atf_add_test_case ipv4_hugepacket + atf_add_test_case ipv4_firsthop + atf_add_test_case ipv4_nprobes + atf_add_test_case ipv4_baseport + atf_add_test_case ipv4_iptos + atf_add_test_case ipv4_srcroute + atf_add_test_case ipv4_dontroute +}