diff options
Diffstat (limited to 'usr.sbin/traceroute')
| -rw-r--r-- | usr.sbin/traceroute/Makefile | 33 | ||||
| -rw-r--r-- | usr.sbin/traceroute/Makefile.depend | 18 | ||||
| -rw-r--r-- | usr.sbin/traceroute/Makefile.depend.options | 7 | ||||
| -rw-r--r-- | usr.sbin/traceroute/as.c | 221 | ||||
| -rw-r--r-- | usr.sbin/traceroute/as.h | 34 | ||||
| -rw-r--r-- | usr.sbin/traceroute/findsaddr-socket.c | 184 | ||||
| -rw-r--r-- | usr.sbin/traceroute/findsaddr-udp.c | 94 | ||||
| -rw-r--r-- | usr.sbin/traceroute/findsaddr.h | 23 | ||||
| -rw-r--r-- | usr.sbin/traceroute/ifaddrlist.c | 160 | ||||
| -rw-r--r-- | usr.sbin/traceroute/ifaddrlist.h | 29 | ||||
| -rw-r--r-- | usr.sbin/traceroute/tests/Makefile | 7 | ||||
| -rwxr-xr-x | usr.sbin/traceroute/tests/traceroute_test.sh | 874 | ||||
| -rw-r--r-- | usr.sbin/traceroute/traceroute.8 | 458 | ||||
| -rw-r--r-- | usr.sbin/traceroute/traceroute.c | 2078 | ||||
| -rw-r--r-- | usr.sbin/traceroute/traceroute.h | 26 |
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); |
