aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/traceroute
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/traceroute')
-rw-r--r--usr.sbin/traceroute/Makefile33
-rw-r--r--usr.sbin/traceroute/Makefile.depend18
-rw-r--r--usr.sbin/traceroute/Makefile.depend.options7
-rw-r--r--usr.sbin/traceroute/as.c221
-rw-r--r--usr.sbin/traceroute/as.h34
-rw-r--r--usr.sbin/traceroute/findsaddr-socket.c184
-rw-r--r--usr.sbin/traceroute/findsaddr-udp.c94
-rw-r--r--usr.sbin/traceroute/findsaddr.h23
-rw-r--r--usr.sbin/traceroute/ifaddrlist.c160
-rw-r--r--usr.sbin/traceroute/ifaddrlist.h29
-rw-r--r--usr.sbin/traceroute/tests/Makefile7
-rwxr-xr-xusr.sbin/traceroute/tests/traceroute_test.sh874
-rw-r--r--usr.sbin/traceroute/traceroute.8458
-rw-r--r--usr.sbin/traceroute/traceroute.c2078
-rw-r--r--usr.sbin/traceroute/traceroute.h26
15 files changed, 4246 insertions, 0 deletions
diff --git a/usr.sbin/traceroute/Makefile b/usr.sbin/traceroute/Makefile
new file mode 100644
index 000000000000..c52bd52abb1d
--- /dev/null
+++ b/usr.sbin/traceroute/Makefile
@@ -0,0 +1,33 @@
+.include <src.opts.mk>
+
+PACKAGE= runtime
+PROG= traceroute
+MAN= traceroute.8
+SRCS= as.c traceroute.c ifaddrlist.c findsaddr-udp.c
+BINOWN= root
+BINMODE=4555
+
+HAS_TESTS=
+SUBDIR.${MK_TESTS}+= tests
+
+.if !defined(TRACEROUTE_NO_IPSEC)
+CFLAGS+= -DIPSEC
+.endif
+# RTT Jitter on the internet these days means printing 3 decimal places on
+# > 1000ms times is plain useless. Uncomment this to enable variable precision
+# reporting, ie: print a variable precision from 0.001ms through 1000ms
+# CFLAGS+= -DSANE_PRECISION
+
+.if !defined(TRACEROUTE_NO_IPSEC)
+LIBADD+= ipsec
+.endif
+
+.if ${MK_CASPER} != "no"
+LIBADD+= casper
+LIBADD+= cap_dns
+CFLAGS+=-DWITH_CASPER
+.endif
+
+WARNS?= 3
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/traceroute/Makefile.depend b/usr.sbin/traceroute/Makefile.depend
new file mode 100644
index 000000000000..8be8495a06ee
--- /dev/null
+++ b/usr.sbin/traceroute/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcapsicum \
+ lib/libcompiler_rt \
+ lib/libipsec \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.sbin/traceroute/Makefile.depend.options b/usr.sbin/traceroute/Makefile.depend.options
new file mode 100644
index 000000000000..5d5af2276e30
--- /dev/null
+++ b/usr.sbin/traceroute/Makefile.depend.options
@@ -0,0 +1,7 @@
+# This file is not autogenerated - take care!
+
+DIRDEPS_OPTIONS= CASPER
+
+DIRDEPS.CASPER.yes= lib/libcasper/services/cap_dns
+
+.include <dirdeps-options.mk>
diff --git a/usr.sbin/traceroute/as.c b/usr.sbin/traceroute/as.c
new file mode 100644
index 000000000000..7fb952ed3da1
--- /dev/null
+++ b/usr.sbin/traceroute/as.c
@@ -0,0 +1,221 @@
+/* $NetBSD: as.c,v 1.1 2001/11/04 23:14:36 atatat Exp $ */
+
+/*
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Andrew Brown.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <err.h>
+#include <stdio.h>
+
+#include "as.h"
+
+#define DEFAULT_AS_SERVER "whois.radb.net"
+#undef AS_DEBUG_FILE
+
+struct aslookup {
+ FILE *as_f;
+#ifdef AS_DEBUG_FILE
+ FILE *as_debug;
+#endif /* AS_DEBUG_FILE */
+};
+
+void *
+as_setup(const char *server)
+{
+ struct aslookup *asn;
+ struct addrinfo hints, *res0, *res;
+ FILE *f;
+ int s, error;
+
+ s = -1;
+ if (server == NULL)
+ server = getenv("RA_SERVER");
+ if (server == NULL)
+ server = DEFAULT_AS_SERVER;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(server, "whois", &hints, &res0);
+ if (error == EAI_SERVICE) {
+ warnx("warning: whois/tcp service not found");
+ error = getaddrinfo(server, "43", &hints, &res0);
+ }
+ if (error != 0) {
+ warnx("%s: %s", server, gai_strerror(error));
+ return (NULL);
+ }
+
+ for (res = res0; res; res = res->ai_next) {
+ s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s < 0)
+ continue;
+ if (connect(s, res->ai_addr, res->ai_addrlen) >= 0)
+ break;
+ close(s);
+ s = -1;
+ }
+ freeaddrinfo(res0);
+ if (s < 0) {
+ warn("connect");
+ return (NULL);
+ }
+
+ f = fdopen(s, "r+");
+ (void)fprintf(f, "!!\n");
+ (void)fflush(f);
+
+ asn = malloc(sizeof(struct aslookup));
+ if (asn == NULL)
+ (void)fclose(f);
+ else
+ asn->as_f = f;
+
+#ifdef AS_DEBUG_FILE
+ asn->as_debug = fopen(AS_DEBUG_FILE, "w");
+ if (asn->as_debug) {
+ (void)fprintf(asn->as_debug, ">> !!\n");
+ (void)fflush(asn->as_debug);
+ }
+#endif /* AS_DEBUG_FILE */
+
+ return (asn);
+}
+
+unsigned int
+as_lookup(void *_asn, char *addr, sa_family_t family)
+{
+ struct aslookup *asn = _asn;
+ char buf[1024];
+ unsigned int as;
+ int rc, dlen, plen;
+
+ as = 0;
+ rc = dlen = 0;
+ plen = (family == AF_INET6) ? 128 : 32;
+ (void)fprintf(asn->as_f, "!r%s/%d,l\n", addr, plen);
+ (void)fflush(asn->as_f);
+
+#ifdef AS_DEBUG_FILE
+ if (asn->as_debug) {
+ (void)fprintf(asn->as_debug, ">> !r%s/%d,l\n", addr, plen);
+ (void)fflush(asn->as_debug);
+ }
+#endif /* AS_DEBUG_FILE */
+
+ while (fgets(buf, sizeof(buf), asn->as_f) != NULL) {
+ buf[sizeof(buf) - 1] = '\0';
+
+#ifdef AS_DEBUG_FILE
+ if (asn->as_debug) {
+ (void)fprintf(asn->as_debug, "<< %s", buf);
+ (void)fflush(asn->as_debug);
+ }
+#endif /* AS_DEBUG_FILE */
+
+ if (rc == 0) {
+ rc = buf[0];
+ switch (rc) {
+ case 'A':
+ /* A - followed by # bytes of answer */
+ sscanf(buf, "A%d\n", &dlen);
+#ifdef AS_DEBUG_FILE
+ if (asn->as_debug) {
+ (void)fprintf(asn->as_debug,
+ "dlen: %d\n", dlen);
+ (void)fflush(asn->as_debug);
+ }
+#endif /* AS_DEBUG_FILE */
+ break;
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ /* C - no data returned */
+ /* D - key not found */
+ /* E - multiple copies of key */
+ /* F - some other error */
+ break;
+ }
+ if (rc == 'A')
+ /* skip to next input line */
+ continue;
+ }
+
+ if (dlen == 0)
+ /* out of data, next char read is end code */
+ rc = buf[0];
+ if (rc != 'A')
+ /* either an error off the bat, or a done code */
+ break;
+
+ /* data received, thank you */
+ dlen -= strlen(buf);
+
+ /* origin line is the interesting bit */
+ if (as == 0 && strncasecmp(buf, "origin:", 7) == 0) {
+ sscanf(buf + 7, " AS%u", &as);
+#ifdef AS_DEBUG_FILE
+ if (asn->as_debug) {
+ (void)fprintf(asn->as_debug, "as: %d\n", as);
+ (void)fflush(asn->as_debug);
+ }
+#endif /* AS_DEBUG_FILE */
+ }
+ }
+
+ return (as);
+}
+
+void
+as_shutdown(void *_asn)
+{
+ struct aslookup *asn = _asn;
+
+ (void)fprintf(asn->as_f, "!q\n");
+ (void)fclose(asn->as_f);
+
+#ifdef AS_DEBUG_FILE
+ if (asn->as_debug) {
+ (void)fprintf(asn->as_debug, ">> !q\n");
+ (void)fclose(asn->as_debug);
+ }
+#endif /* AS_DEBUG_FILE */
+
+ free(asn);
+}
diff --git a/usr.sbin/traceroute/as.h b/usr.sbin/traceroute/as.h
new file mode 100644
index 000000000000..f5a5dae2acec
--- /dev/null
+++ b/usr.sbin/traceroute/as.h
@@ -0,0 +1,34 @@
+/* $NetBSD: as.h,v 1.1 2001/11/04 23:14:36 atatat Exp $ */
+
+/*
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Andrew Brown.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+void *as_setup(const char *);
+unsigned int as_lookup(void *, char *, sa_family_t);
+void as_shutdown(void *);
diff --git a/usr.sbin/traceroute/findsaddr-socket.c b/usr.sbin/traceroute/findsaddr-socket.c
new file mode 100644
index 000000000000..48b0289f8e06
--- /dev/null
+++ b/usr.sbin/traceroute/findsaddr-socket.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* XXX Yes this is WAY too complicated */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/time.h> /* concession to AIX */
+
+#if __STDC__
+struct mbuf;
+struct rtentry;
+#endif
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "gnuc.h"
+#include "os-proto.h"
+
+#include "findsaddr.h"
+
+#define SALEN(sa) ((sa)->sa_len)
+
+#ifndef roundup
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */
+#endif
+
+struct rtmsg {
+ struct rt_msghdr rtmsg;
+ u_char data[512];
+};
+
+static struct rtmsg rtmsg = {
+ { 0, RTM_VERSION, RTM_GET, 0,
+ RTF_UP | RTF_GATEWAY | RTF_HOST | RTF_STATIC,
+ RTA_DST | RTA_IFA, 0, 0, 0, 0, 0, { 0 } },
+ { 0 }
+};
+
+/*
+ * Return the source address for the given destination address
+ */
+const char *
+findsaddr(register const struct sockaddr_in *to,
+ register struct sockaddr_in *from)
+{
+ register struct rt_msghdr *rp;
+ register u_char *cp;
+
+ register struct sockaddr_in *sp, *ifa;
+ register struct sockaddr *sa;
+ register int s, size, cc, seq, i;
+ register pid_t pid;
+ static char errbuf[512];
+
+ s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
+ if (s < 0) {
+ sprintf(errbuf, "socket: %.128s", strerror(errno));
+ return (errbuf);
+ }
+
+ seq = 0;
+ pid = getpid();
+
+ rp = &rtmsg.rtmsg;
+ rp->rtm_seq = ++seq;
+ cp = (u_char *)(rp + 1);
+
+ sp = (struct sockaddr_in *)cp;
+ *sp = *to;
+ cp += roundup(SALEN((struct sockaddr *)sp), sizeof(u_int32_t));
+
+ size = cp - (u_char *)rp;
+ rp->rtm_msglen = size;
+
+ cc = write(s, (char *)rp, size);
+ if (cc < 0) {
+ sprintf(errbuf, "write: %.128s", strerror(errno));
+ close(s);
+ return (errbuf);
+ }
+ if (cc != size) {
+ sprintf(errbuf, "short write (%d != %d)", cc, size);
+ close(s);
+ return (errbuf);
+ }
+
+ size = sizeof(rtmsg);
+ do {
+ memset(rp, 0, size);
+ cc = read(s, (char *)rp, size);
+ if (cc < 0) {
+ sprintf(errbuf, "read: %.128s", strerror(errno));
+ close(s);
+ return (errbuf);
+ }
+
+ } while (rp->rtm_type != RTM_GET || rp->rtm_seq != seq ||
+ rp->rtm_pid != pid);
+ close(s);
+
+
+ if (rp->rtm_version != RTM_VERSION) {
+ sprintf(errbuf, "bad version %d", rp->rtm_version);
+ return (errbuf);
+ }
+ if (rp->rtm_msglen > cc) {
+ sprintf(errbuf, "bad msglen %d > %d", rp->rtm_msglen, cc);
+ return (errbuf);
+ }
+ if (rp->rtm_errno != 0) {
+ sprintf(errbuf, "rtm_errno: %.128s", strerror(rp->rtm_errno));
+ return (errbuf);
+ }
+
+ /* Find the interface sockaddr */
+ cp = (u_char *)(rp + 1);
+ for (i = 1; i != 0; i <<= 1)
+ if ((i & rp->rtm_addrs) != 0) {
+ sa = (struct sockaddr *)cp;
+ switch (i) {
+
+ case RTA_IFA:
+ if (sa->sa_family == AF_INET) {
+ ifa = (struct sockaddr_in *)cp;
+ if (ifa->sin_addr.s_addr != 0) {
+ *from = *ifa;
+ return (NULL);
+ }
+ }
+ break;
+
+ }
+
+ if (SALEN(sa) == 0)
+ cp += sizeof(long);
+ else
+ cp += roundup(SALEN(sa), sizeof(long));
+ }
+
+ return ("failed!");
+}
diff --git a/usr.sbin/traceroute/findsaddr-udp.c b/usr.sbin/traceroute/findsaddr-udp.c
new file mode 100644
index 000000000000..be193290afde
--- /dev/null
+++ b/usr.sbin/traceroute/findsaddr-udp.c
@@ -0,0 +1,94 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2010 Bjoern A. Zeeb <bz@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include "findsaddr.h"
+#include "traceroute.h"
+
+/*
+ * Return the source address for the given destination address.
+ *
+ * This makes use of proper source address selection in the FreeBSD kernel
+ * even taking jails into account (sys/netinet/in_pcb.c:in_pcbladdr()).
+ * We open a UDP socket, and connect to the destination, letting the kernel
+ * do the bind and then read the source IPv4 address using getsockname(2).
+ * This has multiple advantages: no need to do PF_ROUTE operations possibly
+ * needing special privileges, jails properly taken into account and most
+ * important - getting the result the kernel would give us rather than
+ * best-guessing ourselves.
+ */
+const char *
+findsaddr(register const struct sockaddr_in *to,
+ register struct sockaddr_in *from)
+{
+ const char *errstr;
+ struct sockaddr_in cto, cfrom;
+ int s;
+ socklen_t len;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s == -1)
+ return ("failed to open DGRAM socket for src addr selection.");
+
+ errstr = NULL;
+ len = sizeof(struct sockaddr_in);
+ memcpy(&cto, to, len);
+ cto.sin_port = htons(65535); /* Dummy port for connect(2). */
+ if (connect(s, (struct sockaddr *)&cto, len) == -1) {
+ errstr = "failed to connect to peer for src addr selection.";
+ goto err;
+ }
+
+ if (getsockname(s, (struct sockaddr *)&cfrom, &len) == -1) {
+ errstr = "failed to get socket name for src addr selection.";
+ goto err;
+ }
+
+ if (len != sizeof(struct sockaddr_in) || cfrom.sin_family != AF_INET) {
+ errstr = "unexpected address family in src addr selection.";
+ goto err;
+ }
+
+ /* Update source address for traceroute. */
+ setsin(from, cfrom.sin_addr.s_addr);
+
+err:
+ (void) close(s);
+
+ /* No error (string) to return. */
+ return (errstr);
+}
+
+/* end */
diff --git a/usr.sbin/traceroute/findsaddr.h b/usr.sbin/traceroute/findsaddr.h
new file mode 100644
index 000000000000..c63d8d55b342
--- /dev/null
+++ b/usr.sbin/traceroute/findsaddr.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#) $Id: findsaddr.h,v 1.1 2000/11/19 23:13:38 leres Exp $ (LBL)
+ */
+const char *findsaddr(const struct sockaddr_in *, struct sockaddr_in *);
diff --git a/usr.sbin/traceroute/ifaddrlist.c b/usr.sbin/traceroute/ifaddrlist.c
new file mode 100644
index 000000000000..e2b49292fbe7
--- /dev/null
+++ b/usr.sbin/traceroute/ifaddrlist.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/time.h> /* concession to AIX */
+
+#if __STDC__
+struct mbuf;
+struct rtentry;
+#endif
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ifaddrlist.h"
+
+/*
+ * Return the interface list
+ */
+int
+ifaddrlist(register struct ifaddrlist **ipaddrp, register char *errbuf)
+{
+ register int fd, nipaddr;
+ size_t n;
+ register struct ifreq *ifrp, *ifend, *ifnext;
+ register struct sockaddr_in *sin;
+ register struct ifaddrlist *al;
+ struct ifconf ifc;
+ struct ifreq ibuf[(32 * 1024) / sizeof(struct ifreq)], ifr;
+#define MAX_IPADDR ((int)(sizeof(ibuf) / sizeof(ibuf[0])))
+ static struct ifaddrlist ifaddrlist[MAX_IPADDR];
+ char device[sizeof(ifr.ifr_name) + 1];
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ (void)sprintf(errbuf, "socket: %s", strerror(errno));
+ return (-1);
+ }
+ ifc.ifc_len = sizeof(ibuf);
+ ifc.ifc_buf = (caddr_t)ibuf;
+
+ if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 ||
+ ifc.ifc_len < (int)sizeof(struct ifreq)) {
+ if (errno == EINVAL)
+ (void)sprintf(errbuf,
+ "SIOCGIFCONF: ifreq struct too small (%zu bytes)",
+ sizeof(ibuf));
+ else
+ (void)sprintf(errbuf, "SIOCGIFCONF: %s",
+ strerror(errno));
+ (void)close(fd);
+ return (-1);
+ }
+ ifrp = ibuf;
+ ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
+
+ al = ifaddrlist;
+ nipaddr = 0;
+ for (; ifrp < ifend; ifrp = ifnext) {
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ ifnext = ifrp + 1;
+ else
+ ifnext = (struct ifreq *)((char *)ifrp + n);
+ if (ifrp->ifr_addr.sa_family != AF_INET)
+ continue;
+ /*
+ * Need a template to preserve address info that is
+ * used below to locate the next entry. (Otherwise,
+ * SIOCGIFFLAGS stomps over it because the requests
+ * are returned in a union.)
+ */
+ strncpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name));
+ if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
+ if (errno == ENXIO)
+ continue;
+ (void)sprintf(errbuf, "SIOCGIFFLAGS: %.*s: %s",
+ (int)sizeof(ifr.ifr_name), ifr.ifr_name,
+ strerror(errno));
+ (void)close(fd);
+ return (-1);
+ }
+
+ /* Must be up */
+ if ((ifr.ifr_flags & IFF_UP) == 0)
+ continue;
+
+
+ (void)strlcpy(device, ifr.ifr_name, sizeof(device));
+#ifdef sun
+ /* Ignore sun virtual interfaces */
+ if (strchr(device, ':') != NULL)
+ continue;
+#endif
+ if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) {
+ (void)sprintf(errbuf, "SIOCGIFADDR: %s: %s",
+ device, strerror(errno));
+ (void)close(fd);
+ return (-1);
+ }
+
+ if (nipaddr >= MAX_IPADDR) {
+ (void)sprintf(errbuf, "Too many interfaces (%d)",
+ MAX_IPADDR);
+ (void)close(fd);
+ return (-1);
+ }
+ sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ al->addr = sin->sin_addr.s_addr;
+ al->device = strdup(device);
+ ++al;
+ ++nipaddr;
+ }
+ (void)close(fd);
+
+ *ipaddrp = ifaddrlist;
+ return (nipaddr);
+}
diff --git a/usr.sbin/traceroute/ifaddrlist.h b/usr.sbin/traceroute/ifaddrlist.h
new file mode 100644
index 000000000000..1352e01af4d7
--- /dev/null
+++ b/usr.sbin/traceroute/ifaddrlist.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#) $Header: traceroute.h,v 1.1 97/01/04 19:33:33 leres Locked $ (LBL)
+ */
+
+struct ifaddrlist {
+ u_int32_t addr;
+ char *device;
+};
+
+int ifaddrlist(struct ifaddrlist **, char *);
diff --git a/usr.sbin/traceroute/tests/Makefile b/usr.sbin/traceroute/tests/Makefile
new file mode 100644
index 000000000000..7c3d6f777582
--- /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 <bsd.test.mk>
diff --git a/usr.sbin/traceroute/tests/traceroute_test.sh b/usr.sbin/traceroute/tests/traceroute_test.sh
new file mode 100755
index 000000000000..268e0bd58b5b
--- /dev/null
+++ b/usr.sbin/traceroute/tests/traceroute_test.sh
@@ -0,0 +1,874 @@
+# 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}" \
+ 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}" \
+ 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}" \
+ 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
+}
diff --git a/usr.sbin/traceroute/traceroute.8 b/usr.sbin/traceroute/traceroute.8
new file mode 100644
index 000000000000..f36d473f2727
--- /dev/null
+++ b/usr.sbin/traceroute/traceroute.8
@@ -0,0 +1,458 @@
+.\"
+.\" SPDX-License-Identifier: BSD-4.3TAHOE
+.\"
+.\" Copyright (c) 1989, 1995, 1996, 1997, 1999, 2000
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that the above copyright notice and this paragraph are
+.\" duplicated in all such forms and that any documentation,
+.\" advertising materials, and other materials related to such
+.\" distribution and use acknowledge that the software was developed
+.\" by the University of California, Berkeley. The name of the
+.\" University may not be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" $Id: traceroute.8,v 1.19 2000/09/21 08:44:19 leres Exp $
+.\"
+.Dd May 14, 2025
+.Dt TRACEROUTE 8
+.Os
+.Sh NAME
+.Nm traceroute
+.Nd "print the route packets take to network host"
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl adDeEFISnrvx
+.Op Fl A Ar as_server
+.Op Fl f Ar first_ttl
+.Op Fl g Ar gateway
+.Op Fl i Ar iface
+.Op Fl m Ar max_ttl
+.Op Fl M Ar first_ttl
+.Op Fl p Ar port
+.Op Fl P Ar proto
+.Op Fl q Ar nprobes
+.Op Fl s Ar src_addr
+.Op Fl t Ar tos
+.Op Fl w Ar waittime
+.Op Fl z Ar pausemsecs
+.Ar host
+.Op Ar packetlen
+.Ek
+.Sh DESCRIPTION
+The Internet is a large and complex aggregation of network hardware, connected
+together by gateways.
+Tracking the route one's packets follow (or finding the miscreant gateway
+that's discarding your packets) can be difficult.
+.Nm
+utilizes the IP protocol `time to live' field and attempts to elicit an ICMP
+TIME_EXCEEDED response from each gateway along the path to some host.
+.Pp
+The only mandatory parameter is the destination host name or IP number.
+The default probe datagram length is 40 bytes, but this may be increased by
+specifying a packet length (in bytes) after the destination host name.
+.Pp
+Other options are:
+.Bl -tag -width Ds
+.It Fl a
+Turn on AS# lookups for each hop encountered.
+.It Fl A Ar as_server
+Turn on AS# lookups and use the given server instead of the default.
+.It Fl d
+Enable socket level debugging.
+.It Fl D
+When an ICMP response to our probe datagram is received, print the differences
+between the transmitted packet and the packet quoted by the ICMP response.
+A key showing the location of fields within the transmitted packet is printed,
+followed by the original packet in hex, followed by the quoted packet in hex.
+Bytes that are unchanged in the quoted packet are shown as underscores.
+Note, the IP checksum and the TTL of the quoted packet are not expected to
+match.
+By default, only one probe per hop is sent with this option.
+.It Fl e
+Firewall evasion mode.
+Use fixed destination ports for UDP, UDP-Lite, TCP and SCTP probes.
+The destination port does NOT increment with each packet sent.
+.It Fl E
+Detect ECN bleaching.
+Set the
+.Em IPTOS_ECN_ECT1
+Explicit Congestion Notification (ECN) bits
+.Pq Dv 01 ,
+and report if the hop has bleached
+.Pq Dv 00
+or mangled
+.Pq Dv 10
+them, or if it is experiencing congestion
+.Pq Dv 11 .
+Otherwise, report that it passed the bits appropriately.
+If
+.Fl t
+is also specified, the corresponding ECN bits will be replaced.
+.It Fl f Ar first_ttl
+Set the initial time-to-live used in the first outgoing probe packet.
+.It Fl F
+Set the "don't fragment" bit.
+.It Fl g Ar gateway
+Specify a loose source route gateway (8 maximum).
+.It Fl i Ar iface
+Specify a network interface to obtain the source IP address for outgoing probe
+packets.
+This is normally only useful on a multi-homed host.
+(See the
+.Fl s
+flag for another way to do this).
+.It Fl I
+Use ICMP ECHO instead of UDP datagrams.
+(A synonym for "-P icmp").
+.It Fl m Ar max_ttl
+Set the max time-to-live (max number of hops) used in outgoing probe packets.
+The default is the value of the
+.Va net.inet.ip.ttl
+.Xr sysctl 8
+(the same default used for TCP connections).
+.It Fl M Ar first_ttl
+Set the initial time-to-live value used in outgoing probe packets.
+The default is 1, i.e., start with the first hop.
+.It Fl n
+Print hop addresses numerically rather than symbolically and numerically
+(saves a nameserver address-to-name lookup for each gateway found on the path).
+.It Fl p Ar port
+Protocol specific.
+For UDP, UDP-Lite, TCP and SCTP, sets the base
+.Ar port
+number used in probes (default is 33434).
+Traceroute hopes that nothing is listening on UDP ports (or UDP-Lite ports
+if used by
+.Nm
+and supported by the peer)
+.Em port + 1
+to
+.Em port + (max_ttl - first_ttl + 1) * nprobes
+at the destination host (so an ICMP PORT_UNREACHABLE message will be returned
+to terminate the route tracing).
+If something is listening on a port in the default range, this option can be
+used to pick an unused port range.
+.It Fl P Ar proto
+Use packets of specified IP protocol when sending probes.
+The
+.Ar proto
+argument may be one of the following:
+.Bl -tag -width Ar udplite
+.It Ar udp
+Use
+.Xr udp 4
+packets.
+This is the default.
+.It Ar icmp
+Use
+.Xr icmp 4
+.Dq echo request
+packets.
+.It Ar udplite
+Use
+.Xr udplite 4
+packets.
+.It Ar tcp
+Use
+.Xr tcp 4
+.Dq SYN
+packets.
+This will cause a successful traceroute to end with no response (i.e., a
+.Dq *
+response) since
+.Nm
+does not know how to detect the RST or SYN+ACK response from the
+destination host.
+.It Ar sctp
+Use
+.Xr sctp 4
+packets.
+The
+.Ar packetlen
+argument must be a multiple of 4.
+SCTP probes will be constructed as SCTP
+.Dq INIT
+chunks, unless the packet length is too small, in which case the probes
+will be SCTP
+.Dq SHUTDOWN-ACK
+chunks followed by zero or one
+.Dq PAD
+chunks.
+.It Ar gre
+Use
+.Xr gre 4
+packets.
+The GRE packets will be constructed as if they contain a PPTP
+(Point-to-Point Tunneling Protocol) payload.
+.El
+.Pp
+Other protocols may also be specified, either by number or by name (see
+.Xr protocols 5 ) ,
+though
+.Nm
+does not implement any special knowledge of their packet formats.
+This option is useful for determining which router along a path may be blocking
+packets based on IP protocol number.
+But see BUGS below.
+.It Fl q Ar nprobes
+Set the number of probes per hop (default is 3, unless
+.Fl D
+is specified,
+when it is 1).
+.It Fl r
+Bypass the normal routing tables and send directly to a host on an attached
+network.
+If the host is not on a directly-attached network, an error is returned.
+This option can be used to ping a local host through an interface that has no
+route through it (e.g., after the interface was dropped by
+.Xr routed 8 .
+.It Fl s Ar src_addr
+Use the following IP address (which usually is given as an IP number, not a
+hostname) as the source address in outgoing probe packets.
+On multi-homed hosts (those with more than one IP address), this option can be
+used to force the source address to be something other than the IP address of
+the interface the probe packet is sent on.
+If the IP address is not one of this machine's interface addresses, an error is
+returned and nothing is sent.
+(See the
+.Fl i
+flag for another way to do this).
+.It Fl S
+Print a summary of how many probes were not answered for each hop.
+.It Fl t Ar tos
+Set the
+.Em type-of-service
+in probe packets to the following value (default zero).
+The value must be a decimal integer in the range 0 to 255.
+This option can be used to see if different types-of-service result in
+different paths.
+The upper six bits are the Differentiated Services Codepoint (RFC4594).
+The lower two bits are the Explicit Congestion Notification field (RFC3168).
+.It Fl v
+Verbose output.
+Received ICMP packets other than
+.Dv TIME_EXCEEDED
+and
+.Dv UNREACHABLE Ns s
+are listed.
+.It Fl w Ar waittime
+Set the time (in seconds) to wait for a response to a probe (default 5 sec.).
+.It Fl x
+Toggle ip checksums.
+Normally, this prevents traceroute from calculating ip checksums.
+In some cases, the operating system can overwrite parts of the outgoing packet
+but not recalculate the checksum (so in some cases the default is to not
+calculate checksums and using
+.Fl x
+causes them to be calculated).
+Note that checksums are usually required for the last hop when using ICMP ECHO
+probes
+.Pq Fl I .
+So they are always calculated when using ICMP.
+.It Fl z Ar pausemsecs
+Set the time (in milliseconds) to pause between probes (default 0).
+Some systems such as Solaris and routers such as Ciscos rate limit ICMP
+messages.
+A good value to use with this is 500 (e.g., 1/2 second).
+.El
+.Pp
+This program attempts to trace the route an IP packet would follow to some
+internet host by launching UDP probe packets with a small TTL (time to live)
+then listening for an ICMP "time exceeded" reply from a gateway.
+We start our probes with a TTL of one and increase by one until we get an ICMP
+"port unreachable" (which means we got to "host") or hit a max (which defaults
+to the amount of hops specified by the
+.Va net.inet.ip.ttl
+.Xr sysctl 8
+and can be changed with the
+.Fl m
+flag).
+Three probes (change with
+.Fl q
+flag) are sent at each TTL setting and a line is printed showing the TTL,
+address of the gateway and round trip time of each probe.
+If the probe answers come from different gateways, the address of each
+responding system will be printed.
+If there is no response within a 5 sec. timeout interval (changed with the
+.Fl w
+flag), a "*" is printed for that probe.
+.Pp
+We don't want the destination host to process the UDP probe packets so the
+destination port is set to an unlikely value (if some clod on the destination
+is using that value, it can be changed with the
+.Fl p
+flag).
+.Pp
+A sample use and output might be:
+.Bd -literal -offset 4n
+% traceroute nis.nsf.net.
+traceroute to nis.nsf.net (35.1.1.48), 64 hops max, 40 byte packets
+ 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms
+ 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms
+ 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms
+ 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms
+ 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms
+ 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms
+ 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms
+10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms
+11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms
+.Ed
+.Pp
+Note that lines 2 & 3 are the same.
+This is due to a buggy kernel on the 2nd hop system \- lilac-dmc.Berkeley.EDU \-
+that forwards packets with a zero TTL (a bug in the distributed version of
+4.3BSD).
+Note that you have to guess what path the packets are taking cross-country
+since the NSFNet (129.140) doesn't supply address-to-name translations for its
+NSSes.
+.Pp
+A more interesting example is:
+.Bd -literal -offset 4n
+% traceroute allspice.lcs.mit.edu.
+traceroute to allspice.lcs.mit.edu (18.26.0.115), 64 hops max, 40 byte packets
+ 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms
+ 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms
+ 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms
+ 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms
+ 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms
+ 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms
+ 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms
+ 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms
+10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms
+11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms
+12 * * *
+13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms
+14 * * *
+15 * * *
+16 * * *
+17 * * *
+18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms
+.Ed
+.Pp
+Note that the gateways 12, 14, 15, 16 & 17 hops away either don't send ICMP
+"time exceeded" messages or send them with a TTL too small to reach us.
+14 \- 17 are running the MIT C Gateway code that doesn't send "time exceeded"s.
+God only knows what's going on with 12.
+.Pp
+The silent gateway 12 in the above may be the result of a bug in the 4.[23]BSD
+network code (and its derivatives): 4.x (x <= 3) sends an unreachable message
+using whatever TTL remains in the original datagram.
+Since, for gateways, the remaining TTL is zero, the ICMP "time exceeded" is
+guaranteed to not make it back to us.
+The behavior of this bug is slightly more interesting when it appears on the
+destination system:
+.Bd -literal -offset 4n
+ 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms
+ 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms
+ 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms
+ 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms
+ 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms
+ 7 * * *
+ 8 * * *
+ 9 * * *
+10 * * *
+11 * * *
+12 * * *
+13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms !
+.Ed
+.Pp
+Notice that there are 12 "gateways" (13 is the final destination) and exactly
+the last half of them are "missing".
+What's really happening is that rip (a Sun-3 running Sun OS3.5) is using the
+TTL from our arriving datagram as the TTL in its ICMP reply.
+So, the reply will time out on the return path (with no notice sent to anyone
+since ICMP's aren't sent for ICMP's) until we probe with a TTL that's at least
+twice the path length.
+I.e., rip is really only 7 hops away.
+A reply that returns with a TTL of 1 is a clue this problem exists.
+.Nm
+prints a "!" after the time if the TTL is <= 1.
+Since vendors ship a lot of obsolete
+.Pf ( DEC Ns \'s
+Ultrix, Sun 3.x) or
+non-standard
+.Pq HP-UX
+software, expect to see this problem frequently and/or take care picking the
+target host of your probes.
+.Pp
+Other possible annotations after the time are:
+.Bl -hang -offset indent -width 12n
+.It Sy !H
+Host unreachable.
+.It Sy !N
+Network unreachable.
+.It Sy !P
+Protocol unreachable.
+.It Sy !S
+Source route failed.
+.It Sy !F\-<pmtu>
+Fragmentation needed.
+The RFC1191 Path MTU Discovery value is displayed.
+.It Sy !U
+Destination network unknown.
+.It Sy !W
+Destination host unknown.
+.It Sy !I
+Source host is isolated.
+.It Sy !A
+Communication with destination network administratively prohibited.
+.It Sy !Z
+Communication with destination host administratively prohibited.
+.It Sy !Q
+For this ToS the destination network is unreachable.
+.It Sy !T
+For this ToS the destination host is unreachable.
+.It Sy !X
+Communication administratively prohibited.
+.It Sy !V
+Host precedence violation.
+.It Sy !C
+Precedence cutoff in effect.
+.It Sy !<num>
+ICMP unreachable code <num>.
+.El
+.Pp
+These are defined by RFC1812 (which supersedes RFC1716).
+If almost all the probes result in some kind of unreachable,
+.Nm
+will give up and exit.
+.Pp
+This program is intended for use in network testing, measurement and
+management.
+It should be used primarily for manual fault isolation.
+Because of the load it could impose on the network, it is unwise to use
+.Nm
+during normal operations or from automated scripts.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr ping 8 ,
+.Xr traceroute6 8
+.Sh AUTHORS
+Implemented by
+.An Van Jacobson
+from a suggestion by Steve Deering.
+Debugged by a cast of thousands with particularly cogent suggestions or fixes
+from C. Philip Wood, Tim Seaver and Ken Adelman.
+.Sh BUGS
+When using protocols other than UDP, functionality is reduced.
+In particular, the last packet will often appear to be lost, because even
+though it reaches the destination host, there's no way to know that because no
+ICMP message is sent back.
+In the TCP case,
+.Nm
+should listen for a RST from the destination host (or an intermediate router
+that's filtering packets), but this is not implemented yet.
+.Pp
+The AS number capability reports information that may sometimes be inaccurate
+due to discrepancies between the contents of the routing database server and
+the current state of the Internet.
diff --git a/usr.sbin/traceroute/traceroute.c b/usr.sbin/traceroute/traceroute.c
new file mode 100644
index 000000000000..1297d3402ebd
--- /dev/null
+++ b/usr.sbin/traceroute/traceroute.c
@@ -0,0 +1,2078 @@
+/*
+ * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * traceroute host - trace the route ip packets follow going to "host".
+ *
+ * Attempt to trace the route an ip packet would follow to some
+ * internet host. We find out intermediate hops by launching probe
+ * packets with a small ttl (time to live) then listening for an
+ * icmp "time exceeded" reply from a gateway. We start our probes
+ * with a ttl of one and increase by one until we get an icmp "port
+ * unreachable" (which means we got to "host") or hit a max (which
+ * defaults to net.inet.ip.ttl hops & can be changed with the -m flag).
+ * Three probes (change with -q flag) are sent at each ttl setting and
+ * a line is printed showing the ttl, address of the gateway and
+ * round trip time of each probe. If the probe answers come from
+ * different gateways, the address of each responding system will
+ * be printed. If there is no response within a 5 sec. timeout
+ * interval (changed with the -w flag), a "*" is printed for that
+ * probe.
+ *
+ * Probe packets are UDP format. We don't want the destination
+ * host to process them so the destination port is set to an
+ * unlikely value (if some clod on the destination is using that
+ * value, it can be changed with the -p flag).
+ *
+ * A sample use might be:
+ *
+ * [yak 71]% traceroute nis.nsf.net.
+ * traceroute to nis.nsf.net (35.1.1.48), 64 hops max, 40 byte packets
+ * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms
+ * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms
+ * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms
+ * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms
+ * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms
+ * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms
+ * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms
+ * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms
+ *
+ * Note that lines 2 & 3 are the same. This is due to a buggy
+ * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
+ * packets with a zero ttl.
+ *
+ * A more interesting example is:
+ *
+ * [yak 72]% traceroute allspice.lcs.mit.edu.
+ * traceroute to allspice.lcs.mit.edu (18.26.0.115), 64 hops max, 40 byte packets
+ * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms
+ * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms
+ * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms
+ * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms
+ * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms
+ * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms
+ * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms
+ * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms
+ * 12 * * *
+ * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms
+ * 14 * * *
+ * 15 * * *
+ * 16 * * *
+ * 17 * * *
+ * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms
+ *
+ * (I start to see why I'm having so much trouble with mail to
+ * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away
+ * either don't send ICMP "time exceeded" messages or send them
+ * with a ttl too small to reach us. 14 - 17 are running the
+ * MIT C Gateway code that doesn't send "time exceeded"s. God
+ * only knows what's going on with 12.
+ *
+ * The silent gateway 12 in the above may be the result of a bug in
+ * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3)
+ * sends an unreachable message using whatever ttl remains in the
+ * original datagram. Since, for gateways, the remaining ttl is
+ * zero, the icmp "time exceeded" is guaranteed to not make it back
+ * to us. The behavior of this bug is slightly more interesting
+ * when it appears on the destination system:
+ *
+ * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms
+ * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms
+ * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms
+ * 7 * * *
+ * 8 * * *
+ * 9 * * *
+ * 10 * * *
+ * 11 * * *
+ * 12 * * *
+ * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms !
+ *
+ * Notice that there are 12 "gateways" (13 is the final
+ * destination) and exactly the last half of them are "missing".
+ * What's really happening is that rip (a Sun-3 running Sun OS3.5)
+ * is using the ttl from our arriving datagram as the ttl in its
+ * icmp reply. So, the reply will time out on the return path
+ * (with no notice sent to anyone since icmp's aren't sent for
+ * icmp's) until we probe with a ttl that's at least twice the path
+ * length. I.e., rip is really only 7 hops away. A reply that
+ * returns with a ttl of 1 is a clue this problem exists.
+ * Traceroute prints a "!" after the time if the ttl is <= 1.
+ * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
+ * non-standard (HPUX) software, expect to see this problem
+ * frequently and/or take care picking the target host of your
+ * probes.
+ *
+ * Other possible annotations after the time are !H, !N, !P (got a host,
+ * network or protocol unreachable, respectively), !S or !F (source
+ * route failed or fragmentation needed -- neither of these should
+ * ever occur and the associated gateway is busted if you see one). If
+ * almost all the probes result in some kind of unreachable, traceroute
+ * will give up and exit.
+ *
+ * Notes
+ * -----
+ * This program must be run by root or be setuid. (I suggest that
+ * you *don't* make it setuid -- casual use could result in a lot
+ * of unnecessary traffic on our poor, congested nets.)
+ *
+ * This program requires a kernel mod that does not appear in any
+ * system available from Berkeley: A raw ip socket using proto
+ * IPPROTO_RAW must interpret the data sent as an ip datagram (as
+ * opposed to data to be wrapped in an ip datagram). See the README
+ * file that came with the source to this program for a description
+ * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may
+ * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
+ * MODIFIED TO RUN THIS PROGRAM.
+ *
+ * The udp port usage may appear bizarre (well, ok, it is bizarre).
+ * The problem is that an icmp message only contains 8 bytes of
+ * data from the original datagram. 8 bytes is the size of a udp
+ * header so, if we want to associate replies with the original
+ * datagram, the necessary information must be encoded into the
+ * udp header (the ip id could be used but there's no way to
+ * interlock with the kernel's assignment of ip id's and, anyway,
+ * it would have taken a lot more kernel hacking to allow this
+ * code to set the ip id). So, to allow two or more users to
+ * use traceroute simultaneously, we use this task's pid as the
+ * source port (the high bit is set to move the port number out
+ * of the "likely" range). To keep track of which probe is being
+ * replied to (so times and/or hop counts don't get confused by a
+ * reply that was delayed in transit), we increment the destination
+ * port number before each probe.
+ *
+ * Don't use this as a coding example. I was trying to find a
+ * routing problem and this code sort-of popped out after 48 hours
+ * without sleep. I was amazed it ever compiled, much less ran.
+ *
+ * I stole the idea for this program from Steve Deering. Since
+ * the first release, I've learned that had I attended the right
+ * IETF working group meetings, I also could have stolen it from Guy
+ * Almes or Matt Mathis. I don't know (or care) who came up with
+ * the idea first. I envy the originators' perspicacity and I'm
+ * glad they didn't keep the idea a secret.
+ *
+ * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
+ * enhancements to the original distribution.
+ *
+ * I've hacked up a round-trip-route version of this that works by
+ * sending a loose-source-routed udp datagram through the destination
+ * back to yourself. Unfortunately, SO many gateways botch source
+ * routing, the thing is almost worthless. Maybe one day...
+ *
+ * -- Van Jacobson (van@ee.lbl.gov)
+ * Tue Dec 20 03:50:13 PST 1988
+ */
+
+#include <sys/param.h>
+#include <sys/capsicum.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/sctp.h>
+#include <netinet/sctp_header.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <netinet/tcpip.h>
+
+#include <arpa/inet.h>
+
+#ifdef WITH_CASPER
+#include <libcasper.h>
+#include <casper/cap_dns.h>
+#endif
+
+#ifdef IPSEC
+#include <net/route.h>
+#include <netipsec/ipsec.h> /* XXX */
+#endif /* IPSEC */
+
+#include <ctype.h>
+#include <capsicum_helpers.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <memory.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* rfc1716 */
+#ifndef ICMP_UNREACH_FILTER_PROHIB
+#define ICMP_UNREACH_FILTER_PROHIB 13 /* admin prohibited filter */
+#endif
+#ifndef ICMP_UNREACH_HOST_PRECEDENCE
+#define ICMP_UNREACH_HOST_PRECEDENCE 14 /* host precedence violation */
+#endif
+#ifndef ICMP_UNREACH_PRECEDENCE_CUTOFF
+#define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 /* precedence cutoff */
+#endif
+
+#include "findsaddr.h"
+#include "ifaddrlist.h"
+#include "as.h"
+#include "traceroute.h"
+
+/* Maximum number of gateways (include room for one noop) */
+#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(u_int32_t)))
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#define Fprintf (void)fprintf
+#define Printf (void)printf
+
+/* What a GRE packet header looks like */
+struct grehdr {
+ u_int16_t flags;
+ u_int16_t proto;
+ u_int16_t length; /* PPTP version of these fields */
+ u_int16_t callId;
+};
+#ifndef IPPROTO_GRE
+#define IPPROTO_GRE 47
+#endif
+
+/* For GRE, we prepare what looks like a PPTP packet */
+#define GRE_PPTP_PROTO 0x880b
+
+/* Host name and address list */
+struct hostinfo {
+ char *name;
+ int n;
+ u_int32_t *addrs;
+};
+
+/* Data section of the probe packet */
+struct outdata {
+ u_char seq; /* sequence number of this packet */
+ u_char ttl; /* ttl packet left with */
+ struct timeval tv; /* time packet left */
+};
+
+u_char packet[512]; /* last inbound (icmp) packet */
+
+struct ip *outip; /* last output ip packet */
+u_char *outp; /* last output inner protocol packet */
+
+struct ip *hip = NULL; /* Quoted IP header */
+int hiplen = 0;
+
+/* loose source route gateway list (including room for final destination) */
+u_int32_t gwlist[NGATEWAYS + 1];
+
+int s; /* receive (icmp) socket file descriptor */
+int sndsock; /* send (udp) socket file descriptor */
+
+struct sockaddr whereto; /* Who to try to reach */
+struct sockaddr wherefrom; /* Who we are */
+int packlen; /* total length of packet */
+int protlen; /* length of protocol part of packet */
+int minpacket; /* min ip packet size */
+int maxpacket = 32 * 1024; /* max ip packet size */
+int pmtu; /* Path MTU Discovery (RFC1191) */
+u_int pausemsecs;
+
+char *prog;
+char *source;
+char *hostname;
+char *device;
+static const char devnull[] = "/dev/null";
+
+int nprobes = -1;
+int max_ttl;
+int first_ttl = 1;
+u_short ident;
+u_short port; /* protocol specific base "port" */
+
+int options; /* socket options */
+int verbose;
+int waittime = 5; /* time to wait for response (in seconds) */
+int nflag; /* print addresses numerically */
+int as_path; /* print as numbers for each hop */
+char *as_server = NULL;
+void *asn;
+#ifdef CANT_HACK_IPCKSUM
+int doipcksum = 0; /* don't calculate ip checksums by default */
+#else
+int doipcksum = 1; /* calculate ip checksums by default */
+#endif
+int optlen; /* length of ip options */
+int fixedPort = 0; /* Use fixed destination port for TCP and UDP */
+int printdiff = 0; /* Print the difference between sent and quoted */
+int ecnflag = 0; /* ECN bleaching detection flag */
+
+extern int optind;
+extern int opterr;
+extern char *optarg;
+
+#ifdef WITH_CASPER
+static cap_channel_t *capdns;
+#endif
+
+/* Forwards */
+double deltaT(struct timeval *, struct timeval *);
+void freehostinfo(struct hostinfo *);
+void getaddr(u_int32_t *, char *);
+struct hostinfo *gethostinfo(char *);
+u_short in_cksum(u_short *, int);
+u_int32_t sctp_crc32c(const void *, u_int32_t);
+char *inetname(struct in_addr);
+int main(int, char **);
+u_short p_cksum(struct ip *, u_short *, int, int);
+int packet_ok(u_char *, int, struct sockaddr_in *, int);
+char *pr_type(u_char);
+void print(u_char *, int, struct sockaddr_in *);
+#ifdef IPSEC
+int setpolicy(int so, char *policy);
+#endif
+void send_probe(int, int);
+struct outproto *setproto(char *);
+int str2val(const char *, const char *, int, int);
+void tvsub(struct timeval *, struct timeval *);
+void usage(void);
+int wait_for_reply(int, struct sockaddr_in *, const struct timeval *);
+void pkt_compare(const u_char *, int, const u_char *, int);
+
+void udp_prep(struct outdata *);
+int udp_check(const u_char *, int);
+void udplite_prep(struct outdata *);
+int udplite_check(const u_char *, int);
+void tcp_prep(struct outdata *);
+int tcp_check(const u_char *, int);
+void sctp_prep(struct outdata *);
+int sctp_check(const u_char *, int);
+void gre_prep(struct outdata *);
+int gre_check(const u_char *, int);
+void gen_prep(struct outdata *);
+int gen_check(const u_char *, int);
+void icmp_prep(struct outdata *);
+int icmp_check(const u_char *, int);
+
+/* Descriptor structure for each outgoing protocol we support */
+struct outproto {
+ char *name; /* name of protocol */
+ const char *key; /* An ascii key for the bytes of the header */
+ u_char num; /* IP protocol number */
+ u_short hdrlen; /* max size of protocol header */
+ u_short port; /* default base protocol-specific "port" */
+ void (*prepare)(struct outdata *);
+ /* finish preparing an outgoing packet */
+ int (*check)(const u_char *, int);
+ /* check an incoming packet */
+};
+
+/* List of supported protocols. The first one is the default. The last
+ one is the handler for generic protocols not explicitly listed. */
+struct outproto protos[] = {
+ {
+ "udp",
+ "spt dpt len sum",
+ IPPROTO_UDP,
+ sizeof(struct udphdr),
+ 32768 + 666,
+ udp_prep,
+ udp_check
+ },
+ {
+ "udplite",
+ "spt dpt cov sum",
+ IPPROTO_UDPLITE,
+ sizeof(struct udphdr),
+ 32768 + 666,
+ udplite_prep,
+ udplite_check
+ },
+ {
+ "tcp",
+ "spt dpt seq ack xxflwin sum urp",
+ IPPROTO_TCP,
+ sizeof(struct tcphdr),
+ 32768 + 666,
+ tcp_prep,
+ tcp_check
+ },
+ {
+ "sctp",
+ "spt dpt vtag crc tyfllen tyfllen ",
+ IPPROTO_SCTP,
+ sizeof(struct sctphdr),
+ 32768 + 666,
+ sctp_prep,
+ sctp_check
+ },
+ {
+ "gre",
+ "flg pro len clid",
+ IPPROTO_GRE,
+ sizeof(struct grehdr),
+ GRE_PPTP_PROTO,
+ gre_prep,
+ gre_check
+ },
+ {
+ "icmp",
+ "typ cod sum ",
+ IPPROTO_ICMP,
+ sizeof(struct icmp),
+ 0,
+ icmp_prep,
+ icmp_check
+ },
+ {
+ NULL,
+ "",
+ 0,
+ 2 * sizeof(u_short),
+ 0,
+ gen_prep,
+ gen_check
+ },
+};
+struct outproto *proto = &protos[0];
+
+const char *ip_hdr_key = "vhtslen id off tlprsum srcip dstip opts";
+
+int
+main(int argc, char **argv)
+{
+ register int op, code, n;
+ register char *cp;
+ register const char *err;
+ register u_int32_t *ap;
+ register struct sockaddr_in *from = (struct sockaddr_in *)&wherefrom;
+ register struct sockaddr_in *to = (struct sockaddr_in *)&whereto;
+ register struct hostinfo *hi;
+ int on = 1;
+ register struct protoent *pe;
+ register int ttl, probe, i;
+ register int seq = 0;
+ int tos = 0, settos = 0;
+ register int lsrr = 0;
+ register u_short off = 0;
+ struct ifaddrlist *al;
+ char errbuf[132];
+ int requestPort = -1;
+ int sump = 0;
+ int sockerrno;
+#ifdef WITH_CASPER
+ const char *types[] = { "NAME2ADDR", "ADDR2NAME" };
+ int families[1];
+ cap_channel_t *casper;
+#endif
+ cap_rights_t rights;
+ bool cansandbox;
+
+ /* Insure the socket fds won't be 0, 1 or 2 */
+ if (open(devnull, O_RDONLY) < 0 ||
+ open(devnull, O_RDONLY) < 0 ||
+ open(devnull, O_RDONLY) < 0) {
+ Fprintf(stderr, "%s: open \"%s\": %s\n",
+ prog, devnull, strerror(errno));
+ exit(1);
+ }
+ /*
+ * Do the setuid-required stuff first, then lose privileges ASAP.
+ * Do error checking for these two calls where they appeared in
+ * the original code.
+ */
+ cp = "icmp";
+ pe = getprotobyname(cp);
+ if (pe) {
+ if ((s = socket(AF_INET, SOCK_RAW, pe->p_proto)) < 0)
+ sockerrno = errno;
+ else if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
+ sockerrno = errno;
+ }
+
+ if (setuid(getuid()) != 0) {
+ perror("setuid()");
+ exit(1);
+ }
+
+#ifdef WITH_CASPER
+ casper = cap_init();
+ if (casper == NULL)
+ errx(1, "unable to create casper process");
+ capdns = cap_service_open(casper, "system.dns");
+ if (capdns == NULL)
+ errx(1, "unable to open system.dns service");
+ if (cap_dns_type_limit(capdns, types, 2) < 0)
+ errx(1, "unable to limit access to system.dns service");
+ families[0] = AF_INET;
+ if (cap_dns_family_limit(capdns, families, 1) < 0)
+ errx(1, "unable to limit access to system.dns service");
+#endif /* WITH_CASPER */
+
+#ifdef IPCTL_DEFTTL
+ {
+ int mib[4] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_DEFTTL };
+ size_t sz = sizeof(max_ttl);
+
+ if (sysctl(mib, 4, &max_ttl, &sz, NULL, 0) == -1) {
+ perror("sysctl(net.inet.ip.ttl)");
+ exit(1);
+ }
+ }
+#else /* !IPCTL_DEFTTL */
+ max_ttl = 30;
+#endif
+
+#ifdef WITH_CASPER
+ cap_close(casper);
+#endif
+
+ if (argv[0] == NULL)
+ prog = "traceroute";
+ else if ((cp = strrchr(argv[0], '/')) != NULL)
+ prog = cp + 1;
+ else
+ prog = argv[0];
+
+ opterr = 0;
+ while ((op = getopt(argc, argv, "aA:eEdDFInrSvxf:g:i:M:m:P:p:q:s:t:w:z:")) != EOF)
+ switch (op) {
+ case 'a':
+ as_path = 1;
+ break;
+
+ case 'A':
+ as_path = 1;
+ as_server = optarg;
+ break;
+
+ case 'd':
+ options |= SO_DEBUG;
+ break;
+
+ case 'D':
+ printdiff = 1;
+ break;
+
+ case 'e':
+ fixedPort = 1;
+ break;
+
+ case 'E':
+ ecnflag = 1;
+ break;
+
+ case 'f':
+ case 'M': /* FreeBSD compat. */
+ first_ttl = str2val(optarg, "first ttl", 1, 255);
+ break;
+
+ case 'F':
+ off = IP_DF;
+ break;
+
+ case 'g':
+ if (lsrr >= NGATEWAYS) {
+ Fprintf(stderr,
+ "%s: No more than %d gateways\n",
+ prog, NGATEWAYS);
+ exit(1);
+ }
+ getaddr(gwlist + lsrr, optarg);
+ ++lsrr;
+ break;
+
+ case 'i':
+ device = optarg;
+ break;
+
+ case 'I':
+ proto = setproto("icmp");
+ break;
+
+ case 'm':
+ max_ttl = str2val(optarg, "max ttl", 1, 255);
+ break;
+
+ case 'n':
+ ++nflag;
+ break;
+
+ case 'P':
+ proto = setproto(optarg);
+ break;
+
+ case 'p':
+ requestPort = (u_short)str2val(optarg, "port",
+ 1, (1 << 16) - 1);
+ break;
+
+ case 'q':
+ nprobes = str2val(optarg, "nprobes", 1, -1);
+ break;
+
+ case 'r':
+ options |= SO_DONTROUTE;
+ break;
+
+ case 's':
+ /*
+ * set the ip source address of the outbound
+ * probe (e.g., on a multi-homed host).
+ */
+ source = optarg;
+ break;
+
+ case 'S':
+ sump = 1;
+ break;
+
+ case 't':
+ tos = str2val(optarg, "tos", 0, 255);
+ ++settos;
+ break;
+
+ case 'v':
+ ++verbose;
+ break;
+
+ case 'x':
+ doipcksum = (doipcksum == 0);
+ break;
+
+ case 'w':
+ waittime = str2val(optarg, "wait time",
+ 1, 24 * 60 * 60);
+ break;
+
+ case 'z':
+ pausemsecs = str2val(optarg, "pause msecs",
+ 0, 60 * 60 * 1000);
+ break;
+
+ default:
+ usage();
+ }
+
+ /* Set requested port, if any, else default for this protocol */
+ port = (requestPort != -1) ? requestPort : proto->port;
+
+ if (nprobes == -1)
+ nprobes = printdiff ? 1 : 3;
+
+ if (first_ttl > max_ttl) {
+ Fprintf(stderr,
+ "%s: first ttl (%d) may not be greater than max ttl (%d)\n",
+ prog, first_ttl, max_ttl);
+ exit(1);
+ }
+
+ if (!doipcksum)
+ Fprintf(stderr, "%s: Warning: ip checksums disabled\n", prog);
+
+ if (lsrr > 0)
+ optlen = (lsrr + 1) * sizeof(gwlist[0]);
+ minpacket = sizeof(*outip) + proto->hdrlen + optlen;
+ if (minpacket > 40)
+ packlen = minpacket;
+ else
+ packlen = 40;
+
+ /* Process destination and optional packet size */
+ switch (argc - optind) {
+
+ case 2:
+ packlen = str2val(argv[optind + 1],
+ "packet length", minpacket, maxpacket);
+ /* Fall through */
+
+ case 1:
+ hostname = argv[optind];
+ hi = gethostinfo(hostname);
+ setsin(to, hi->addrs[0]);
+ if (hi->n > 1)
+ Fprintf(stderr,
+ "%s: Warning: %s has multiple addresses; using %s\n",
+ prog, hostname, inet_ntoa(to->sin_addr));
+ hostname = hi->name;
+ hi->name = NULL;
+ freehostinfo(hi);
+ break;
+
+ default:
+ usage();
+ }
+
+ setlinebuf(stdout);
+
+ protlen = packlen - sizeof(*outip) - optlen;
+ if ((proto->num == IPPROTO_SCTP) && (packlen & 3)) {
+ Fprintf(stderr, "%s: packet length must be a multiple of 4\n",
+ prog);
+ exit(1);
+ }
+
+ outip = (struct ip *)malloc((unsigned)packlen);
+ if (outip == NULL) {
+ Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
+ exit(1);
+ }
+ memset((char *)outip, 0, packlen);
+
+ outip->ip_v = IPVERSION;
+ if (settos)
+ outip->ip_tos = tos;
+ if (ecnflag) {
+ outip->ip_tos &= ~IPTOS_ECN_MASK;
+ outip->ip_tos |= IPTOS_ECN_ECT1;
+ }
+ outip->ip_len = htons(packlen);
+ outip->ip_off = htons(off);
+ outip->ip_p = proto->num;
+ outp = (u_char *)(outip + 1);
+ if (lsrr > 0) {
+ register u_char *optlist;
+
+ optlist = outp;
+ outp += optlen;
+
+ /* final hop */
+ gwlist[lsrr] = to->sin_addr.s_addr;
+
+ outip->ip_dst.s_addr = gwlist[0];
+
+ /* force 4 byte alignment */
+ optlist[0] = IPOPT_NOP;
+ /* loose source route option */
+ optlist[1] = IPOPT_LSRR;
+ i = lsrr * sizeof(gwlist[0]);
+ optlist[2] = i + 3;
+ /* Pointer to LSRR addresses */
+ optlist[3] = IPOPT_MINOFF;
+ memcpy(optlist + 4, gwlist + 1, i);
+ } else
+ outip->ip_dst = to->sin_addr;
+
+ outip->ip_hl = (outp - (u_char *)outip) >> 2;
+ ident = (getpid() & 0xffff) | 0x8000;
+
+ if (pe == NULL) {
+ Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp);
+ exit(1);
+ }
+ if (s < 0) {
+ errno = sockerrno;
+ Fprintf(stderr, "%s: icmp socket: %s\n", prog, strerror(errno));
+ exit(1);
+ }
+ if (options & SO_DEBUG)
+ (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on,
+ sizeof(on));
+ if (options & SO_DONTROUTE)
+ (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
+ sizeof(on));
+
+#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+ if (setpolicy(s, "in bypass") < 0)
+ errx(1, "%s", ipsec_strerror());
+
+ if (setpolicy(s, "out bypass") < 0)
+ errx(1, "%s", ipsec_strerror());
+#endif /* defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) */
+
+ if (sndsock < 0) {
+ errno = sockerrno;
+ Fprintf(stderr, "%s: raw socket: %s\n", prog, strerror(errno));
+ exit(1);
+ }
+
+#ifdef SO_SNDBUF
+ if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&packlen,
+ sizeof(packlen)) < 0) {
+ Fprintf(stderr, "%s: SO_SNDBUF: %s\n", prog, strerror(errno));
+ exit(1);
+ }
+#endif
+#ifdef IP_HDRINCL
+ if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
+ sizeof(on)) < 0) {
+ Fprintf(stderr, "%s: IP_HDRINCL: %s\n", prog, strerror(errno));
+ exit(1);
+ }
+#else
+#ifdef IP_TOS
+ if (settos && setsockopt(sndsock, IPPROTO_IP, IP_TOS,
+ (char *)&tos, sizeof(tos)) < 0) {
+ Fprintf(stderr, "%s: setsockopt tos %d: %s\n",
+ prog, tos, strerror(errno));
+ exit(1);
+ }
+#endif
+#endif
+ if (options & SO_DEBUG)
+ (void)setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, (char *)&on,
+ sizeof(on));
+ if (options & SO_DONTROUTE)
+ (void)setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
+ sizeof(on));
+
+ /* Get the interface address list */
+ n = ifaddrlist(&al, errbuf);
+ if (n < 0) {
+ Fprintf(stderr, "%s: ifaddrlist: %s\n", prog, errbuf);
+ exit(1);
+ }
+ if (n == 0) {
+ Fprintf(stderr,
+ "%s: Can't find any network interfaces\n", prog);
+ exit(1);
+ }
+
+ /* Look for a specific device */
+ if (device != NULL) {
+ for (i = n; i > 0; --i, ++al)
+ if (strcmp(device, al->device) == 0)
+ break;
+ if (i <= 0) {
+ Fprintf(stderr, "%s: Can't find interface %.32s\n",
+ prog, device);
+ exit(1);
+ }
+ }
+
+ /* Determine our source address */
+ if (source == NULL) {
+ /*
+ * If a device was specified, use the interface address.
+ * Otherwise, try to determine our source address.
+ */
+ if (device != NULL)
+ setsin(from, al->addr);
+ else if ((err = findsaddr(to, from)) != NULL) {
+ Fprintf(stderr, "%s: findsaddr: %s\n",
+ prog, err);
+ exit(1);
+ }
+ } else {
+ hi = gethostinfo(source);
+ source = hi->name;
+ hi->name = NULL;
+ /*
+ * If the device was specified make sure it
+ * corresponds to the source address specified.
+ * Otherwise, use the first address (and warn if
+ * there are more than one).
+ */
+ if (device != NULL) {
+ for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap)
+ if (*ap == al->addr)
+ break;
+ if (i <= 0) {
+ Fprintf(stderr,
+ "%s: %s is not on interface %.32s\n",
+ prog, source, device);
+ exit(1);
+ }
+ setsin(from, *ap);
+ } else {
+ setsin(from, hi->addrs[0]);
+ if (hi->n > 1)
+ Fprintf(stderr,
+ "%s: Warning: %s has multiple addresses; using %s\n",
+ prog, source, inet_ntoa(from->sin_addr));
+ }
+ freehostinfo(hi);
+ }
+
+ outip->ip_src = from->sin_addr;
+
+ /* Check the source address (-s), if any, is valid */
+ if (bind(sndsock, (struct sockaddr *)from, sizeof(*from)) < 0) {
+ Fprintf(stderr, "%s: bind: %s\n",
+ prog, strerror(errno));
+ exit(1);
+ }
+
+ if (as_path) {
+ asn = as_setup(as_server);
+ if (asn == NULL) {
+ Fprintf(stderr, "%s: as_setup failed, AS# lookups"
+ " disabled\n", prog);
+ (void)fflush(stderr);
+ as_path = 0;
+ }
+ }
+
+ if (connect(sndsock, (struct sockaddr *)&whereto,
+ sizeof(whereto)) != 0) {
+ Fprintf(stderr, "%s: connect: %s\n", prog, strerror(errno));
+ exit(1);
+ }
+
+#ifdef WITH_CASPER
+ cansandbox = true;
+#else
+ if (nflag)
+ cansandbox = true;
+ else
+ cansandbox = false;
+#endif
+
+ caph_cache_catpages();
+
+ /*
+ * Here we enter capability mode. Further down access to global
+ * namespaces (e.g filesystem) is restricted (see capsicum(4)).
+ * We must connect(2) our socket before this point.
+ */
+ if (cansandbox && cap_enter() < 0) {
+ if (errno != ENOSYS) {
+ Fprintf(stderr, "%s: cap_enter: %s\n", prog,
+ strerror(errno));
+ exit(1);
+ } else {
+ cansandbox = false;
+ }
+ }
+
+ cap_rights_init(&rights, CAP_SEND, CAP_SETSOCKOPT);
+ if (cansandbox && cap_rights_limit(sndsock, &rights) < 0) {
+ Fprintf(stderr, "%s: cap_rights_limit sndsock: %s\n", prog,
+ strerror(errno));
+ exit(1);
+ }
+
+ cap_rights_init(&rights, CAP_RECV, CAP_EVENT);
+ if (cansandbox && cap_rights_limit(s, &rights) < 0) {
+ Fprintf(stderr, "%s: cap_rights_limit s: %s\n", prog,
+ strerror(errno));
+ exit(1);
+ }
+
+#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+ if (setpolicy(sndsock, "in bypass") < 0)
+ errx(1, "%s", ipsec_strerror());
+
+ if (setpolicy(sndsock, "out bypass") < 0)
+ errx(1, "%s", ipsec_strerror());
+#endif /* defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) */
+
+ Fprintf(stderr, "%s to %s (%s)",
+ prog, hostname, inet_ntoa(to->sin_addr));
+ if (source)
+ Fprintf(stderr, " from %s", source);
+ Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, packlen);
+ (void)fflush(stderr);
+
+ for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
+ u_int32_t lastaddr = 0;
+ int gotlastaddr = 0;
+ int got_there = 0;
+ int unreachable = 0;
+ int sentfirst = 0;
+ int loss;
+
+ Printf("%2d ", ttl);
+ for (probe = 0, loss = 0; probe < nprobes; ++probe) {
+ register int cc;
+ struct timeval t1, t2;
+ register struct ip *ip;
+ struct outdata outdata;
+
+ if (sentfirst && pausemsecs > 0)
+ usleep(pausemsecs * 1000);
+ /* Prepare outgoing data */
+ outdata.seq = ++seq;
+ outdata.ttl = ttl;
+
+ /* Avoid alignment problems by copying bytewise: */
+ (void)gettimeofday(&t1, NULL);
+ memcpy(&outdata.tv, &t1, sizeof(outdata.tv));
+
+ /* Finalize and send packet */
+ (*proto->prepare)(&outdata);
+ send_probe(seq, ttl);
+ ++sentfirst;
+
+ /* Wait for a reply */
+ while ((cc = wait_for_reply(s, from, &t1)) != 0) {
+ double T;
+ int precis;
+
+ (void)gettimeofday(&t2, NULL);
+ i = packet_ok(packet, cc, from, seq);
+ /* Skip short packet */
+ if (i == 0)
+ continue;
+ if (!gotlastaddr ||
+ from->sin_addr.s_addr != lastaddr) {
+ if (gotlastaddr)
+ printf("\n ");
+ print(packet, cc, from);
+ lastaddr = from->sin_addr.s_addr;
+ ++gotlastaddr;
+ }
+ T = deltaT(&t1, &t2);
+#ifdef SANE_PRECISION
+ if (T >= 1000.0)
+ precis = 0;
+ else if (T >= 100.0)
+ precis = 1;
+ else if (T >= 10.0)
+ precis = 2;
+ else
+#endif
+ precis = 3;
+ Printf(" %.*f ms", precis, T);
+ if (ecnflag) {
+ u_char ecn = hip->ip_tos & IPTOS_ECN_MASK;
+ switch (ecn) {
+ case IPTOS_ECN_ECT1:
+ Printf(" (ecn=passed)");
+ break;
+ case IPTOS_ECN_NOTECT:
+ Printf(" (ecn=bleached)");
+ break;
+ case IPTOS_ECN_CE:
+ Printf(" (ecn=congested)");
+ break;
+ default:
+ Printf(" (ecn=mangled)");
+ break;
+ }
+ }
+ if (printdiff) {
+ Printf("\n");
+ Printf("%*.*s%s\n",
+ -(outip->ip_hl << 3),
+ outip->ip_hl << 3,
+ ip_hdr_key,
+ proto->key);
+ pkt_compare((void *)outip, packlen,
+ (void *)hip, hiplen);
+ }
+ if (i == -2) {
+#ifndef ARCHAIC
+ ip = (struct ip *)packet;
+ if (ip->ip_ttl <= 1)
+ Printf(" !");
+#endif
+ ++got_there;
+ break;
+ }
+ /* time exceeded in transit */
+ if (i == -1)
+ break;
+ code = i - 1;
+ switch (code) {
+
+ case ICMP_UNREACH_PORT:
+#ifndef ARCHAIC
+ ip = (struct ip *)packet;
+ if (ip->ip_ttl <= 1)
+ Printf(" !");
+#endif
+ ++got_there;
+ break;
+
+ case ICMP_UNREACH_NET:
+ ++unreachable;
+ Printf(" !N");
+ break;
+
+ case ICMP_UNREACH_HOST:
+ ++unreachable;
+ Printf(" !H");
+ break;
+
+ case ICMP_UNREACH_PROTOCOL:
+ ++got_there;
+ Printf(" !P");
+ break;
+
+ case ICMP_UNREACH_NEEDFRAG:
+ ++unreachable;
+ Printf(" !F-%d", pmtu);
+ break;
+
+ case ICMP_UNREACH_SRCFAIL:
+ ++unreachable;
+ Printf(" !S");
+ break;
+
+ case ICMP_UNREACH_NET_UNKNOWN:
+ ++unreachable;
+ Printf(" !U");
+ break;
+
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ ++unreachable;
+ Printf(" !W");
+ break;
+
+ case ICMP_UNREACH_ISOLATED:
+ ++unreachable;
+ Printf(" !I");
+ break;
+
+ case ICMP_UNREACH_NET_PROHIB:
+ ++unreachable;
+ Printf(" !A");
+ break;
+
+ case ICMP_UNREACH_HOST_PROHIB:
+ ++unreachable;
+ Printf(" !Z");
+ break;
+
+ case ICMP_UNREACH_TOSNET:
+ ++unreachable;
+ Printf(" !Q");
+ break;
+
+ case ICMP_UNREACH_TOSHOST:
+ ++unreachable;
+ Printf(" !T");
+ break;
+
+ case ICMP_UNREACH_FILTER_PROHIB:
+ ++unreachable;
+ Printf(" !X");
+ break;
+
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ ++unreachable;
+ Printf(" !V");
+ break;
+
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ ++unreachable;
+ Printf(" !C");
+ break;
+
+ default:
+ ++unreachable;
+ Printf(" !<%d>", code);
+ break;
+ }
+ break;
+ }
+ if (cc == 0) {
+ loss++;
+ Printf(" *");
+ }
+ (void)fflush(stdout);
+ }
+ if (sump) {
+ Printf(" (%d%% loss)", (loss * 100) / nprobes);
+ }
+ putchar('\n');
+ if (got_there ||
+ (unreachable > 0 && unreachable >= nprobes - 1))
+ break;
+ }
+ if (as_path)
+ as_shutdown(asn);
+ exit(0);
+}
+
+int
+wait_for_reply(register int sock, register struct sockaddr_in *fromp,
+ register const struct timeval *tp)
+{
+ fd_set *fdsp;
+ size_t nfds;
+ struct timeval now, wait;
+ register int cc = 0;
+ register int error;
+ int fromlen = sizeof(*fromp);
+
+ nfds = howmany(sock + 1, NFDBITS);
+ if ((fdsp = malloc(nfds * sizeof(fd_mask))) == NULL)
+ err(1, "malloc");
+ memset(fdsp, 0, nfds * sizeof(fd_mask));
+ FD_SET(sock, fdsp);
+
+ wait.tv_sec = tp->tv_sec + waittime;
+ wait.tv_usec = tp->tv_usec;
+ (void)gettimeofday(&now, NULL);
+ tvsub(&wait, &now);
+ if (wait.tv_sec < 0) {
+ wait.tv_sec = 0;
+ wait.tv_usec = 1;
+ }
+
+ error = select(sock + 1, fdsp, NULL, NULL, &wait);
+ if (error == -1 && errno == EINVAL) {
+ Fprintf(stderr, "%s: botched select() args\n", prog);
+ exit(1);
+ }
+ if (error > 0)
+ cc = recvfrom(sock, (char *)packet, sizeof(packet), 0,
+ (struct sockaddr *)fromp, &fromlen);
+
+ free(fdsp);
+ return (cc);
+}
+
+void
+send_probe(int seq, int ttl)
+{
+ register int cc;
+
+ outip->ip_ttl = ttl;
+ outip->ip_id = htons(ident + seq);
+
+ /* XXX undocumented debugging hack */
+ if (verbose > 1) {
+ register const u_short *sp;
+ register int nshorts, i;
+
+ sp = (u_short *)outip;
+ nshorts = (u_int)packlen / sizeof(u_short);
+ i = 0;
+ Printf("[ %d bytes", packlen);
+ while (--nshorts >= 0) {
+ if ((i++ % 8) == 0)
+ Printf("\n\t");
+ Printf(" %04x", ntohs(*sp++));
+ }
+ if (packlen & 1) {
+ if ((i % 8) == 0)
+ Printf("\n\t");
+ Printf(" %02x", *(u_char *)sp);
+ }
+ Printf("]\n");
+ }
+
+#if !defined(IP_HDRINCL) && defined(IP_TTL)
+ if (setsockopt(sndsock, IPPROTO_IP, IP_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0) {
+ Fprintf(stderr, "%s: setsockopt ttl %d: %s\n",
+ prog, ttl, strerror(errno));
+ exit(1);
+ }
+#endif
+
+ cc = send(sndsock, (char *)outip, packlen, 0);
+ if (cc < 0 || cc != packlen) {
+ if (cc < 0)
+ Fprintf(stderr, "%s: sendto: %s\n",
+ prog, strerror(errno));
+ Printf("%s: wrote %s %d chars, ret=%d\n",
+ prog, hostname, packlen, cc);
+ (void)fflush(stdout);
+ }
+}
+
+#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+int
+setpolicy(int so, char *policy)
+{
+ char *buf;
+
+ buf = ipsec_set_policy(policy, strlen(policy));
+ if (buf == NULL) {
+ warnx("%s", ipsec_strerror());
+ return (-1);
+ }
+ (void)setsockopt(so, IPPROTO_IP, IP_IPSEC_POLICY,
+ buf, ipsec_get_policylen(buf));
+
+ free(buf);
+
+ return (0);
+}
+#endif
+
+double
+deltaT(struct timeval *t1p, struct timeval *t2p)
+{
+ register double dt;
+
+ dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
+ (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
+ return (dt);
+}
+
+/*
+ * Convert an ICMP "type" field to a printable string.
+ */
+char *
+pr_type(register u_char t)
+{
+ static char *ttab[] = {
+ "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable",
+ "Source Quench", "Redirect", "ICMP 6", "ICMP 7",
+ "Echo", "ICMP 9", "ICMP 10", "Time Exceeded",
+ "Param Problem", "Timestamp", "Timestamp Reply", "Info Request",
+ "Info Reply"
+ };
+
+ if (t > 16)
+ return ("OUT-OF-RANGE");
+
+ return (ttab[t]);
+}
+
+int
+packet_ok(register u_char *buf, int cc, register struct sockaddr_in *from,
+ register int seq)
+{
+ register struct icmp *icp;
+ register u_char type, code;
+ register int hlen;
+#ifndef ARCHAIC
+ register struct ip *ip;
+
+ ip = (struct ip *) buf;
+ hlen = ip->ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN) {
+ if (verbose)
+ Printf("packet too short (%d bytes) from %s\n", cc,
+ inet_ntoa(from->sin_addr));
+ return (0);
+ }
+ cc -= hlen;
+ icp = (struct icmp *)(buf + hlen);
+#else
+ icp = (struct icmp *)buf;
+#endif
+ type = icp->icmp_type;
+ code = icp->icmp_code;
+ /* Path MTU Discovery (RFC1191) */
+ if (code != ICMP_UNREACH_NEEDFRAG)
+ pmtu = 0;
+ else {
+ pmtu = ntohs(icp->icmp_nextmtu);
+ }
+ if (type == ICMP_ECHOREPLY
+ && proto->num == IPPROTO_ICMP
+ && (*proto->check)((u_char *)icp, (u_char)seq))
+ return (-2);
+ if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
+ type == ICMP_UNREACH) {
+ u_char *inner;
+
+ hip = &icp->icmp_ip;
+ hiplen = ((u_char *)icp + cc) - (u_char *)hip;
+ hlen = hip->ip_hl << 2;
+ inner = (u_char *)((u_char *)hip + hlen);
+ if (hlen + 16 <= cc
+ && hip->ip_p == proto->num
+ && (*proto->check)(inner, (u_char)seq))
+ return (type == ICMP_TIMXCEED ? -1 : code + 1);
+ }
+#ifndef ARCHAIC
+ if (verbose) {
+ register int i;
+ u_int32_t *lp = (u_int32_t *)&icp->icmp_ip;
+
+ Printf("\n%d bytes from %s to ", cc, inet_ntoa(from->sin_addr));
+ Printf("%s: icmp type %d (%s) code %d\n",
+ inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code);
+ for (i = 4; i <= cc - ICMP_MINLEN; i += sizeof(*lp))
+ Printf("%2d: %8.8x\n", i, ntohl(*lp++));
+ }
+#endif
+ return (0);
+}
+
+void
+icmp_prep(struct outdata *outdata)
+{
+ struct icmp *const icmpheader = (struct icmp *) outp;
+
+ icmpheader->icmp_type = ICMP_ECHO;
+ icmpheader->icmp_id = htons(ident);
+ icmpheader->icmp_seq = htons(outdata->seq);
+ icmpheader->icmp_cksum = 0;
+ icmpheader->icmp_cksum = in_cksum((u_short *)icmpheader, protlen);
+ if (icmpheader->icmp_cksum == 0)
+ icmpheader->icmp_cksum = 0xffff;
+}
+
+int
+icmp_check(const u_char *data, int seq)
+{
+ struct icmp *const icmpheader = (struct icmp *) data;
+
+ return (icmpheader->icmp_id == htons(ident)
+ && icmpheader->icmp_seq == htons(seq));
+}
+
+void
+udp_prep(struct outdata *outdata)
+{
+ struct udphdr *const outudp = (struct udphdr *) outp;
+
+ outudp->uh_sport = htons(ident + (fixedPort ? outdata->seq : 0));
+ outudp->uh_dport = htons(port + (fixedPort ? 0 : outdata->seq));
+ outudp->uh_ulen = htons((u_short)protlen);
+ outudp->uh_sum = 0;
+ if (doipcksum) {
+ u_short sum = p_cksum(outip, (u_short *)outudp, protlen, protlen);
+ outudp->uh_sum = (sum) ? sum : 0xffff;
+ }
+
+ return;
+}
+
+int
+udp_check(const u_char *data, int seq)
+{
+ struct udphdr *const udp = (struct udphdr *) data;
+
+ return (ntohs(udp->uh_sport) == ident + (fixedPort ? seq : 0) &&
+ ntohs(udp->uh_dport) == port + (fixedPort ? 0 : seq));
+}
+
+void
+udplite_prep(struct outdata *outdata)
+{
+ struct udphdr *const outudp = (struct udphdr *) outp;
+
+ outudp->uh_sport = htons(ident + (fixedPort ? outdata->seq : 0));
+ outudp->uh_dport = htons(port + (fixedPort ? 0 : outdata->seq));
+ outudp->uh_ulen = htons(8);
+ outudp->uh_sum = 0;
+ if (doipcksum) {
+ u_short sum = p_cksum(outip, (u_short *)outudp, protlen, 8);
+ outudp->uh_sum = (sum) ? sum : 0xffff;
+ }
+
+ return;
+}
+
+int
+udplite_check(const u_char *data, int seq)
+{
+ struct udphdr *const udp = (struct udphdr *) data;
+
+ return (ntohs(udp->uh_sport) == ident + (fixedPort ? seq : 0) &&
+ ntohs(udp->uh_dport) == port + (fixedPort ? 0 : seq));
+}
+
+void
+tcp_prep(struct outdata *outdata)
+{
+ struct tcphdr *const tcp = (struct tcphdr *) outp;
+
+ tcp->th_sport = htons(ident);
+ tcp->th_dport = htons(port + (fixedPort ? 0 : outdata->seq));
+ tcp->th_seq = (tcp->th_sport << 16) | tcp->th_dport;
+ tcp->th_ack = 0;
+ tcp->th_off = 5;
+ __tcp_set_flags(tcp, TH_SYN);
+ tcp->th_sum = 0;
+
+ if (doipcksum)
+ tcp->th_sum = p_cksum(outip, (u_short *)tcp, protlen, protlen);
+}
+
+int
+tcp_check(const u_char *data, int seq)
+{
+ struct tcphdr *const tcp = (struct tcphdr *) data;
+
+ return (ntohs(tcp->th_sport) == ident
+ && ntohs(tcp->th_dport) == port + (fixedPort ? 0 : seq)
+ && tcp->th_seq == (tcp_seq)((tcp->th_sport << 16) | tcp->th_dport));
+}
+
+void
+sctp_prep(struct outdata *outdata)
+{
+ struct sctphdr *const sctp = (struct sctphdr *) outp;
+ struct sctp_chunkhdr *chk;
+ struct sctp_init_chunk *init;
+ struct sctp_paramhdr *param;
+
+ sctp->src_port = htons(ident);
+ sctp->dest_port = htons(port + (fixedPort ? 0 : outdata->seq));
+ if (protlen >= (int)(sizeof(struct sctphdr) +
+ sizeof(struct sctp_init_chunk))) {
+ sctp->v_tag = 0;
+ } else {
+ sctp->v_tag = (sctp->src_port << 16) | sctp->dest_port;
+ }
+ sctp->checksum = htonl(0);
+ if (protlen >= (int)(sizeof(struct sctphdr) +
+ sizeof(struct sctp_init_chunk))) {
+ /*
+ * Send a packet containing an INIT chunk. This works
+ * better in case of firewalls on the path, but
+ * results in a probe packet containing at least
+ * 32 bytes of payload. For shorter payloads, use
+ * SHUTDOWN-ACK chunks.
+ */
+ init = (struct sctp_init_chunk *)(sctp + 1);
+ init->ch.chunk_type = SCTP_INITIATION;
+ init->ch.chunk_flags = 0;
+ init->ch.chunk_length = htons((u_int16_t)(protlen -
+ sizeof(struct sctphdr)));
+ init->init.initiate_tag = (sctp->src_port << 16) |
+ sctp->dest_port;
+ init->init.a_rwnd = htonl(1500);
+ init->init.num_outbound_streams = htons(1);
+ init->init.num_inbound_streams = htons(1);
+ init->init.initial_tsn = htonl(0);
+ if (protlen >= (int)(sizeof(struct sctphdr) +
+ sizeof(struct sctp_init_chunk) +
+ sizeof(struct sctp_paramhdr))) {
+ param = (struct sctp_paramhdr *)(init + 1);
+ param->param_type = htons(SCTP_PAD);
+ param->param_length =
+ htons((u_int16_t)(protlen -
+ sizeof(struct sctphdr) -
+ sizeof(struct sctp_init_chunk)));
+ }
+ } else {
+ /*
+ * Send a packet containing a SHUTDOWN-ACK chunk,
+ * possibly followed by a PAD chunk.
+ */
+ if (protlen >=
+ (int)(sizeof(struct sctphdr) +
+ sizeof(struct sctp_chunkhdr))) {
+ chk = (struct sctp_chunkhdr *)(sctp + 1);
+ chk->chunk_type = SCTP_SHUTDOWN_ACK;
+ chk->chunk_flags = 0;
+ chk->chunk_length = htons(4);
+ }
+ if (protlen >=
+ (int)(sizeof(struct sctphdr) +
+ 2 * sizeof(struct sctp_chunkhdr))) {
+ chk = chk + 1;
+ chk->chunk_type = SCTP_PAD_CHUNK;
+ chk->chunk_flags = 0;
+ chk->chunk_length = htons(protlen -
+ (sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr)));
+ }
+ }
+ if (doipcksum) {
+ sctp->checksum = sctp_crc32c(sctp, protlen);
+ }
+}
+
+int
+sctp_check(const u_char *data, int seq)
+{
+ struct sctphdr *const sctp = (struct sctphdr *) data;
+
+ if (ntohs(sctp->src_port) != ident ||
+ ntohs(sctp->dest_port) != port + (fixedPort ? 0 : seq))
+ return (0);
+ if (protlen < (int)(sizeof(struct sctphdr) +
+ sizeof(struct sctp_init_chunk))) {
+ return (sctp->v_tag ==
+ (u_int32_t)((sctp->src_port << 16) | sctp->dest_port));
+ } else {
+ /*
+ * Don't verify the initiate_tag, since it is not available,
+ * most of the time.
+ */
+ return (sctp->v_tag == 0);
+ }
+}
+
+void
+gre_prep(struct outdata *outdata)
+{
+ struct grehdr *const gre = (struct grehdr *) outp;
+
+ gre->flags = htons(0x2001);
+ gre->proto = htons(port);
+ gre->length = 0;
+ gre->callId = htons(ident + outdata->seq);
+}
+
+int
+gre_check(const u_char *data, int seq)
+{
+ struct grehdr *const gre = (struct grehdr *) data;
+
+ return (ntohs(gre->proto) == port
+ && ntohs(gre->callId) == ident + seq);
+}
+
+void
+gen_prep(struct outdata *outdata)
+{
+ u_int16_t *const ptr = (u_int16_t *) outp;
+
+ ptr[0] = htons(ident);
+ ptr[1] = htons(port + outdata->seq);
+}
+
+int
+gen_check(const u_char *data, int seq)
+{
+ u_int16_t *const ptr = (u_int16_t *) data;
+
+ return (ntohs(ptr[0]) == ident
+ && ntohs(ptr[1]) == port + seq);
+}
+
+void
+print(register u_char *buf, register int cc, register struct sockaddr_in *from)
+{
+ register struct ip *ip;
+ register int hlen;
+ char addr[INET_ADDRSTRLEN];
+
+ ip = (struct ip *) buf;
+ hlen = ip->ip_hl << 2;
+ cc -= hlen;
+
+ strlcpy(addr, inet_ntoa(from->sin_addr), sizeof(addr));
+
+ if (as_path)
+ Printf(" [AS%u]", as_lookup(asn, addr, AF_INET));
+
+ if (nflag)
+ Printf(" %s", addr);
+ else
+ Printf(" %s (%s)", inetname(from->sin_addr), addr);
+
+ if (verbose)
+ Printf(" %d bytes to %s", cc, inet_ntoa(ip->ip_dst));
+}
+
+/*
+ * Checksum routine for UDP and TCP headers.
+ */
+u_short
+p_cksum(struct ip *ip, u_short *data, int len, int cov)
+{
+ static struct ipovly ipo;
+ u_short sum[2];
+
+ ipo.ih_pr = ip->ip_p;
+ ipo.ih_len = htons(len);
+ ipo.ih_src = ip->ip_src;
+ ipo.ih_dst = ip->ip_dst;
+
+ sum[1] = in_cksum((u_short *)&ipo, sizeof(ipo)); /* pseudo ip hdr cksum */
+ sum[0] = in_cksum(data, cov); /* payload data cksum */
+
+ return (~in_cksum(sum, sizeof(sum)));
+}
+
+/*
+ * Checksum routine for Internet Protocol family headers (C Version)
+ */
+u_short
+in_cksum(register u_short *addr, register int len)
+{
+ register int nleft = len;
+ register u_short *w = addr;
+ register u_short answer;
+ register int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1)
+ sum += *(u_char *)w;
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
+
+/*
+ * CRC32C routine for the Stream Control Transmission Protocol
+ */
+
+#define CRC32C(c, d) (c = (c >> 8) ^ crc_c[(c ^ (d)) & 0xFF])
+
+static u_int32_t crc_c[256] = {
+ 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
+ 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
+ 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
+ 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
+ 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
+ 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
+ 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
+ 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
+ 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
+ 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
+ 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
+ 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
+ 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
+ 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
+ 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
+ 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
+ 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
+ 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
+ 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
+ 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
+ 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
+ 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
+ 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
+ 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
+ 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
+ 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
+ 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
+ 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
+ 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
+ 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
+ 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
+ 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
+ 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
+ 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
+ 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
+ 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
+ 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
+ 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
+ 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
+ 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
+ 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
+ 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
+ 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
+ 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
+ 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
+ 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
+ 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
+ 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
+ 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
+ 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
+ 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
+ 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
+ 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
+ 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
+ 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
+ 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
+ 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
+ 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
+ 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
+ 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
+ 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
+ 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
+ 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
+ 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
+};
+
+u_int32_t
+sctp_crc32c(const void *packet, u_int32_t len)
+{
+ u_int32_t i, crc32c;
+ u_int8_t byte0, byte1, byte2, byte3;
+ const u_int8_t *buf = (const u_int8_t *)packet;
+
+ crc32c = ~0;
+ for (i = 0; i < len; i++)
+ CRC32C(crc32c, buf[i]);
+ crc32c = ~crc32c;
+ byte0 = crc32c & 0xff;
+ byte1 = (crc32c >> 8) & 0xff;
+ byte2 = (crc32c >> 16) & 0xff;
+ byte3 = (crc32c >> 24) & 0xff;
+ crc32c = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);
+ return (htonl(crc32c));
+}
+
+/*
+ * Subtract 2 timeval structs: out = out - in.
+ * Out is assumed to be within about LONG_MAX seconds of in.
+ */
+void
+tvsub(register struct timeval *out, register struct timeval *in)
+{
+
+ if ((out->tv_usec -= in->tv_usec) < 0) {
+ --out->tv_sec;
+ out->tv_usec += 1000000;
+ }
+ out->tv_sec -= in->tv_sec;
+}
+
+/*
+ * Construct an Internet address representation.
+ * If the nflag has been supplied, give
+ * numeric value, otherwise try for symbolic name.
+ */
+char *
+inetname(struct in_addr in)
+{
+ register char *cp;
+ register struct hostent *hp;
+ static int first = 1;
+ static char domain[MAXHOSTNAMELEN + 1], line[MAXHOSTNAMELEN + 1];
+
+ if (first && !nflag) {
+ first = 0;
+ if (gethostname(domain, sizeof(domain) - 1) < 0)
+ domain[0] = '\0';
+ else {
+ cp = strchr(domain, '.');
+ if (cp == NULL) {
+#ifdef WITH_CASPER
+ if (capdns != NULL)
+ hp = cap_gethostbyname(capdns, domain);
+ else
+#endif
+ hp = gethostbyname(domain);
+ if (hp != NULL)
+ cp = strchr(hp->h_name, '.');
+ }
+ if (cp == NULL)
+ domain[0] = '\0';
+ else {
+ ++cp;
+ (void)strncpy(domain, cp, sizeof(domain) - 1);
+ domain[sizeof(domain) - 1] = '\0';
+ }
+ }
+ }
+ if (!nflag && in.s_addr != INADDR_ANY) {
+#ifdef WITH_CASPER
+ if (capdns != NULL)
+ hp = cap_gethostbyaddr(capdns, (char *)&in, sizeof(in),
+ AF_INET);
+ else
+#endif
+ hp = gethostbyaddr((char *)&in, sizeof(in), AF_INET);
+ if (hp != NULL) {
+ if ((cp = strchr(hp->h_name, '.')) != NULL &&
+ strcmp(cp + 1, domain) == 0)
+ *cp = '\0';
+ (void)strncpy(line, hp->h_name, sizeof(line) - 1);
+ line[sizeof(line) - 1] = '\0';
+ return (line);
+ }
+ }
+ return (inet_ntoa(in));
+}
+
+struct hostinfo *
+gethostinfo(register char *hostname)
+{
+ register int n;
+ register struct hostent *hp;
+ register struct hostinfo *hi;
+ register char **p;
+ register u_int32_t addr, *ap;
+
+ if (strlen(hostname) >= MAXHOSTNAMELEN) {
+ Fprintf(stderr, "%s: hostname \"%.32s...\" is too long\n",
+ prog, hostname);
+ exit(1);
+ }
+ hi = calloc(1, sizeof(*hi));
+ if (hi == NULL) {
+ Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno));
+ exit(1);
+ }
+ addr = inet_addr(hostname);
+ if ((int32_t)addr != -1) {
+ hi->name = strdup(hostname);
+ hi->n = 1;
+ hi->addrs = calloc(1, sizeof(hi->addrs[0]));
+ if (hi->addrs == NULL) {
+ Fprintf(stderr, "%s: calloc %s\n",
+ prog, strerror(errno));
+ exit(1);
+ }
+ hi->addrs[0] = addr;
+ return (hi);
+ }
+
+#ifdef WITH_CASPER
+ if (capdns != NULL)
+ hp = cap_gethostbyname(capdns, hostname);
+ else
+#endif
+ hp = gethostbyname(hostname);
+ if (hp == NULL) {
+ Fprintf(stderr, "%s: unknown host %s\n", prog, hostname);
+ exit(1);
+ }
+ if (hp->h_addrtype != AF_INET || hp->h_length != 4) {
+ Fprintf(stderr, "%s: bad host %s\n", prog, hostname);
+ exit(1);
+ }
+ hi->name = strdup(hp->h_name);
+ for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p)
+ continue;
+ hi->n = n;
+ hi->addrs = calloc(n, sizeof(hi->addrs[0]));
+ if (hi->addrs == NULL) {
+ Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno));
+ exit(1);
+ }
+ for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p)
+ memcpy(ap, *p, sizeof(*ap));
+ return (hi);
+}
+
+void
+freehostinfo(register struct hostinfo *hi)
+{
+ if (hi->name != NULL) {
+ free(hi->name);
+ hi->name = NULL;
+ }
+ free((char *)hi->addrs);
+ free((char *)hi);
+}
+
+void
+getaddr(register u_int32_t *ap, register char *hostname)
+{
+ register struct hostinfo *hi;
+
+ hi = gethostinfo(hostname);
+ *ap = hi->addrs[0];
+ freehostinfo(hi);
+}
+
+void
+setsin(register struct sockaddr_in *sin, register u_int32_t addr)
+{
+
+ memset(sin, 0, sizeof(*sin));
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = addr;
+}
+
+/* String to value with optional min and max. Handles decimal and hex. */
+int
+str2val(register const char *str, register const char *what,
+ register int mi, register int ma)
+{
+ register const char *cp;
+ register int val;
+ char *ep;
+
+ if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
+ cp = str + 2;
+ val = (int)strtol(cp, &ep, 16);
+ } else
+ val = (int)strtol(str, &ep, 10);
+ if (*ep != '\0') {
+ Fprintf(stderr, "%s: \"%s\" bad value for %s\n",
+ prog, str, what);
+ exit(1);
+ }
+ if (val < mi && mi >= 0) {
+ if (mi == 0)
+ Fprintf(stderr, "%s: %s must be >= %d\n",
+ prog, what, mi);
+ else
+ Fprintf(stderr, "%s: %s must be > %d\n",
+ prog, what, mi - 1);
+ exit(1);
+ }
+ if (val > ma && ma >= 0) {
+ Fprintf(stderr, "%s: %s must be <= %d\n", prog, what, ma);
+ exit(1);
+ }
+ return (val);
+}
+
+struct outproto *
+setproto(char *pname)
+{
+ struct outproto *proto;
+ int i;
+
+ for (i = 0; protos[i].name != NULL; i++) {
+ if (strcasecmp(protos[i].name, pname) == 0) {
+ break;
+ }
+ }
+ proto = &protos[i];
+ if (proto->name == NULL) { /* generic handler */
+ struct protoent *pe;
+ u_long pnum;
+
+ /* Determine the IP protocol number */
+ if ((pe = getprotobyname(pname)) != NULL)
+ pnum = pe->p_proto;
+ else
+ pnum = str2val(optarg, "proto number", 1, 255);
+ proto->num = pnum;
+ }
+ return (proto);
+}
+
+void
+pkt_compare(const u_char *a, int la, const u_char *b, int lb) {
+ int l;
+ int i;
+
+ for (i = 0; i < la; i++)
+ Printf("%02x", (unsigned int)a[i]);
+ Printf("\n");
+ l = (la <= lb) ? la : lb;
+ for (i = 0; i < l; i++)
+ if (a[i] == b[i])
+ Printf("__");
+ else
+ Printf("%02x", (unsigned int)b[i]);
+ for (; i < lb; i++)
+ Printf("%02x", (unsigned int)b[i]);
+ Printf("\n");
+}
+
+
+void
+usage(void)
+{
+ Fprintf(stderr,
+ "Usage: %s [-adDeEFInrSvx] [-A as_server] [-f first_ttl] [-g gateway]\n"
+ "\t[-i iface] [-m max_ttl] [-M first_ttl] [-p port] [-P proto]\n"
+ "\t[-q nprobes] [-s src_addr] [-t tos] [-w waittime]\n"
+ "\t[-z pausemsecs] host [packetlen]\n", prog);
+ exit(1);
+}
diff --git a/usr.sbin/traceroute/traceroute.h b/usr.sbin/traceroute/traceroute.h
new file mode 100644
index 000000000000..7ddf087f9fb6
--- /dev/null
+++ b/usr.sbin/traceroute/traceroute.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#) $Id: traceroute.h,v 1.1 2000/11/23 20:06:54 leres Exp $ (LBL)
+ */
+
+extern char *prog;
+
+void setsin(struct sockaddr_in *, u_int32_t);