From 7f5f010bbdf8790a2e9ca9b907117ed4eb0d2ad3 Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Mon, 3 Jun 2013 10:54:08 +0000 Subject: [PATCH] netbsd ping & traceroute . remove minix ping . add support for socket(AF_INET, SOCK_RAW, {IPPROTO_ICMP,IPPROTO_UDP}) . gives test48 a better chance of detecting network connectivity Change-Id: Ia678546d27ac378642f1160a141e1fc33458cce2 --- distrib/sets/lists/minix/mi | 14 +- minix/commands/Makefile | 2 +- minix/commands/ping/Makefile | 5 - minix/commands/ping/ping.c | 150 -- minix/include/net/gen/inet.h | 2 - minix/lib/libc/sys/recvfrom.c | 34 + minix/lib/libc/sys/sendto.c | 22 + minix/lib/libc/sys/socket.c | 63 + minix/man/man1/Makefile | 2 +- minix/man/man1/ping.1 | 31 - minix/tests/test48.c | 2 +- sbin/Makefile | 2 +- sbin/ping/Makefile | 22 + sbin/ping/ping.8 | 478 ++++++ sbin/ping/ping.c | 1923 ++++++++++++++++++++++ sbin/ping/ping_hostops.c | 53 + sbin/ping/ping_rumpops.c | 58 + sbin/ping/prog_ops.h | 79 + sys/net/Makefile | 2 + sys/netinet/Makefile | 4 +- sys/netinet/ip_icmp.h | 253 +++ usr.sbin/Makefile | 2 +- usr.sbin/traceroute/CHANGES | 149 ++ usr.sbin/traceroute/Makefile | 29 + usr.sbin/traceroute/as.c | 224 +++ usr.sbin/traceroute/as.h | 34 + usr.sbin/traceroute/gnuc.h | 36 + usr.sbin/traceroute/ifaddrlist.c | 146 ++ usr.sbin/traceroute/ifaddrlist.h | 31 + usr.sbin/traceroute/mean.awk | 14 + usr.sbin/traceroute/median.awk | 31 + usr.sbin/traceroute/prog_ops.h | 72 + usr.sbin/traceroute/traceroute.8 | 419 +++++ usr.sbin/traceroute/traceroute.c | 1905 +++++++++++++++++++++ usr.sbin/traceroute/traceroute_hostops.c | 55 + usr.sbin/traceroute/traceroute_rumpops.c | 59 + usr.sbin/traceroute/trrt2netbsd | 272 +++ usr.sbin/traceroute/version.c | 2 + 38 files changed, 6485 insertions(+), 196 deletions(-) delete mode 100644 minix/commands/ping/Makefile delete mode 100644 minix/commands/ping/ping.c delete mode 100644 minix/man/man1/ping.1 create mode 100644 sbin/ping/Makefile create mode 100644 sbin/ping/ping.8 create mode 100644 sbin/ping/ping.c create mode 100644 sbin/ping/ping_hostops.c create mode 100644 sbin/ping/ping_rumpops.c create mode 100644 sbin/ping/prog_ops.h create mode 100644 sys/netinet/ip_icmp.h create mode 100644 usr.sbin/traceroute/CHANGES create mode 100644 usr.sbin/traceroute/Makefile create mode 100644 usr.sbin/traceroute/as.c create mode 100644 usr.sbin/traceroute/as.h create mode 100644 usr.sbin/traceroute/gnuc.h create mode 100644 usr.sbin/traceroute/ifaddrlist.c create mode 100644 usr.sbin/traceroute/ifaddrlist.h create mode 100644 usr.sbin/traceroute/mean.awk create mode 100644 usr.sbin/traceroute/median.awk create mode 100644 usr.sbin/traceroute/prog_ops.h create mode 100644 usr.sbin/traceroute/traceroute.8 create mode 100644 usr.sbin/traceroute/traceroute.c create mode 100644 usr.sbin/traceroute/traceroute_hostops.c create mode 100644 usr.sbin/traceroute/traceroute_rumpops.c create mode 100755 usr.sbin/traceroute/trrt2netbsd create mode 100644 usr.sbin/traceroute/version.c diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index d79bc793b..611f7d5d9 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -144,6 +144,7 @@ ./sbin/mknod minix-sys ./sbin/newfs_ext2fs minix-sys ./sbin/nologin minix-sys +./sbin/ping minix-sys ./sbin/poweroff minix-sys ./sbin/reboot minix-sys ./sbin/shutdown minix-sys @@ -435,7 +436,7 @@ ./usr/bin/paste minix-sys ./usr/bin/patch minix-sys ./usr/bin/pathchk minix-sys -./usr/bin/ping minix-sys +./usr/bin/ping minix-sys obsolete ./usr/bin/pkgin_all minix-sys ./usr/bin/pkgin_cd minix-sys ./usr/bin/pkgin_sets minix-sys @@ -1441,6 +1442,8 @@ ./usr/include/net/if_ether.h minix-sys ./usr/include/net/if.h minix-sys ./usr/include/net/netlib.h minix-sys +./usr/include/net/radix.h minix-sys +./usr/include/net/route.h minix-sys ./usr/include/netconfig.h minix-sys ./usr/include/netdb.h minix-sys ./usr/include/netgroup.h minix-sys @@ -1448,7 +1451,11 @@ ./usr/include/netinet/in.h minix-sys ./usr/include/netinet/in_systm.h minix-sys ./usr/include/netinet/ip.h minix-sys +./usr/include/netinet/ip_icmp.h minix-sys +./usr/include/netinet/ip_var.h minix-sys ./usr/include/netinet/tcp.h minix-sys +./usr/include/netinet/udp.h minix-sys +./usr/include/netinet/udp_var.h minix-sys ./usr/include/netinet6 minix-sys ./usr/include/netinet6/in6.h minix-sys ./usr/include/nlist.h minix-sys @@ -2429,7 +2436,7 @@ ./usr/man/man1/patch.1 minix-sys ./usr/man/man1/pathchk.1 minix-sys ./usr/man/man1/pax.1 minix-sys -./usr/man/man1/ping.1 minix-sys +./usr/man/man1/ping.1 minix-sys obsolete ./usr/man/man1/pkg_view.1 minix-sys ./usr/man/man1/playwave.1 minix-sys ./usr/man/man1/pr.1 minix-sys @@ -5083,6 +5090,7 @@ ./usr/man/man8/ossdevlinks.8 minix-sys ./usr/man/man8/part.8 minix-sys ./usr/man/man8/partition.8 minix-sys +./usr/man/man8/ping.8 minix-sys ./usr/man/man8/postinstall.8 minix-sys ./usr/man/man8/poweroff.8 minix-sys ./usr/man/man8/printroot.8 minix-sys @@ -5107,6 +5115,7 @@ ./usr/man/man8/sync.8 minix-sys ./usr/man/man8/syslogd.8 minix-sys ./usr/man/man8/tcpd.8 minix-sys +./usr/man/man8/traceroute.8 minix-sys ./usr/man/man8/unix.8 minix-sys ./usr/man/man8/unlink.8 minix-sys ./usr/man/man8/unstr.8 minix-sys @@ -5156,6 +5165,7 @@ ./usr/sbin/postinstall minix-sys ./usr/sbin/pwd_mkdb minix-sys ./usr/sbin/rdate minix-sys +./usr/sbin/traceroute minix-sys ./usr/sbin/unlink minix-sys ./usr/sbin/user minix-sys ./usr/sbin/useradd minix-sys diff --git a/minix/commands/Makefile b/minix/commands/Makefile index 11b275914..cd7e1da42 100644 --- a/minix/commands/Makefile +++ b/minix/commands/Makefile @@ -18,7 +18,7 @@ SUBDIR= add_route arp ash at backup btrace \ mined \ mount mt netconf \ nonamed \ - ping postinstall prep printroot \ + postinstall prep printroot \ profile progressbar pr_routes ps pwdauth \ ramdisk rarpd rawspeed rcp readclock \ remsync rget rlogin \ diff --git a/minix/commands/ping/Makefile b/minix/commands/ping/Makefile deleted file mode 100644 index 9fb8d1f47..000000000 --- a/minix/commands/ping/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -PROG= ping -BINMODE= 4755 -MAN= - -.include diff --git a/minix/commands/ping/ping.c b/minix/commands/ping/ping.c deleted file mode 100644 index de6c66c00..000000000 --- a/minix/commands/ping/ping.c +++ /dev/null @@ -1,150 +0,0 @@ -/* -ping.c -*/ - -#define DEBUG 1 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define WRITE_SIZE 30 -char buffer[16*1024]; - -int main(int argc, char *argv[]); - -#if DEBUG -#define where() fprintf(stderr, "%s %d:", __FILE__, __LINE__); -#endif - -#if __STDC__ -#define PROTO(x,y) x y -#else -#define PROTO(x,y) X () -#endif - -PROTO (int main, (int argc, char *argv[]) ); -static PROTO (void sig_hand, (int signal) ); - -main(argc, argv) -int argc; -char *argv[]; -{ - int fd, i; - int result, result1; - nwio_ipopt_t ipopt; - ip_hdr_t *ip_hdr; - int ihl; - icmp_hdr_t *icmp_hdr; - ipaddr_t dst_addr; - struct hostent *hostent; - int length; - - if (argc<2 || argc>3) - { - fprintf(stderr, "Usage: %s hostname [length]\n", argv[0]); - exit(1); - } - hostent= gethostbyname(argv[1]); - if (!hostent) - { - dst_addr= inet_addr(argv[1]); - if (dst_addr == -1) - { - fprintf(stderr, "%s: unknown host (%s)\n", - argv[0], argv[1]); - exit(1); - } - } - else - dst_addr= *(ipaddr_t *)(hostent->h_addr); - if (argc == 3) - { - length= strtol (argv[2], (char **)0, 0); - if (length< sizeof(icmp_hdr_t) + IP_MIN_HDR_SIZE) - { - fprintf(stderr, "%s: length too small (%s)\n", - argv[0], argv[2]); - exit(1); - } - } - else - length= WRITE_SIZE; - - fd= open ("/dev/ip", O_RDWR); - if (fd<0) - perror("open"), exit(1); - - ipopt.nwio_flags= NWIO_COPY | NWIO_PROTOSPEC; - ipopt.nwio_proto= 1; - - result= ioctl (fd, NWIOSIPOPT, &ipopt); - if (result<0) - perror("ioctl (NWIOSIPOPT)"), exit(1); - - result= ioctl (fd, NWIOGIPOPT, &ipopt); - if (result<0) - perror("ioctl (NWIOGIPOPT)"), exit(1); - - for (i= 0; i< 20; i++) - { - ip_hdr= (ip_hdr_t *)buffer; - ip_hdr->ih_dst= dst_addr; - - icmp_hdr= (icmp_hdr_t *)(buffer+20); - icmp_hdr->ih_type= 8; - icmp_hdr->ih_code= 0; - icmp_hdr->ih_chksum= 0; - icmp_hdr->ih_chksum= ~oneC_sum(0, (u16_t *)icmp_hdr, - WRITE_SIZE-20); - result= write(fd, buffer, length); - if (result<0) - { - perror("write"); - exit(1); - } - if (result != length) - { - where(); - fprintf(stderr, "result= %d\n", result); - exit(1); - } - - alarm(0); - signal (SIGALRM, sig_hand); - alarm(1); - - result= read(fd, buffer, sizeof(buffer)); - if (result>= 0 || errno != EINTR) - break; - } - if (i >= 20) - { - printf("no answer from %s\n", argv[1]); - exit(1); - } - if (result<0) - { - perror ("read"); - exit(1); - } - printf("%s is alive\n", argv[1]); - exit(0); -} - -static void sig_hand(signal) -int signal; -{ -} diff --git a/minix/include/net/gen/inet.h b/minix/include/net/gen/inet.h index 527ba6860..e70c5aa8f 100644 --- a/minix/include/net/gen/inet.h +++ b/minix/include/net/gen/inet.h @@ -9,7 +9,5 @@ server/ip/gen/inet.h ipaddr_t inet_addr( const char *addr ); ipaddr_t inet_network( const char *addr ); -char *inet_ntoa( ipaddr_t addr ); -int inet_aton( const char *cp, ipaddr_t *pin ); #endif /* __SERVER__IP__GEN__INET_H__ */ diff --git a/minix/lib/libc/sys/recvfrom.c b/minix/lib/libc/sys/recvfrom.c index 5f5a19b9e..01d3e48c9 100644 --- a/minix/lib/libc/sys/recvfrom.c +++ b/minix/lib/libc/sys/recvfrom.c @@ -20,6 +20,9 @@ #include #include +#include +#include + #define DEBUG 0 static ssize_t _tcp_recvfrom(int sock, void *__restrict buffer, size_t length, @@ -85,6 +88,37 @@ ssize_t recvfrom(int sock, void *__restrict buffer, size_t length, } } + { + ip_hdr_t *ip_hdr; + int ihl, rd; + icmp_hdr_t *icmp_hdr; + struct sockaddr_in sin; + + rd = read(sock, buffer, length); + + if(rd < 0) return rd; + + assert(rd >= sizeof(*ip_hdr)); + + ip_hdr= buffer; + + if (address != NULL) + { + int len; + memset(&sin, 0, sizeof(sin)); + sin.sin_family= AF_INET; + sin.sin_addr.s_addr= ip_hdr->ih_src; + sin.sin_len= sizeof(sin); + len= *address_len; + if (len > sizeof(sin)) + len= sizeof(sin); + memcpy(address, &sin, len); + *address_len= sizeof(sin); + } + + return rd; + } + #if DEBUG fprintf(stderr, "recvfrom: not implemented for fd %d\n", sock); #endif diff --git a/minix/lib/libc/sys/sendto.c b/minix/lib/libc/sys/sendto.c index 4f174bba2..f0d6a2811 100644 --- a/minix/lib/libc/sys/sendto.c +++ b/minix/lib/libc/sys/sendto.c @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include #include @@ -76,6 +78,26 @@ ssize_t sendto(int sock, const void *message, size_t length, int flags, } } + { + ip_hdr_t *ip_hdr; + int ihl; + icmp_hdr_t *icmp_hdr; + struct sockaddr_in *sinp; + + sinp = (struct sockaddr_in *) __UNCONST(dest_addr); + if (sinp->sin_family != AF_INET) + { + errno= EAFNOSUPPORT; + return -1; + } + + /* raw */ + ip_hdr= (ip_hdr_t *)message; + ip_hdr->ih_dst= sinp->sin_addr.s_addr; + + return write(sock, message, length); + } + #if DEBUG fprintf(stderr, "sendto: not implemented for fd %d\n", sock); #endif diff --git a/minix/lib/libc/sys/socket.c b/minix/lib/libc/sys/socket.c index 9f1883847..f73828a01 100644 --- a/minix/lib/libc/sys/socket.c +++ b/minix/lib/libc/sys/socket.c @@ -11,7 +11,19 @@ __weak_alias(socket, __socket30) #include #include #include + #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -21,6 +33,7 @@ __weak_alias(socket, __socket30) static int _tcp_socket(int type, int protocol); static int _udp_socket(int type, int protocol); static int _uds_socket(int type, int protocol); +static int _raw_socket(int type, int protocol); static void _socket_flags(int type, int *result); int socket(int domain, int type, int protocol) @@ -53,6 +66,12 @@ int socket(int domain, int type, int protocol) if (domain == AF_INET && sock_type == SOCK_DGRAM) return _udp_socket(type, protocol); + if (domain == AF_INET && sock_type == SOCK_RAW && protocol == IPPROTO_ICMP) + return _raw_socket(type, protocol); + + if (domain == AF_INET && sock_type == SOCK_RAW && protocol == IPPROTO_UDP) + return _raw_socket(type, protocol); + #if DEBUG fprintf(stderr, "socket: nothing for domain %d, type %d, protocol %d\n", domain, type, protocol); @@ -127,6 +146,50 @@ static int _udp_socket(int type, int protocol) return fd; } +static int _raw_socket(int type, int protocol) +{ + int r, fd, t_errno, flags = O_RDWR; + struct sockaddr_in sin; + nwio_ipopt_t ipopt; + int result; + + if (protocol != IPPROTO_ICMP && protocol != IPPROTO_UDP && protocol != 0) + { +#if DEBUG + fprintf(stderr, "socket(icmp): bad protocol %d\n", protocol); +#endif + errno= EPROTONOSUPPORT; + return -1; + } + _socket_flags(type, &flags); + fd= open(IP_DEVICE, flags); + if (fd == -1) + return fd; + + memset(&ipopt, 0, sizeof(ipopt)); + + ipopt.nwio_flags= NWIO_COPY; + + if(protocol) { + ipopt.nwio_flags |= NWIO_PROTOSPEC; + ipopt.nwio_proto = protocol; + } + + result = ioctl (fd, NWIOSIPOPT, &ipopt); + if (result<0) { + close(fd); + return -1; + } + + result = ioctl (fd, NWIOGIPOPT, &ipopt); + if (result<0) { + close(fd); + return -1; + } + + return fd; +} + static int _uds_socket(int type, int protocol) { int fd, r, flags = O_RDWR, sock_type; diff --git a/minix/man/man1/Makefile b/minix/man/man1/Makefile index 9c6f46237..8f8b8e14c 100644 --- a/minix/man/man1/Makefile +++ b/minix/man/man1/Makefile @@ -11,7 +11,7 @@ MAN= ash.1 at.1 \ look.1 lp.1 lspci.1 mail.1 \ mixer.1 \ mkproto.1 mount.1 mt.1 \ - ping.1 playwave.1 prep.1 \ + playwave.1 prep.1 \ profile.1 ps.1 rcp.1 recwave.1 \ remsync.1 rget.1 rlogin.1 rsh.1 rz.1 \ spell.1 svc.1 svrctl.1 \ diff --git a/minix/man/man1/ping.1 b/minix/man/man1/ping.1 deleted file mode 100644 index 7a85db2bf..000000000 --- a/minix/man/man1/ping.1 +++ /dev/null @@ -1,31 +0,0 @@ -.TH PING 1 -.SH NAME -ping \- send ICMP ECHO_REQUEST packets to network hosts -.SH SYNOPSIS -.B /usr/bin/ping hostname [length] -.de EX -.TP 20 -\\fB\$1\\fR -# \\$2 -.. -.SH EXAMPLES -.TP 20 -.B ping 192.168.1.1 -# Ping host 192.168.1.1 -.TP 20 -.B ping www.minix3.org 500 -# Ping www.minix3.org with 500 byte IP packets -.SH DESCRIPTION -.PP -Sends ICMP ECHO_REQUEST packets to the specified host and waits for a ECHO_REPLY. -Optionally, the length (size) of the IP packet can be specified. The default is -30 bytes. (Note, the 14 byte Ethernet header will make this a 44 byte packet.) -.SH OPTIONS -.IP length -Size (in bytes) of the transmitted ICMP packet. The default length of the IP -packet is 30 bytes. (Adding the ethernet header will result in a 44 byte -packet being transmitted.) -.SH "SEE ALSO" -ifconfig(8) -.SH AUTHOR -Leith Brandeland diff --git a/minix/tests/test48.c b/minix/tests/test48.c index 33f565c66..fa8dba57f 100644 --- a/minix/tests/test48.c +++ b/minix/tests/test48.c @@ -580,7 +580,7 @@ static int can_use_network(void) int status; /* try to ping minix3.org */ - status = system("ping www.minix3.org > /dev/null 2>&1"); + status = system("ping -w 5 www.minix3.org > /dev/null 2>&1"); if (status == 127) { printf("cannot execute ping\n"); diff --git a/sbin/Makefile b/sbin/Makefile index ee3735542..ed2ae4306 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -10,9 +10,9 @@ SUBDIR= \ chown \ fsck init \ mknod nologin \ + ping \ reboot \ shutdown \ - # support for various file systems SUBDIR+= newfs_ext2fs fsck_ext2fs diff --git a/sbin/ping/Makefile b/sbin/ping/Makefile new file mode 100644 index 000000000..466f7a918 --- /dev/null +++ b/sbin/ping/Makefile @@ -0,0 +1,22 @@ +# $NetBSD: Makefile,v 1.17 2013/11/09 21:39:27 christos Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +USE_FORT?= yes # setuid +RUMPPRG=ping +MAN= ping.8 +BINOWN= root +BINMODE=4555 +LDADD= -lm +DPADD= ${LIBM} + +# BJG - no ipsec +#CPPFLAGS+= -DIPSEC + +#LDADD+= -lipsec +#DPADD+= ${LIBIPSEC} + +.if ${MACHINE_ARCH} == "vax" +COPTS.ping.c=-O0 +.endif + +.include diff --git a/sbin/ping/ping.8 b/sbin/ping/ping.8 new file mode 100644 index 000000000..b74b740d9 --- /dev/null +++ b/sbin/ping/ping.8 @@ -0,0 +1,478 @@ +.\" $NetBSD: ping.8,v 1.50 2011/09/10 20:47:33 wiz Exp $ +.\" +.\" Copyright (c) 1985, 1991, 1993 +.\" 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. 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 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. +.\" +.\" @(#)ping.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd September 10, 2011 +.Dt PING 8 +.Os +.Sh NAME +.Nm ping +.Nd send +.Tn ICMP ECHO_REQUEST +packets to network hosts +.Sh SYNOPSIS +.Nm +.Op Fl aCDdfLnoPQqRrv +.Op Fl c Ar count +.Op Fl E Ar policy +.Op Fl g Ar gateway +.Op Fl h Ar host +.Op Fl I Ar srcaddr +.Op Fl i Ar interval +.Op Fl l Ar preload +.Op Fl p Ar pattern +.Op Fl s Ar packetsize +.Op Fl T Ar ttl +.Op Fl t Ar tos +.Op Fl w Ar deadline +.Ar host +.Sh DESCRIPTION +.Nm +uses the +.Tn ICMP +protocol's mandatory +.Tn ECHO_REQUEST +datagram to elicit an +.Tn ICMP ECHO_RESPONSE +from a host or gateway. +.Tn ECHO_REQUEST +datagrams (``pings'') have an IP and +.Tn ICMP +header, +followed by a +.Dq struct timespec +and then an arbitrary number of ``pad'' bytes used to fill out the +packet. +The options are as follows: +.Bl -tag -width Ds +.It Fl a +Emit an audible beep (by sending an ascii BEL character to the +standard error output) after each non-duplicate response is received. +This is disabled for flood pings as it would probably cause temporary +insanity. +.It Fl C +Send timestamps in compat format; two 32 bit words in little endian format, +the first one representing seconds, and the second one representing +microseconds. +.It Fl c Ar count +Stop after sending (and waiting the specified delay to receive) +.Ar count +.Tn ECHO_RESPONSE +packets. +.It Fl D +Set the +.Dv Don't Fragment +bit in the IP header. +This can be used to determine the path MTU. +.It Fl d +Set the +.Dv SO_DEBUG +option on the socket being used. +.It Fl E Ar policy +Use IPsec policy specification string +.Ar policy +for packets. +For the format of specification string, please refer +.Xr ipsec_set_policy 3 . +Please note that this option is same as +.Fl P +in KAME/FreeBSD and KAME/BSDI +(as +.Fl P +was already occupied in +.Nx ) . +.It Fl f +Flood ping. +Outputs packets as fast as they come back or one hundred times per second, +whichever is more. +For every +.Tn ECHO_REQUEST +sent a period ``.'' is printed, while for every +.Tn ECHO_REPLY +received a backspace is printed. +This provides a rapid display of how many packets are being dropped. +Only the super-user may use this option. +.Bf -emphasis +This can be very hard on a network and should be used with caution. +.Ef +.It Fl g Ar gateway +Use Loose Source Routing to send the ECHO_REQUEST packets via +.Ar gateway . +.It Fl h Ar host +is an alternate way of specifying the target host instead of as the +last argument. +.It Fl I Ar srcaddr +Set the source IP address to +.Ar srcaddr +which can be a hostname or an IP number. +For multicast datagrams, it also specifies the outgoing interface. +.It Fl i Ar interval +Wait +.Ar interval +seconds +.Em between sending each packet . +The default is to wait for one second between each packet, +except when the -f option is used the wait interval is 0.01 seconds. +.It Fl L +Disable loopback when sending to multicast destinations, +so the transmitting host doesn't see the ICMP requests. +.It Fl l Ar preload +If +.Ar preload +is specified, +.Nm +sends that many packets as fast as possible before falling into its normal +mode of behavior. +Only the super-user may use this option. +.It Fl n +Numeric output only. +No attempt will be made to look up symbolic names for host addresses. +.It Fl o +Exit successfully after receiving one reply packet. +.It Fl P +Use a pseudo-random sequence for the data instead of the default, +fixed sequence of incrementing 8-bit integers. +This is useful to foil compression on PPP and other links. +.It Fl p Ar pattern +You may specify up to 16 ``pad'' bytes to fill out the packet you send. +This is useful for diagnosing data-dependent problems in a network. +For example, +.Dq Li \-p ff +will cause the sent packet to be filled with all +ones. +.It Fl Q +Do not display responses such as Network Unreachable ICMP messages +concerning the ECHO_REQUESTs sent. +.It Fl q +Quiet output. +Nothing is displayed except the summary lines at startup time and +when finished. +.It Fl R +Record Route. +Includes the +.Tn RECORD_ROUTE +option in the +.Tn ECHO_REQUEST +packet and displays the route buffer on returned packets. +This should show the path to the target host and back, which is +especially useful in the case of asymmetric routing. +Note that the IP header is only large enough for nine such addresses, +and only seven when using the +.Fl g +option. +This is why it was necessary to invent +.Xr traceroute 8 . +Many hosts ignore or discard this option. +.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 packetsize +Specifies the number of data bytes to be sent. +The default is 56, which translates into 64 +.Tn ICMP +data bytes when combined +with the 8 bytes of +.Tn ICMP +header data. +The maximum allowed value is 65467 bytes. +.It Fl T Ar ttl +Use the specified time-to-live. +.It Fl t Ar tos +Use the specified hexadecimal type of service. +.It Fl v +Verbose output. +.Tn ICMP +packets other than +.Tn ECHO_RESPONSE +that are received are listed. +.It Fl w Ar deadline +Specifies a timeout, in seconds, before ping exits regardless of +how many packets have been sent or received. +.El +.Pp +When using +.Nm +for fault isolation, it should first be run on the local host, to verify +that the local network interface is up and running. +Then, hosts and gateways further and further away should be ``pinged''. +.Pp +Round-trip times and packet loss statistics are computed. +If duplicate packets are received, they are not included in the packet +loss calculation, although the round trip time of these packets is used +in calculating the minimum/average/maximum round-trip time numbers. +.Pp +When the specified number of packets have been sent (and received) or +if the program is terminated with a +.Dv SIGINT , +a brief summary is displayed. +The summary information can be displayed while +.Nm +is running by sending it a +.Dv SIGINFO +signal (see the +.Dq status +argument for +.Xr stty 1 +for more information). +.Pp +.Nm +continually sends one datagram per second, and prints one line of +output for every ECHO_RESPONSE returned. +On a trusted system with IP +Security Options enabled, if the network idiom is not MONO, +.Nm +also prints a second line containing the hexadecimal representation +of the IP security option in the ECHO_RESPONSE. +If the +.Fl c +count option is given, only that number of requests is sent. +No output is produced if there is no response. +Round-trip times and packet loss statistics are computed. +If duplicate packets are received, +they are not included in the packet loss calculation, +although the round trip time of these packets is used in calculating +the minimum/average/maximum round-trip time numbers. +When the specified number of packets have been sent (and received) or if +the program is terminated with an interrupt (SIGINT), a brief +summary is displayed. +When not using the +.Fl f +(flood) option, the first interrupt, usually generated by control-C or DEL, +causes +.Nm +to wait for its outstanding requests to return. +It will wait no longer than the longest round trip time +encountered by previous, successful pings. +The second interrupt stops ping immediately. +.Pp +This program is intended for use in network testing, measurement and +management. +Because of the load it can impose on the network, it is unwise to use +.Nm +during normal operations or from automated scripts. +.Sh ICMP PACKET DETAILS +An IP header without options is 20 bytes. +An +.Tn ICMP +.Tn ECHO_REQUEST +packet contains an additional 8 bytes worth of +.Tn ICMP +header followed by an arbitrary amount of data. +When a +.Ar packetsize +is given, this indicated the size of this extra piece of data (the +default is 56). +Thus the amount of data received inside of an IP packet of type +.Tn ICMP +.Tn ECHO_REPLY +will always be 8 bytes more than the requested data space (the +.Tn ICMP +header). +.Pp +If the data space is at least +.Dv sizeof(struct timespec) +(16) large, +.Nm +uses the first +.Dv sizeof(struct timespec) +bytes to include a timestamp to compute round trip times. +Otherwise if the data space is at least eight bytes large (or the +.Fl C +flag is specified), +.Nm +uses the first eight bytes of this space to include a timestamp to compute +round trip times. +If there are not enough bytes of pad no round trip times are given. +.Sh DUPLICATE AND DAMAGED PACKETS +.Nm +will report duplicate and damaged packets. +Duplicate packets should never occur, and seem to be caused by +inappropriate link-level retransmissions. +Duplicates may occur in many situations and are rarely (if ever) a +good sign, although the presence of low levels of duplicates may not +always be cause for alarm. +.Pp +Damaged packets are obviously serious cause for alarm and often +indicate broken hardware somewhere in the +.Nm +packet's path (in the network or in the hosts). +.Sh TRYING DIFFERENT DATA PATTERNS +The (inter)network layer should never treat packets differently depending +on the data contained in the data portion. +Unfortunately, data-dependent problems have been known to sneak into +networks and remain undetected for long periods of time. +In many cases the particular pattern that will have problems is something +that doesn't have sufficient ``transitions'', such as all ones or all +zeros, or a pattern right at the edge, such as almost all zeros. +It isn't necessarily enough to specify a data pattern of all zeros (for +example) on the command line because the pattern that is of interest is +at the data link level, and the relationship between what you type and +what the controllers transmit can be complicated. +.Pp +This means that if you have a data-dependent problem you will probably +have to do a lot of testing to find it. +If you are lucky, you may manage to find a file that either can't be sent +across your network or that takes much longer to transfer than other +similar length files. +You can then examine this file for repeated patterns that you can test +using the +.Fl p +option of +.Nm . +.Sh TTL DETAILS +The +.Tn TTL +value of an IP packet represents the maximum number of IP routers +that the packet can go through before being thrown away. +In current practice you can expect each router in the Internet to decrement +the +.Tn TTL +field by exactly one. +.Pp +The +.Tn TCP/IP +specification states that the +.Tn TTL +field for +.Tn TCP +packets should +be set to 60, but many systems use smaller values +.Po +.Bx 4.3 +uses 30, +.Bx 4.2 +used 15 +.Pc . +.Pp +The maximum possible value of this field is 255, and most +.Ux +systems set the +.Tn TTL +field of +.Tn ICMP ECHO_REQUEST +packets to 255. +This is why you will find you can ``ping'' some hosts, but not reach them +with +.Xr telnet 1 +or +.Xr ftp 1 . +.Pp +In normal operation ping prints the ttl value from the packet it receives. +When a remote system receives a ping packet, it can do one of three things +with the +.Tn TTL +field in its response: +.Bl -bullet +.It +Not change it; this is what Berkeley +.Ux +systems did before the +.Bx 4.3 tahoe +release. +In this case the +.Tn TTL +value in the received packet will be 255 minus the +number of routers in the round-trip path. +.It +Set it to 255; this is what current Berkeley +.Ux +systems do. +In this case the +.Tn TTL +value in the received packet will be 255 minus the +number of routers in the path +.Em from +the remote system +.Em to +the +.Nm Ns Em ing +host. +.It +Set it to some other value. +Some machines use the same value for +.Tn ICMP +packets that they use for +.Tn TCP +packets, for example either 30 or 60. +Others may use completely wild values. +.El +.Sh EXIT STATUS +.Nm +returns 0 on success (the host is alive), +and non-zero if the arguments are incorrect or the host is not responding. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr icmp 4 , +.Xr inet 4 , +.Xr ip 4 , +.Xr ifconfig 8 , +.Xr routed 8 , +.Xr spray 8 , +.Xr traceroute 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . +IPsec support was added by WIDE/KAME project. +.Sh BUGS +Flood pinging is not recommended in general, and flood pinging a broadcast +or multicast address should only be done under very controlled conditions. +.Pp +The +.Nm +program has evolved differently under different operating systems, +and in some cases the same flag performs a different function +under different operating systems. +The +.Fl t +flag conflicts with +.Fx . +The +.Fl a , c , I , i , +.Fl l , P , p , s , +and +.Fl t +flags conflict with +.Sy Solaris . +.Pp +Some hosts and gateways ignore the +.Tn RECORD_ROUTE +option. +.Pp +The maximum IP header length is too small for options like +.Tn RECORD_ROUTE +to +be completely useful. +There's not much that that can be done about this, however. diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c new file mode 100644 index 000000000..f3d47e04e --- /dev/null +++ b/sbin/ping/ping.c @@ -0,0 +1,1923 @@ +/* $NetBSD: ping.c,v 1.107 2013/10/19 01:08:25 christos Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * 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. 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 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. + */ + +/* + * P I N G . C + * + * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * Modified at Uc Berkeley + * Record Route and verbose headers - Phil Dykstra, BRL, March 1988. + * Multicast options (ttl, if, loop) - Steve Deering, Stanford, August 1988. + * ttl, duplicate detection - Cliff Frost, UCB, April 1989 + * Pad pattern - Cliff Frost (from Tom Ferrin, UCSF), April 1989 + * + * Status - + * Public Domain. Distribution Unlimited. + * + * Bugs - + * More statistics could always be gathered. + * This program has to run SUID to ROOT to access the ICMP socket. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: ping.c,v 1.107 2013/10/19 01:08:25 christos Exp $"); +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef IPSEC +#include +#endif /*IPSEC*/ + +#include "prog_ops.h" + +#define FLOOD_INTVL 0.01 /* default flood output interval */ +#define MAXPACKET (IP_MAXPACKET-60-8) /* max packet size */ + +#define F_VERBOSE 0x0001 +#define F_QUIET 0x0002 /* minimize all output */ +#define F_SEMI_QUIET 0x0004 /* ignore our ICMP errors */ +#define F_FLOOD 0x0008 /* flood-ping */ +#define F_RECORD_ROUTE 0x0010 /* record route */ +#define F_SOURCE_ROUTE 0x0020 /* loose source route */ +#define F_PING_FILLED 0x0040 /* is buffer filled with user data? */ +#define F_PING_RANDOM 0x0080 /* use random data */ +#define F_NUMERIC 0x0100 /* do not do gethostbyaddr() calls */ +#define F_TIMING 0x0200 /* room for a timestamp */ +#define F_DF 0x0400 /* set IP DF bit */ +#define F_SOURCE_ADDR 0x0800 /* set source IP address/interface */ +#define F_ONCE 0x1000 /* exit(0) after receiving 1 reply */ +#define F_MCAST 0x2000 /* multicast target */ +#define F_MCAST_NOLOOP 0x4000 /* no multicast loopback */ +#define F_AUDIBLE 0x8000 /* audible output */ +#define F_TIMING64 0x10000 /* 64 bit time, nanoseconds */ +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC +#define F_POLICY 0x20000 +#else +#define F_AUTHHDR 0x20000 +#define F_ENCRYPT 0x40000 +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ + + +/* MAX_DUP_CHK is the number of bits in received table, the + * maximum number of received sequence numbers we can track to check + * for duplicates. + */ +#define MAX_DUP_CHK (8 * 2048) +static u_char rcvd_tbl[MAX_DUP_CHK/8]; +static int nrepeats = 0; +#define A(seq) rcvd_tbl[(seq/8)%sizeof(rcvd_tbl)] /* byte in array */ +#define B(seq) (1 << (seq & 0x07)) /* bit in byte */ +#define SET(seq) (A(seq) |= B(seq)) +#define CLR(seq) (A(seq) &= (~B(seq))) +#define TST(seq) (A(seq) & B(seq)) + +struct tv32 { + int32_t tv32_sec; + int32_t tv32_usec; +}; + + +static u_char *packet; +static int packlen; +static int pingflags = 0, options; +static int pongflags = 0; +static char *fill_pat; + +static int s; /* Socket file descriptor */ +static int sloop; /* Socket file descriptor/loopback */ + +#define PHDR_LEN sizeof(struct tv32) /* size of timestamp header */ +#define PHDR64_LEN sizeof(struct timespec) /* size of timestamp header */ +static struct sockaddr_in whereto, send_addr; /* Who to ping */ +static struct sockaddr_in src_addr; /* from where */ +static struct sockaddr_in loc_addr; /* 127.1 */ +static int datalen; /* How much data */ +static int phdrlen; + +#ifndef __NetBSD__ +static char *progname; +#define getprogname() (progname) +#define setprogname(name) ((void)(progname = (name))) +#endif + +static char hostname[MAXHOSTNAMELEN]; + +static struct { + struct ip o_ip; + char o_opt[MAX_IPOPTLEN]; + union { + u_char u_buf[MAXPACKET+offsetof(struct icmp, icmp_data)]; + struct icmp u_icmp; + } o_u; +} out_pack; +#define opack_icmp out_pack.o_u.u_icmp +static struct ip *opack_ip; + +static char optspace[MAX_IPOPTLEN]; /* record route space */ +static int optlen; + +static int npackets; /* total packets to send */ +static int preload; /* number of packets to "preload" */ +static int ntransmitted; /* output sequence # = #sent */ +static int ident; /* our ID, in network byte order */ + +static int nreceived; /* # of packets we got back */ + +static double interval; /* interval between packets */ +static struct timespec interval_tv; +static double tmin = 999999999.0; +static double tmax = 0.0; +static double tsum = 0.0; /* sum of all times */ +static double tsumsq = 0.0; +static double maxwait = 0.0; + +#ifndef __minix +static int bufspace = IP_MAXPACKET; +#endif + +static struct timespec now, clear_cache, last_tx, next_tx, first_tx; +static struct timespec last_rx, first_rx; +static int lastrcvd = 1; /* last ping sent has been received */ + +static struct timespec jiggle_time; +static int jiggle_cnt, total_jiggled, jiggle_direction = -1; + +__dead static void doit(void); +static void prefinish(int); +static void prtsig(int); +__dead static void finish(int); +static void summary(int); +static void pinger(void); +static void fill(void); +static void rnd_fill(void); +static double diffsec(struct timespec *, struct timespec *); +#if 0 +static void timespecadd(struct timespec *, struct timespec *); +#endif +static void sec_to_timespec(const double, struct timespec *); +static double timespec_to_sec(const struct timespec *); +static void pr_pack(u_char *, int, struct sockaddr_in *); +static u_int16_t in_cksum(u_int16_t *, u_int); +static void pr_saddr(u_char *); +static char *pr_addr(struct in_addr *); +static void pr_iph(struct icmp *, int); +static void pr_retip(struct icmp *, int); +static int pr_icmph(struct icmp *, struct sockaddr_in *, int); +static void jiggle(int), jiggle_flush(int); +static void gethost(const char *, const char *, + struct sockaddr_in *, char *, int); +__dead static void usage(void); + +int +main(int argc, char *argv[]) +{ + int c, i, on = 1, hostind = 0; + long l; + int len = -1, compat = 0; + u_char ttl = 0; + u_long tos = 0; + char *p; +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC + char *policy_in = NULL; + char *policy_out = NULL; +#endif +#endif +#ifdef SIGINFO + struct sigaction sa; +#endif + + if (prog_init && prog_init() == -1) + err(1, "init failed"); + + if ((s = prog_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) + err(1, "Cannot create socket"); + if ((sloop = prog_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) + err(1, "Cannot create socket"); + +#ifndef __minix + /* + * sloop is never read on. This prevents packets from + * queueing in its recv buffer. + */ + if (prog_shutdown(sloop, SHUT_RD) == -1) + warn("Cannot shutdown for read"); +#endif + + if (prog_setuid(prog_getuid()) == -1) + err(1, "setuid"); + + setprogname(argv[0]); + +#ifndef IPSEC +#define IPSECOPT +#else +#ifdef IPSEC_POLICY_IPSEC +#define IPSECOPT "E:" +#else +#define IPSECOPT "AE" +#endif /*IPSEC_POLICY_IPSEC*/ +#endif + while ((c = getopt(argc, argv, + "ac:CdDfg:h:i:I:l:Lnop:PqQrRs:t:T:vw:" IPSECOPT)) != -1) { +#undef IPSECOPT + switch (c) { + case 'a': + pingflags |= F_AUDIBLE; + break; + case 'C': + compat = 1; + break; + case 'c': + npackets = strtol(optarg, &p, 0); + if (*p != '\0' || npackets <= 0) + errx(1, "Bad/invalid number of packets"); + break; + case 'D': + pingflags |= F_DF; + break; + case 'd': + options |= SO_DEBUG; + break; + case 'f': + pingflags |= F_FLOOD; + break; + case 'h': + hostind = optind-1; + break; + case 'i': /* wait between sending packets */ + interval = strtod(optarg, &p); + if (*p != '\0' || interval <= 0) + errx(1, "Bad/invalid interval %s", optarg); + break; + case 'l': + preload = strtol(optarg, &p, 0); + if (*p != '\0' || preload < 0) + errx(1, "Bad/invalid preload value %s", + optarg); + break; + case 'n': + pingflags |= F_NUMERIC; + break; + case 'o': + pingflags |= F_ONCE; + break; + case 'p': /* fill buffer with user pattern */ + if (pingflags & F_PING_RANDOM) + errx(1, "Only one of -P and -p allowed"); + pingflags |= F_PING_FILLED; + fill_pat = optarg; + break; + case 'P': + if (pingflags & F_PING_FILLED) + errx(1, "Only one of -P and -p allowed"); + pingflags |= F_PING_RANDOM; + break; + case 'q': + pingflags |= F_QUIET; + break; + case 'Q': + pingflags |= F_SEMI_QUIET; + break; + case 'r': + options |= SO_DONTROUTE; + break; + case 's': /* size of packet to send */ + l = strtol(optarg, &p, 0); + if (*p != '\0' || l < 0) + errx(1, "Bad/invalid packet size %s", optarg); + if (l > MAXPACKET) + errx(1, "packet size is too large"); + len = (int)l; + break; + case 'v': + pingflags |= F_VERBOSE; + break; + case 'R': + pingflags |= F_RECORD_ROUTE; + break; + case 'L': + pingflags |= F_MCAST_NOLOOP; + break; + case 't': + tos = strtoul(optarg, &p, 0); + if (*p != '\0' || tos > 0xFF) + errx(1, "bad tos value: %s", optarg); + break; + case 'T': + l = strtol(optarg, &p, 0); + if (*p != '\0' || l > 255 || l <= 0) + errx(1, "ttl out of range"); + ttl = (u_char)l; /* cannot check >255 otherwise */ + break; + case 'I': + pingflags |= F_SOURCE_ADDR; + gethost("-I", optarg, &src_addr, 0, 0); + break; + case 'g': + pingflags |= F_SOURCE_ROUTE; + gethost("-g", optarg, &send_addr, 0, 0); + break; + case 'w': + maxwait = strtod(optarg, &p); + if (*p != '\0' || maxwait <= 0) + errx(1, "Bad/invalid maxwait time %s", optarg); + break; +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC + case 'E': + pingflags |= F_POLICY; + if (!strncmp("in", optarg, 2)) { + policy_in = strdup(optarg); + if (!policy_in) + err(1, "strdup"); + } else if (!strncmp("out", optarg, 3)) { + policy_out = strdup(optarg); + if (!policy_out) + err(1, "strdup"); + } else + errx(1, "invalid security policy"); + break; +#else + case 'A': + pingflags |= F_AUTHHDR; + break; + case 'E': + pingflags |= F_ENCRYPT; + break; +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ + default: + usage(); + break; + } + } + + if (interval == 0) + interval = (pingflags & F_FLOOD) ? FLOOD_INTVL : 1.0; +#ifndef sgi + if (pingflags & F_FLOOD && prog_getuid()) + errx(1, "Must be superuser to use -f"); + if (interval < 1.0 && prog_getuid()) + errx(1, "Must be superuser to use < 1 sec ping interval"); + if (preload > 0 && prog_getuid()) + errx(1, "Must be superuser to use -l"); +#endif + sec_to_timespec(interval, &interval_tv); + + if ((pingflags & (F_AUDIBLE|F_FLOOD)) == (F_AUDIBLE|F_FLOOD)) + warnx("Sorry, no audible output for flood pings"); + + if (npackets != 0) { + npackets += preload; + } else { + npackets = INT_MAX; + } + + if (hostind == 0) { + if (optind != argc-1) + usage(); + else + hostind = optind; + } + else if (hostind >= argc - 1) + usage(); + + gethost("", argv[hostind], &whereto, hostname, sizeof(hostname)); + if (IN_MULTICAST(ntohl(whereto.sin_addr.s_addr))) + pingflags |= F_MCAST; + if (!(pingflags & F_SOURCE_ROUTE)) + (void) memcpy(&send_addr, &whereto, sizeof(send_addr)); + + loc_addr.sin_family = AF_INET; + loc_addr.sin_len = sizeof(struct sockaddr_in); + loc_addr.sin_addr.s_addr = htonl((127 << 24) + 1); + + if (len != -1) + datalen = len; + else + datalen = 64; + if (!compat && datalen >= (int)PHDR64_LEN) { /* can we time them? */ + pingflags |= F_TIMING64; + phdrlen = PHDR64_LEN; + } else if (datalen >= (int)PHDR_LEN) { /* can we time them? */ + pingflags |= F_TIMING; + phdrlen = PHDR_LEN; + } else + phdrlen = 0; + + packlen = datalen + 60 + 76; /* MAXIP + MAXICMP */ + datalen -= phdrlen; + if ((packet = malloc(packlen)) == NULL) + err(1, "Out of memory"); + + if (pingflags & F_PING_FILLED) { + fill(); + } else if (pingflags & F_PING_RANDOM) { + rnd_fill(); + } else { + for (i = phdrlen; i < datalen; i++) + opack_icmp.icmp_data[i] = i; + } + + ident = arc4random() & 0xFFFF; + + if (options & SO_DEBUG) { + if (prog_setsockopt(s, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)) == -1) + warn("Can't turn on socket debugging"); + } + if (options & SO_DONTROUTE) { + if (prog_setsockopt(s, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)) == -1) + warn("SO_DONTROUTE"); + } + + if (options & SO_DEBUG) { + if (prog_setsockopt(sloop, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)) == -1) + warn("Can't turn on socket debugging"); + } + if (options & SO_DONTROUTE) { + if (prog_setsockopt(sloop, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)) == -1) + warn("SO_DONTROUTE"); + } + + if (pingflags & F_SOURCE_ROUTE) { + optspace[IPOPT_OPTVAL] = IPOPT_LSRR; + optspace[IPOPT_OLEN] = optlen = 7; + optspace[IPOPT_OFFSET] = IPOPT_MINOFF; + (void)memcpy(&optspace[IPOPT_MINOFF-1], &whereto.sin_addr, + sizeof(whereto.sin_addr)); + optspace[optlen++] = IPOPT_NOP; + } + if (pingflags & F_RECORD_ROUTE) { + optspace[optlen+IPOPT_OPTVAL] = IPOPT_RR; + optspace[optlen+IPOPT_OLEN] = (MAX_IPOPTLEN -1-optlen); + optspace[optlen+IPOPT_OFFSET] = IPOPT_MINOFF; + optlen = MAX_IPOPTLEN; + } + /* this leaves opack_ip 0(mod 4) aligned */ + opack_ip = (struct ip *)((char *)&out_pack.o_ip + + sizeof(out_pack.o_opt) + - optlen); + (void) memcpy(opack_ip + 1, optspace, optlen); + +#ifndef __minix + if (prog_setsockopt(s,IPPROTO_IP,IP_HDRINCL, + (char *) &on, sizeof(on)) < 0) + err(1, "Can't set special IP header"); +#endif + + opack_ip->ip_v = IPVERSION; + opack_ip->ip_hl = (sizeof(struct ip)+optlen) >> 2; + opack_ip->ip_tos = tos; + opack_ip->ip_off = (pingflags & F_DF) ? IP_DF : 0; + opack_ip->ip_ttl = ttl ? ttl : MAXTTL; + opack_ip->ip_p = IPPROTO_ICMP; + opack_ip->ip_src = src_addr.sin_addr; + opack_ip->ip_dst = send_addr.sin_addr; + + if (pingflags & F_MCAST) { + if (pingflags & F_MCAST_NOLOOP) { + u_char loop = 0; + if (prog_setsockopt(s, IPPROTO_IP, + IP_MULTICAST_LOOP, + (char *) &loop, 1) < 0) + err(1, "Can't disable multicast loopback"); + } + + if (ttl != 0 + && prog_setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, + (char *) &ttl, 1) < 0) + err(1, "Can't set multicast time-to-live"); + + if ((pingflags & F_SOURCE_ADDR) + && prog_setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, + (char *) &src_addr.sin_addr, + sizeof(src_addr.sin_addr)) < 0) + err(1, "Can't set multicast source interface"); + + } else if (pingflags & F_SOURCE_ADDR) { + if (prog_setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, + (char *) &src_addr.sin_addr, + sizeof(src_addr.sin_addr)) < 0) + err(1, "Can't set source interface/address"); + } +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC + { + char *buf; + if (pingflags & F_POLICY) { + if (policy_in != NULL) { + buf = ipsec_set_policy(policy_in, strlen(policy_in)); + if (buf == NULL) + errx(1, "%s", ipsec_strerror()); + if (prog_setsockopt(s, IPPROTO_IP, IP_IPSEC_POLICY, + buf, ipsec_get_policylen(buf)) < 0) { + err(1, "ipsec policy cannot be configured"); + } + free(buf); + } + if (policy_out != NULL) { + buf = ipsec_set_policy(policy_out, strlen(policy_out)); + if (buf == NULL) + errx(1, "%s", ipsec_strerror()); + if (prog_setsockopt(s, IPPROTO_IP, IP_IPSEC_POLICY, + buf, ipsec_get_policylen(buf)) < 0) { + err(1, "ipsec policy cannot be configured"); + } + free(buf); + } + } + buf = ipsec_set_policy("out bypass", strlen("out bypass")); + if (buf == NULL) + errx(1, "%s", ipsec_strerror()); + if (prog_setsockopt(sloop, IPPROTO_IP, IP_IPSEC_POLICY, + buf, ipsec_get_policylen(buf)) < 0) { +#if 0 + warnx("ipsec is not configured"); +#else + /* ignore it, should be okay */ +#endif + } + free(buf); + } +#else + { + int optval; + if (pingflags & F_AUTHHDR) { + optval = IPSEC_LEVEL_REQUIRE; +#ifdef IP_AUTH_TRANS_LEVEL + (void)prog_setsockopt(s, IPPROTO_IP, IP_AUTH_TRANS_LEVEL, + (char *)&optval, sizeof(optval)); +#else + (void)prog_setsockopt(s, IPPROTO_IP, IP_AUTH_LEVEL, + (char *)&optval, sizeof(optval)); +#endif + } + if (pingflags & F_ENCRYPT) { + optval = IPSEC_LEVEL_REQUIRE; + (void)prog_setsockopt(s, IPPROTO_IP, IP_ESP_TRANS_LEVEL, + (char *)&optval, sizeof(optval)); + } + optval = IPSEC_LEVEL_BYPASS; +#ifdef IP_AUTH_TRANS_LEVEL + (void)prog_setsockopt(sloop, IPPROTO_IP, IP_AUTH_TRANS_LEVEL, + (char *)&optval, sizeof(optval)); +#else + (void)prog_setsockopt(sloop, IPPROTO_IP, IP_AUTH_LEVEL, + (char *)&optval, sizeof(optval)); +#endif + (void)prog_setsockopt(sloop, IPPROTO_IP, IP_ESP_TRANS_LEVEL, + (char *)&optval, sizeof(optval)); + } +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ + + (void)printf("PING %s (%s): %d data bytes\n", hostname, + inet_ntoa(whereto.sin_addr), datalen + phdrlen); + +#ifndef __minix + /* When pinging the broadcast address, you can get a lot + * of answers. Doing something so evil is useful if you + * are trying to stress the ethernet, or just want to + * fill the arp cache to get some stuff for /etc/ethers. + */ + while (0 > prog_setsockopt(s, SOL_SOCKET, SO_RCVBUF, + (char*)&bufspace, sizeof(bufspace))) { + if ((bufspace -= 4096) <= 0) + err(1, "Cannot set the receive buffer size"); + } + + /* make it possible to send giant probes, but do not worry now + * if it fails, since we probably won't send giant probes. + */ + (void)prog_setsockopt(s, SOL_SOCKET, SO_SNDBUF, + (char*)&bufspace, sizeof(bufspace)); +#endif + + (void)signal(SIGINT, prefinish); + +#ifdef SIGINFO + sa.sa_handler = prtsig; + sa.sa_flags = SA_NOKERNINFO; + sigemptyset(&sa.sa_mask); + (void)sigaction(SIGINFO, &sa, NULL); +#else + (void)signal(SIGQUIT, prtsig); +#endif + (void)signal(SIGCONT, prtsig); + + /* fire off them quickies */ + for (i = 0; i < preload; i++) { + clock_gettime(CLOCK_MONOTONIC, &now); + pinger(); + } + + doit(); + return 0; +} + + +static void +doit(void) +{ + int cc; + struct sockaddr_in from; + socklen_t fromlen; + double sec, last, d_last; + struct pollfd fdmaskp[1]; + + (void)clock_gettime(CLOCK_MONOTONIC, &clear_cache); + if (maxwait != 0) { + last = timespec_to_sec(&clear_cache) + maxwait; + d_last = 0; + } else { + last = 0; + d_last = 365*24*60*60; + } + + do { + clock_gettime(CLOCK_MONOTONIC, &now); + + if (last != 0) + d_last = last - timespec_to_sec(&now); + + if (ntransmitted < npackets && d_last > 0) { + /* send if within 100 usec or late for next packet */ + sec = diffsec(&next_tx, &now); + if (sec <= 0.0001 || + (lastrcvd && (pingflags & F_FLOOD))) { + pinger(); + sec = diffsec(&next_tx, &now); + } + if (sec < 0.0) + sec = 0.0; + if (d_last < sec) + sec = d_last; + + } else { + /* For the last response, wait twice as long as the + * worst case seen, or 10 times as long as the + * maximum interpacket interval, whichever is longer. + */ + sec = MAX(2 * tmax, 10 * interval) - + diffsec(&now, &last_tx); + if (d_last < sec) + sec = d_last; + if (sec <= 0) + break; + } + + fdmaskp[0].fd = s; + fdmaskp[0].events = POLLIN; + cc = prog_poll(fdmaskp, 1, (int)(sec * 1000)); + if (cc <= 0) { + if (cc < 0) { + if (errno == EINTR) + continue; + jiggle_flush(1); + err(1, "poll"); + } + continue; + } + + fromlen = sizeof(from); + cc = prog_recvfrom(s, (char *) packet, packlen, + 0, (struct sockaddr *)&from, + &fromlen); + if (cc < 0) { + perror("recvfrom failed"); + if (errno != EINTR) { + jiggle_flush(1); + warn("recvfrom"); + (void)fflush(stderr); + } + continue; + } + clock_gettime(CLOCK_MONOTONIC, &now); + pr_pack(packet, cc, &from); + + } while (nreceived < npackets + && (nreceived == 0 || !(pingflags & F_ONCE))); + + finish(0); +} + + +static void +jiggle_flush(int nl) /* new line if there are dots */ +{ + int serrno = errno; + + if (jiggle_cnt > 0) { + total_jiggled += jiggle_cnt; + jiggle_direction = 1; + do { + (void)putchar('.'); + } while (--jiggle_cnt > 0); + + } else if (jiggle_cnt < 0) { + total_jiggled -= jiggle_cnt; + jiggle_direction = -1; + do { + (void)putchar('\b'); + } while (++jiggle_cnt < 0); + } + + if (nl) { + if (total_jiggled != 0) + (void)putchar('\n'); + total_jiggled = 0; + jiggle_direction = -1; + } + + (void)fflush(stdout); + (void)fflush(stderr); + jiggle_time = now; + errno = serrno; +} + + +/* jiggle the cursor for flood-ping + */ +static void +jiggle(int delta) +{ + double dt; + + if (pingflags & F_QUIET) + return; + + /* do not back up into messages */ + if (total_jiggled+jiggle_cnt+delta < 0) + return; + + jiggle_cnt += delta; + + /* flush the FLOOD dots when things are quiet + * or occassionally to make the cursor jiggle. + */ + dt = diffsec(&last_tx, &jiggle_time); + if (dt > 0.2 || (dt >= 0.15 && delta*jiggle_direction < 0)) + jiggle_flush(0); +} + + +/* + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first phdrlen bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time, or a UNIX "timespec" in native + * format. + */ +static void +pinger(void) +{ + struct tv32 tv32; +#ifndef __minix + int i, cc, sw; +#else + int i, cc; +#endif + + opack_icmp.icmp_code = 0; + opack_icmp.icmp_seq = htons((u_int16_t)(ntransmitted)); + +#ifndef __minix + /* clear the cached route in the kernel after an ICMP + * response such as a Redirect is seen to stop causing + * more such packets. Also clear the cached route + * periodically in case of routing changes that make + * black holes come and go. + */ + if (clear_cache.tv_sec != now.tv_sec) { + opack_icmp.icmp_type = ICMP_ECHOREPLY; + opack_icmp.icmp_id = ~ident; + opack_icmp.icmp_cksum = 0; + opack_icmp.icmp_cksum = in_cksum((u_int16_t *)&opack_icmp, + phdrlen); + sw = 0; + if (prog_setsockopt(sloop,IPPROTO_IP,IP_HDRINCL, + (char *)&sw,sizeof(sw)) < 0) + err(1, "Can't turn off special IP header"); + if (prog_sendto(sloop, (char *) &opack_icmp, + ICMP_MINLEN, MSG_DONTROUTE, + (struct sockaddr *)&loc_addr, + sizeof(struct sockaddr_in)) < 0) { + /* + * XXX: we only report this as a warning in verbose + * mode because people get confused when they see + * this error when they are running in single user + * mode and they have not configured lo0 + */ + if (pingflags & F_VERBOSE) + warn("failed to clear cached route"); + } + sw = 1; + if (prog_setsockopt(sloop,IPPROTO_IP,IP_HDRINCL, + (char *)&sw, sizeof(sw)) < 0) + err(1, "Can't set special IP header"); + + (void)clock_gettime(CLOCK_MONOTONIC, &clear_cache); + } +#endif + + opack_icmp.icmp_type = ICMP_ECHO; + opack_icmp.icmp_id = ident; + + if (pingflags & F_TIMING) { + tv32.tv32_sec = (uint32_t)htonl(now.tv_sec); + tv32.tv32_usec = htonl(now.tv_nsec / 1000); + (void) memcpy(&opack_icmp.icmp_data[0], &tv32, sizeof(tv32)); + } else if (pingflags & F_TIMING64) + (void) memcpy(&opack_icmp.icmp_data[0], &now, sizeof(now)); + + cc = MAX(datalen, ICMP_MINLEN) + phdrlen; + opack_icmp.icmp_cksum = 0; + opack_icmp.icmp_cksum = in_cksum((u_int16_t *)&opack_icmp, cc); + + cc += opack_ip->ip_hl<<2; + opack_ip->ip_len = cc; + i = prog_sendto(s, (char *) opack_ip, cc, 0, + (struct sockaddr *)&send_addr, sizeof(struct sockaddr_in)); + + if (i != cc) { + jiggle_flush(1); + if (i < 0) + warn("sendto"); + else + warnx("wrote %s %d chars, ret=%d", hostname, cc, i); + (void)fflush(stderr); + } + lastrcvd = 0; + + CLR(ntransmitted); + ntransmitted++; + + last_tx = now; + if (next_tx.tv_sec == 0) { + first_tx = now; + next_tx = now; + } + + /* Transmit regularly, at always the same microsecond in the + * second when going at one packet per second. + * If we are at most 100 ms behind, send extras to get caught up. + * Otherwise, skip packets we were too slow to send. + */ + if (diffsec(&next_tx, &now) <= interval) { + do { + timespecadd(&next_tx, &interval_tv, &next_tx); + } while (diffsec(&next_tx, &now) < -0.1); + } + + if (pingflags & F_FLOOD) + jiggle(1); + + /* While the packet is going out, ready buffer for the next + * packet. Use a fast but not very good random number generator. + */ + if (pingflags & F_PING_RANDOM) + rnd_fill(); +} + + +static void +pr_pack_sub(int cc, + char *addr, + int seqno, + int dupflag, + int ttl, + double triptime) +{ + jiggle_flush(1); + + if (pingflags & F_FLOOD) + return; + + (void)printf("%d bytes from %s: icmp_seq=%u", cc, addr, seqno); + if (dupflag) + (void)printf(" DUP!"); + (void)printf(" ttl=%d", ttl); + if (pingflags & (F_TIMING|F_TIMING64)) { + const unsigned int prec = (pingflags & F_TIMING64) != 0 ? 6 : 3; + + (void)printf(" time=%.*f ms", prec, triptime*1000.0); + } + + /* + * Send beep to stderr, since that's more likely than stdout + * to go to a terminal.. + */ + if (pingflags & F_AUDIBLE && !dupflag) + (void)fprintf(stderr,"\a"); +} + + +/* + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +static void +pr_pack(u_char *buf, + int tot_len, + struct sockaddr_in *from) +{ + struct ip *ip; + struct icmp *icp; + int i, j, net_len; + u_char *cp; + static int old_rrlen; + static char old_rr[MAX_IPOPTLEN]; + int hlen, dupflag = 0, dumped; + double triptime = 0.0; +#define PR_PACK_SUB() {if (!dumped) { \ + dumped = 1; \ + pr_pack_sub(net_len, inet_ntoa(from->sin_addr), \ + ntohs((u_int16_t)icp->icmp_seq), \ + dupflag, ip->ip_ttl, triptime);}} + + /* Check the IP header */ + ip = (struct ip *) buf; + hlen = ip->ip_hl << 2; + if (tot_len < hlen + ICMP_MINLEN) { + if (pingflags & F_VERBOSE) { + jiggle_flush(1); + (void)printf("packet too short (%d bytes) from %s\n", + tot_len, inet_ntoa(from->sin_addr)); + } + return; + } + + /* Now the ICMP part */ + dumped = 0; + net_len = tot_len - hlen; + icp = (struct icmp *)(buf + hlen); + if (icp->icmp_type == ICMP_ECHOREPLY + && icp->icmp_id == ident) { + + if (icp->icmp_seq == htons((u_int16_t)(ntransmitted-1))) + lastrcvd = 1; + last_rx = now; + if (first_rx.tv_sec == 0) + first_rx = last_rx; + nreceived++; + if (pingflags & (F_TIMING|F_TIMING64)) { + struct timespec tv; + + if (pingflags & F_TIMING) { + struct tv32 tv32; + + (void)memcpy(&tv32, icp->icmp_data, sizeof(tv32)); + tv.tv_sec = (uint32_t)ntohl(tv32.tv32_sec); + tv.tv_nsec = ntohl(tv32.tv32_usec) * 1000; + } else if (pingflags & F_TIMING64) + (void)memcpy(&tv, icp->icmp_data, sizeof(tv)); + else + memset(&tv, 0, sizeof(tv)); /* XXX: gcc */ + + triptime = diffsec(&last_rx, &tv); + tsum += triptime; + tsumsq += triptime * triptime; + if (triptime < tmin) + tmin = triptime; + if (triptime > tmax) + tmax = triptime; + } + + if (TST(ntohs((u_int16_t)icp->icmp_seq))) { + nrepeats++, nreceived--; + dupflag=1; + } else { + SET(ntohs((u_int16_t)icp->icmp_seq)); + } + + if (tot_len != opack_ip->ip_len) { + PR_PACK_SUB(); + switch (opack_ip->ip_len - tot_len) { + case MAX_IPOPTLEN: + if ((pongflags & F_RECORD_ROUTE) != 0) + break; + if ((pingflags & F_RECORD_ROUTE) == 0) + goto out; + pongflags |= F_RECORD_ROUTE; + (void)printf("\nremote host does not " + "support record route"); + break; + case 8: + if ((pongflags & F_SOURCE_ROUTE) != 0) + break; + if ((pingflags & F_SOURCE_ROUTE) == 0) + goto out; + pongflags |= F_SOURCE_ROUTE; + (void)printf("\nremote host does not " + "support source route"); + break; + default: + out: + (void)printf("\nwrong total length %d " + "instead of %d", tot_len, opack_ip->ip_len); + break; + } + } + + if (!dupflag) { + static u_int16_t last_seqno = 0xffff; + u_int16_t seqno = ntohs((u_int16_t)icp->icmp_seq); + u_int16_t gap = seqno - (last_seqno + 1); + if (gap > 0 && gap < 0x8000 && + (pingflags & F_VERBOSE)) { + (void)printf("[*** sequence gap of %u " + "packets from %u ... %u ***]\n", gap, + (u_int16_t) (last_seqno + 1), + (u_int16_t) (seqno - 1)); + if (pingflags & F_QUIET) + summary(0); + } + + if (gap < 0x8000) + last_seqno = seqno; + } + + if (pingflags & F_QUIET) + return; + + if (!(pingflags & F_FLOOD)) + PR_PACK_SUB(); + + /* check the data */ + if ((size_t)(tot_len - hlen) > + offsetof(struct icmp, icmp_data) + datalen + && !(pingflags & F_PING_RANDOM) + && memcmp(icp->icmp_data + phdrlen, + opack_icmp.icmp_data + phdrlen, + datalen - phdrlen)) { + for (i = phdrlen; i < datalen; i++) { + if (icp->icmp_data[i] != + opack_icmp.icmp_data[i]) + break; + } + PR_PACK_SUB(); + (void)printf("\nwrong data byte #%d should have been" + " %#x but was %#x", i, + (u_char)opack_icmp.icmp_data[i], + (u_char)icp->icmp_data[i]); + for (i = phdrlen; i < datalen; i++) { + if ((i % 16) == 0) + (void)printf("\n\t"); + (void)printf("%2x ",(u_char)icp->icmp_data[i]); + } + } + + } else { + if (!pr_icmph(icp, from, net_len)) + return; + dumped = 2; + } + + /* Display any IP options */ + cp = buf + sizeof(struct ip); + while (hlen > (int)sizeof(struct ip)) { + switch (*cp) { + case IPOPT_EOL: + hlen = 0; + break; + case IPOPT_LSRR: + hlen -= 2; + j = *++cp; + ++cp; + j -= IPOPT_MINOFF; + if (j <= 0) + continue; + if (dumped <= 1) { + j = ((j+3)/4)*4; + hlen -= j; + cp += j; + break; + } + PR_PACK_SUB(); + (void)printf("\nLSRR: "); + for (;;) { + pr_saddr(cp); + cp += 4; + hlen -= 4; + j -= 4; + if (j <= 0) + break; + (void)putchar('\n'); + } + break; + case IPOPT_RR: + j = *++cp; /* get length */ + i = *++cp; /* and pointer */ + hlen -= 2; + if (i > j) + i = j; + i -= IPOPT_MINOFF; + if (i <= 0) + continue; + if (dumped <= 1) { + if (i == old_rrlen + && !memcmp(cp, old_rr, i)) { + if (dumped) + (void)printf("\t(same route)"); + j = ((i+3)/4)*4; + hlen -= j; + cp += j; + break; + } + old_rrlen = i; + (void) memcpy(old_rr, cp, i); + } + if (!dumped) { + jiggle_flush(1); + (void)printf("RR: "); + dumped = 1; + } else { + (void)printf("\nRR: "); + } + for (;;) { + pr_saddr(cp); + cp += 4; + hlen -= 4; + i -= 4; + if (i <= 0) + break; + (void)putchar('\n'); + } + break; + case IPOPT_NOP: + if (dumped <= 1) + break; + PR_PACK_SUB(); + (void)printf("\nNOP"); + break; +#ifdef sgi + case IPOPT_SECURITY: /* RFC 1108 RIPSO BSO */ + case IPOPT_ESO: /* RFC 1108 RIPSO ESO */ + case IPOPT_CIPSO: /* Commercial IPSO */ + if ((sysconf(_SC_IP_SECOPTS)) > 0) { + i = (unsigned)cp[1]; + hlen -= i - 1; + PR_PACK_SUB(); + (void)printf("\nSEC:"); + while (i--) { + (void)printf(" %02x", *cp++); + } + cp--; + break; + } +#endif + default: + PR_PACK_SUB(); + (void)printf("\nunknown option 0x%x", *cp); + break; + } + hlen--; + cp++; + } + + if (dumped) { + (void)putchar('\n'); + (void)fflush(stdout); + } else { + jiggle(-1); + } +} + + +/* Compute the IP checksum + * This assumes the packet is less than 32K long. + */ +static u_int16_t +in_cksum(u_int16_t *p, u_int len) +{ + u_int32_t sum = 0; + int nwords = len >> 1; + + while (nwords-- != 0) + sum += *p++; + + if (len & 1) { + union { + u_int16_t w; + u_int8_t c[2]; + } u; + u.c[0] = *(u_char *)p; + u.c[1] = 0; + sum += u.w; + } + + /* end-around-carry */ + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return (~sum); +} + + +/* + * compute the difference of two timespecs in seconds + */ +static double +diffsec(struct timespec *timenow, + struct timespec *then) +{ + if (timenow->tv_sec == 0) + return -1; + return (timenow->tv_sec - then->tv_sec) + * 1.0 + (timenow->tv_nsec - then->tv_nsec) / 1000000000.0; +} + + +#if 0 +static void +timespecadd(struct timespec *t1, + struct timespec *t2) +{ + + t1->tv_sec += t2->tv_sec; + if ((t1->tv_nsec += t2->tv_nsec) >= 1000000000) { + t1->tv_sec++; + t1->tv_nsec -= 1000000000; + } +} +#endif + + +static void +sec_to_timespec(const double sec, struct timespec *tp) +{ + tp->tv_sec = sec; + tp->tv_nsec = (sec - tp->tv_sec) * 1000000000.0; +} + + +static double +timespec_to_sec(const struct timespec *tp) +{ + return tp->tv_sec + tp->tv_nsec / 1000000000.0; +} + + +/* + * Print statistics. + * Heavily buffered STDIO is used here, so that all the statistics + * will be written with 1 sys-write call. This is nice when more + * than one copy of the program is running on a terminal; it prevents + * the statistics output from becomming intermingled. + */ +static void +summary(int header) +{ + jiggle_flush(1); + + if (header) + (void)printf("\n----%s PING Statistics----\n", hostname); + (void)printf("%d packets transmitted, ", ntransmitted); + (void)printf("%d packets received, ", nreceived); + if (nrepeats) + (void)printf("+%d duplicates, ", nrepeats); + if (ntransmitted) { + if (nreceived > ntransmitted) + (void)printf("-- somebody's duplicating packets!"); + else + (void)printf("%.1f%% packet loss", + (((ntransmitted-nreceived)*100.0) / + ntransmitted)); + } + (void)printf("\n"); + if (nreceived && (pingflags & (F_TIMING|F_TIMING64))) { + double n = nreceived + nrepeats; + double avg = (tsum / n); + double variance = 0.0; + const unsigned int prec = (pingflags & F_TIMING64) != 0 ? 6 : 3; + if (n>1) + variance = (tsumsq - n*avg*avg) /(n-1); + + printf("round-trip min/avg/max/stddev = " + "%.*f/%.*f/%.*f/%.*f ms\n", + prec, tmin * 1000.0, + prec, avg * 1000.0, + prec, tmax * 1000.0, + prec, sqrt(variance) * 1000.0); + if (pingflags & F_FLOOD) { + double r = diffsec(&last_rx, &first_rx); + double t = diffsec(&last_tx, &first_tx); + if (r == 0) + r = 0.0001; + if (t == 0) + t = 0.0001; + (void)printf(" %.1f packets/sec sent, " + " %.1f packets/sec received\n", + ntransmitted/t, nreceived/r); + } + } +} + + +/* + * Print statistics when SIGINFO is received. + */ +/* ARGSUSED */ +static void +prtsig(int dummy) +{ + + summary(0); +#ifndef SIGINFO + (void)signal(SIGQUIT, prtsig); +#endif +} + + +/* + * On the first SIGINT, allow any outstanding packets to dribble in + */ +static void +prefinish(int dummy) +{ + if (lastrcvd /* quit now if caught up */ + || nreceived == 0) /* or if remote is dead */ + finish(0); + + (void)signal(dummy, finish); /* do this only the 1st time */ + + if (npackets > ntransmitted) /* let the normal limit work */ + npackets = ntransmitted; +} + +/* + * Print statistics and give up. + */ +/* ARGSUSED */ +static void +finish(int dummy) +{ +#ifdef SIGINFO + (void)signal(SIGINFO, SIG_DFL); +#else + (void)signal(SIGQUIT, SIG_DFL); +#endif + + summary(1); + exit(nreceived > 0 ? 0 : 2); +} + + +static int /* 0=do not print it */ +ck_pr_icmph(struct icmp *icp, + struct sockaddr_in *from, + int cc, + int override) /* 1=override VERBOSE if interesting */ +{ + int hlen; + struct ip ipb, *ip = &ipb; + struct icmp icp2b, *icp2 = &icp2b; + int res; + + if (pingflags & F_VERBOSE) { + res = 1; + jiggle_flush(1); + } else { + res = 0; + } + + (void) memcpy(ip, icp->icmp_data, sizeof(*ip)); + hlen = ip->ip_hl << 2; + if (ip->ip_p == IPPROTO_ICMP + && hlen + 6 <= cc) { + (void) memcpy(icp2, &icp->icmp_data[hlen], sizeof(*icp2)); + if (icp2->icmp_id == ident) { + /* remember to clear route cached in kernel + * if this non-Echo-Reply ICMP message was for one + * of our packets. + */ + clear_cache.tv_sec = 0; + + if (!res && override + && (pingflags & (F_QUIET|F_SEMI_QUIET)) == 0) { + jiggle_flush(1); + (void)printf("%d bytes from %s: ", + cc, pr_addr(&from->sin_addr)); + res = 1; + } + } + } + + return res; +} + + +/* + * Print a descriptive string about an ICMP header other than an echo reply. + */ +static int /* 0=printed nothing */ +pr_icmph(struct icmp *icp, + struct sockaddr_in *from, + int cc) +{ + switch (icp->icmp_type ) { + case ICMP_UNREACH: + if (!ck_pr_icmph(icp, from, cc, 1)) + return 0; + switch (icp->icmp_code) { + case ICMP_UNREACH_NET: + (void)printf("Destination Net Unreachable"); + break; + case ICMP_UNREACH_HOST: + (void)printf("Destination Host Unreachable"); + break; + case ICMP_UNREACH_PROTOCOL: + (void)printf("Destination Protocol Unreachable"); + break; + case ICMP_UNREACH_PORT: + (void)printf("Destination Port Unreachable"); + break; + case ICMP_UNREACH_NEEDFRAG: + (void)printf("frag needed and DF set. Next MTU=%d", + ntohs(icp->icmp_nextmtu)); + break; + case ICMP_UNREACH_SRCFAIL: + (void)printf("Source Route Failed"); + break; + case ICMP_UNREACH_NET_UNKNOWN: + (void)printf("Unreachable unknown net"); + break; + case ICMP_UNREACH_HOST_UNKNOWN: + (void)printf("Unreachable unknown host"); + break; + case ICMP_UNREACH_ISOLATED: + (void)printf("Unreachable host isolated"); + break; + case ICMP_UNREACH_NET_PROHIB: + (void)printf("Net prohibited access"); + break; + case ICMP_UNREACH_HOST_PROHIB: + (void)printf("Host prohibited access"); + break; + case ICMP_UNREACH_TOSNET: + (void)printf("Bad TOS for net"); + break; + case ICMP_UNREACH_TOSHOST: + (void)printf("Bad TOS for host"); + break; + case 13: + (void)printf("Communication prohibited"); + break; + case 14: + (void)printf("Host precedence violation"); + break; + case 15: + (void)printf("Precedence cutoff"); + break; + default: + (void)printf("Bad Destination Unreachable Code: %d", + icp->icmp_code); + break; + } + /* Print returned IP header information */ + pr_retip(icp, cc); + break; + + case ICMP_SOURCEQUENCH: + if (!ck_pr_icmph(icp, from, cc, 1)) + return 0; + (void)printf("Source Quench"); + pr_retip(icp, cc); + break; + + case ICMP_REDIRECT: + if (!ck_pr_icmph(icp, from, cc, 1)) + return 0; + switch (icp->icmp_code) { + case ICMP_REDIRECT_NET: + (void)printf("Redirect Network"); + break; + case ICMP_REDIRECT_HOST: + (void)printf("Redirect Host"); + break; + case ICMP_REDIRECT_TOSNET: + (void)printf("Redirect Type of Service and Network"); + break; + case ICMP_REDIRECT_TOSHOST: + (void)printf("Redirect Type of Service and Host"); + break; + default: + (void)printf("Redirect--Bad Code: %d", icp->icmp_code); + break; + } + (void)printf(" New router addr: %s", + pr_addr(&icp->icmp_hun.ih_gwaddr)); + pr_retip(icp, cc); + break; + + case ICMP_ECHO: + if (!ck_pr_icmph(icp, from, cc, 0)) + return 0; + (void)printf("Echo Request: ID=%d seq=%d", + ntohs(icp->icmp_id), ntohs(icp->icmp_seq)); + break; + + case ICMP_ECHOREPLY: + /* displaying other's pings is too noisey */ +#if 0 + if (!ck_pr_icmph(icp, from, cc, 0)) + return 0; + (void)printf("Echo Reply: ID=%d seq=%d", + ntohs(icp->icmp_id), ntohs(icp->icmp_seq)); + break; +#else + return 0; +#endif + + case ICMP_ROUTERADVERT: + if (!ck_pr_icmph(icp, from, cc, 0)) + return 0; + (void)printf("Router Discovery Advert"); + break; + + case ICMP_ROUTERSOLICIT: + if (!ck_pr_icmph(icp, from, cc, 0)) + return 0; + (void)printf("Router Discovery Solicit"); + break; + + case ICMP_TIMXCEED: + if (!ck_pr_icmph(icp, from, cc, 1)) + return 0; + switch (icp->icmp_code ) { + case ICMP_TIMXCEED_INTRANS: + (void)printf("Time To Live exceeded"); + break; + case ICMP_TIMXCEED_REASS: + (void)printf("Frag reassembly time exceeded"); + break; + default: + (void)printf("Time exceeded, Bad Code: %d", + icp->icmp_code); + break; + } + pr_retip(icp, cc); + break; + + case ICMP_PARAMPROB: + if (!ck_pr_icmph(icp, from, cc, 1)) + return 0; + (void)printf("Parameter problem: pointer = 0x%02x", + icp->icmp_hun.ih_pptr); + pr_retip(icp, cc); + break; + + case ICMP_TSTAMP: + if (!ck_pr_icmph(icp, from, cc, 0)) + return 0; + (void)printf("Timestamp"); + break; + + case ICMP_TSTAMPREPLY: + if (!ck_pr_icmph(icp, from, cc, 0)) + return 0; + (void)printf("Timestamp Reply"); + break; + + case ICMP_IREQ: + if (!ck_pr_icmph(icp, from, cc, 0)) + return 0; + (void)printf("Information Request"); + break; + + case ICMP_IREQREPLY: + if (!ck_pr_icmph(icp, from, cc, 0)) + return 0; + (void)printf("Information Reply"); + break; + + case ICMP_MASKREQ: + if (!ck_pr_icmph(icp, from, cc, 0)) + return 0; + (void)printf("Address Mask Request"); + break; + + case ICMP_MASKREPLY: + if (!ck_pr_icmph(icp, from, cc, 0)) + return 0; + (void)printf("Address Mask Reply"); + break; + + default: + if (!ck_pr_icmph(icp, from, cc, 0)) + return 0; + (void)printf("Bad ICMP type: %d", icp->icmp_type); + if (pingflags & F_VERBOSE) + pr_iph(icp, cc); + } + + return 1; +} + + +/* + * Print an IP header with options. + */ +static void +pr_iph(struct icmp *icp, + int cc) +{ + int hlen; + u_char *cp; + struct ip ipb, *ip = &ipb; + + (void) memcpy(ip, icp->icmp_data, sizeof(*ip)); + + hlen = ip->ip_hl << 2; + cp = (u_char *) &icp->icmp_data[20]; /* point to options */ + + (void)printf("\n Vr HL TOS Len ID Flg off TTL Pro cks Src Dst\n"); + (void)printf(" %1x %1x %02x %04x %04x", + ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); + (void)printf(" %1x %04x", + ((ip->ip_off)&0xe000)>>13, (ip->ip_off)&0x1fff); + (void)printf(" %02x %02x %04x", + ip->ip_ttl, ip->ip_p, ip->ip_sum); + (void)printf(" %15s ", + inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); + (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); + /* dump any option bytes */ + while (hlen-- > 20 && cp < (u_char*)icp+cc) { + (void)printf("%02x", *cp++); + } +} + +/* + * Print an ASCII host address starting from a string of bytes. + */ +static void +pr_saddr(u_char *cp) +{ + n_long l; + struct in_addr addr; + + l = (u_char)*++cp; + l = (l<<8) + (u_char)*++cp; + l = (l<<8) + (u_char)*++cp; + l = (l<<8) + (u_char)*++cp; + addr.s_addr = htonl(l); + (void)printf("\t%s", (l == 0) ? "0.0.0.0" : pr_addr(&addr)); +} + + +/* + * Return an ASCII host address + * as a dotted quad and optionally with a hostname + */ +static char * +pr_addr(struct in_addr *addr) /* in network order */ +{ + struct hostent *hp; + static char buf[MAXHOSTNAMELEN+4+16+1]; + + if ((pingflags & F_NUMERIC) + || !(hp = gethostbyaddr((char *)addr, sizeof(*addr), AF_INET))) { + (void)snprintf(buf, sizeof(buf), "%s", inet_ntoa(*addr)); + } else { + (void)snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name, + inet_ntoa(*addr)); + } + + return buf; +} + +/* + * Dump some info on a returned (via ICMP) IP packet. + */ +static void +pr_retip(struct icmp *icp, + int cc) +{ + int hlen; + u_char *cp; + struct ip ipb, *ip = &ipb; + + (void) memcpy(ip, icp->icmp_data, sizeof(*ip)); + + if (pingflags & F_VERBOSE) + pr_iph(icp, cc); + + hlen = ip->ip_hl << 2; + cp = (u_char *) &icp->icmp_data[hlen]; + + if (ip->ip_p == IPPROTO_TCP) { + if (pingflags & F_VERBOSE) + (void)printf("\n TCP: from port %u, to port %u", + (*cp*256+*(cp+1)), (*(cp+2)*256+*(cp+3))); + } else if (ip->ip_p == IPPROTO_UDP) { + if (pingflags & F_VERBOSE) + (void)printf("\n UDP: from port %u, to port %u", + (*cp*256+*(cp+1)), (*(cp+2)*256+*(cp+3))); + } else if (ip->ip_p == IPPROTO_ICMP) { + struct icmp icp2; + (void) memcpy(&icp2, cp, sizeof(icp2)); + if (icp2.icmp_type == ICMP_ECHO) { + if (pingflags & F_VERBOSE) + (void)printf("\n ID=%u icmp_seq=%u", + ntohs((u_int16_t)icp2.icmp_id), + ntohs((u_int16_t)icp2.icmp_seq)); + else + (void)printf(" for icmp_seq=%u", + ntohs((u_int16_t)icp2.icmp_seq)); + } + } +} + +static void +fill(void) +{ + int i, j, k; + char *cp; + int pat[16]; + + for (cp = fill_pat; *cp != '\0'; cp++) { + if (!isxdigit((unsigned char)*cp)) + break; + } + if (cp == fill_pat || *cp != '\0' || (cp-fill_pat) > 16*2) { + (void)fflush(stdout); + errx(1, "\"-p %s\": patterns must be specified with" + " 1-32 hex digits\n", + fill_pat); + } + + i = sscanf(fill_pat, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", + &pat[0], &pat[1], &pat[2], &pat[3], + &pat[4], &pat[5], &pat[6], &pat[7], + &pat[8], &pat[9], &pat[10], &pat[11], + &pat[12], &pat[13], &pat[14], &pat[15]); + + for (k = phdrlen, j = 0; k <= datalen; k++) { + opack_icmp.icmp_data[k] = pat[j]; + if (++j >= i) + j = 0; + } + + if (!(pingflags & F_QUIET)) { + (void)printf("PATTERN: 0x"); + for (j=0; j>24; + } +} + + +static void +gethost(const char *arg, + const char *name, + struct sockaddr_in *sa, + char *realname, + int realname_len) +{ + struct hostent *hp; + + (void)memset(sa, 0, sizeof(*sa)); + sa->sin_family = AF_INET; + sa->sin_len = sizeof(struct sockaddr_in); + + /* If it is an IP address, try to convert it to a name to + * have something nice to display. + */ + if (inet_aton(name, &sa->sin_addr) != 0) { + if (realname) { + if (pingflags & F_NUMERIC) + hp = 0; + else + hp = gethostbyaddr((char *)&sa->sin_addr, + sizeof(sa->sin_addr), + AF_INET); + (void)strlcpy(realname, hp ? hp->h_name : name, + realname_len); + } + return; + } + + hp = gethostbyname(name); + if (!hp) + errx(1, "Cannot resolve \"%s\" (%s)",name,hstrerror(h_errno)); + + if (hp->h_addrtype != AF_INET) + errx(1, "%s only supported with IP", arg); + + (void)memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr)); + + if (realname) + (void)strlcpy(realname, hp->h_name, realname_len); +} + + +static void +usage(void) +{ +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC +#define IPSECOPT "\n [-E policy] " +#else +#define IPSECOPT "\n [-AE] " +#endif /*IPSEC_POLICY_IPSEC*/ +#else +#define IPSECOPT "" +#endif /*IPSEC*/ + + (void)fprintf(stderr, "usage: \n" + "%s [-aCDdfLnoPQqRrv] [-c count] [-g gateway] [-h host]" + " [-I addr] [-i interval]\n" + " [-l preload] [-p pattern] [-s size] [-T ttl] [-t tos]" + " [-w maxwait] " IPSECOPT "host\n", + getprogname()); + exit(1); +} diff --git a/sbin/ping/ping_hostops.c b/sbin/ping/ping_hostops.c new file mode 100644 index 000000000..72b1f82fb --- /dev/null +++ b/sbin/ping/ping_hostops.c @@ -0,0 +1,53 @@ +/* $NetBSD: ping_hostops.c,v 1.2 2011/03/11 09:59:56 pooka Exp $ */ + +/* + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * 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 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 +#ifndef lint +__RCSID("$NetBSD: ping_hostops.c,v 1.2 2011/03/11 09:59:56 pooka Exp $"); +#endif /* !lint */ + +#include +#include +#include + +#include +#include + +#include "prog_ops.h" + +const struct prog_ops prog_ops = { + .op_socket = socket, + .op_setsockopt = setsockopt, + .op_shutdown = shutdown, + .op_poll = poll, + .op_recvfrom = recvfrom, + .op_sendto = sendto, + .op_close = close, + .op_getuid = getuid, + .op_setuid = setuid, +}; diff --git a/sbin/ping/ping_rumpops.c b/sbin/ping/ping_rumpops.c new file mode 100644 index 000000000..7e2f21144 --- /dev/null +++ b/sbin/ping/ping_rumpops.c @@ -0,0 +1,58 @@ +/* $NetBSD: ping_rumpops.c,v 1.2 2011/03/11 09:59:56 pooka Exp $ */ + +/* + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * 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 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 +#ifndef lint +__RCSID("$NetBSD: ping_rumpops.c,v 1.2 2011/03/11 09:59:56 pooka Exp $"); +#endif /* !lint */ + +#include +#include + +#include +#include + +#include +#include +#include + +#include "prog_ops.h" + +const struct prog_ops prog_ops = { + .op_init = rumpclient_init, + + .op_socket = rump_sys_socket, + .op_setsockopt= rump_sys_setsockopt, + .op_shutdown = rump_sys_shutdown, + .op_poll = rump_sys_poll, + .op_sendto = rump_sys_sendto, + .op_recvfrom = rump_sys_recvfrom, + .op_close = rump_sys_close, + .op_getuid = rump_sys_getuid, + .op_setuid = rump_sys_setuid, +}; diff --git a/sbin/ping/prog_ops.h b/sbin/ping/prog_ops.h new file mode 100644 index 000000000..ccc5a7066 --- /dev/null +++ b/sbin/ping/prog_ops.h @@ -0,0 +1,79 @@ +/* $NetBSD: prog_ops.h,v 1.3 2011/03/11 09:59:56 pooka Exp $ */ + +/* + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * 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 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. + */ + +#ifndef _PROG_OPS_H_ +#define _PROG_OPS_H_ + +#include + +#ifndef CRUNCHOPS +struct prog_ops { + int (*op_init)(void); + + int (*op_socket)(int, int, int); + int (*op_setsockopt)(int, int, int, const void *, socklen_t); + int (*op_shutdown)(int, int); + + int (*op_poll)(struct pollfd *, nfds_t, int); + + ssize_t (*op_recvfrom)(int, void *, size_t, int, + struct sockaddr *, socklen_t *); + ssize_t (*op_sendto)(int, const void *, size_t, int, + const struct sockaddr *, socklen_t); + + int (*op_close)(int); + + uid_t (*op_getuid)(void); + int (*op_setuid)(uid_t); +}; +extern const struct prog_ops prog_ops; + +#define prog_init prog_ops.op_init +#define prog_socket prog_ops.op_socket +#define prog_setsockopt prog_ops.op_setsockopt +#define prog_shutdown prog_ops.op_shutdown +#define prog_poll prog_ops.op_poll +#define prog_recvfrom prog_ops.op_recvfrom +#define prog_sendto prog_ops.op_sendto +#define prog_close prog_ops.op_close +#define prog_getuid prog_ops.op_getuid +#define prog_setuid prog_ops.op_setuid +#else +#define prog_init ((int (*)(void))NULL) +#define prog_socket socket +#define prog_setsockopt setsockopt +#define prog_shutdown shutdown +#define prog_poll poll +#define prog_recvfrom recvfrom +#define prog_sendto sendto +#define prog_close close +#define prog_getuid getuid +#define prog_setuid setuid +#endif + +#endif /* _PROG_OPS_H_ */ diff --git a/sys/net/Makefile b/sys/net/Makefile index 751f0734c..3870aabca 100644 --- a/sys/net/Makefile +++ b/sys/net/Makefile @@ -6,6 +6,8 @@ INCSDIR= /usr/include/net INCS= ethertypes.h \ if_ether.h \ + radix.h \ + route.h \ \ \ \ diff --git a/sys/netinet/Makefile b/sys/netinet/Makefile index 26e610888..d8bf7e6a6 100644 --- a/sys/netinet/Makefile +++ b/sys/netinet/Makefile @@ -7,8 +7,10 @@ INCS= \ in_systm.h \ \ ip.h \ + ip_icmp.h \ + ip_var.h \ tcp.h \ - \ + udp.h udp_var.h \ .if !defined(__MINIX) diff --git a/sys/netinet/ip_icmp.h b/sys/netinet/ip_icmp.h new file mode 100644 index 000000000..2cdfdbbb9 --- /dev/null +++ b/sys/netinet/ip_icmp.h @@ -0,0 +1,253 @@ +/* $NetBSD: ip_icmp.h,v 1.33 2011/12/24 20:18:54 christos Exp $ */ + +/* + * Copyright (c) 1982, 1986, 1993 + * 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. 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 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. + * + * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef _NETINET_IP_ICMP_H_ +#define _NETINET_IP_ICMP_H_ + + +/* + * Interface Control Message Protocol Definitions. + * Per RFC 792, September 1981. + */ + +/* + * Internal of an ICMP Router Advertisement + */ +struct icmp_ra_addr { + u_int32_t ira_addr; + u_int32_t ira_preference; +} __packed; + +/* + * Structure of an icmp header. + */ +struct icmp { + u_int8_t icmp_type; /* type of message, see below */ + u_int8_t icmp_code; /* type sub code */ + u_int16_t icmp_cksum; /* ones complement cksum of struct */ + union { + u_int8_t ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ + struct ih_idseq { + n_short icd_id; + n_short icd_seq; + } ih_idseq __packed; + int32_t ih_void; + + /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ + struct ih_pmtu { + n_short ipm_void; + n_short ipm_nextmtu; + } ih_pmtu __packed; + struct ih_rtradv { + u_int8_t irt_num_addrs; + u_int8_t irt_wpa; + u_int16_t irt_lifetime; + } ih_rtradv __packed; + } icmp_hun /* XXX __packed ??? */; +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu +#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs +#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa +#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime + union { + struct id_ts { + n_time its_otime; + n_time its_rtime; + n_time its_ttime; + } id_ts __packed; + struct id_ip { + struct ip idi_ip; + /* options and then 64 bits of data */ + } id_ip /* XXX: __packed ??? */; + struct icmp_ra_addr id_radv; + u_int32_t id_mask; + int8_t id_data[1]; + } icmp_dun /* XXX __packed ??? */; +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_radv icmp_dun.id_mask +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data +}; + +/* + * Lower bounds on packet lengths for various types. + * For the error advice packets must first insure that the + * packet is large enought to contain the returned ip header. + * Only then can we do the check to see if 64 bits of packet + * data have been returned, since we need to check the returned + * ip header length. + */ +#define ICMP_MINLEN 8 /* abs minimum */ +#define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */ +#define ICMP_MASKLEN 12 /* address mask */ +#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */ +#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8) + /* N.B.: must separately check that ip_hl >= 5 */ + +/* + * Definition of type and code field values. + */ +#define ICMP_ECHOREPLY 0 /* echo reply */ +#define ICMP_UNREACH 3 /* dest unreachable, codes: */ +#define ICMP_UNREACH_NET 0 /* bad net */ +#define ICMP_UNREACH_HOST 1 /* bad host */ +#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */ +#define ICMP_UNREACH_PORT 3 /* bad port */ +#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */ +#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */ +#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */ +#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */ +#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */ +#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */ +#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */ +#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */ +#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */ +#define ICMP_UNREACH_ADMIN_PROHIBIT 13 /* communication + administratively + prohibited */ +#define ICMP_UNREACH_HOST_PREC 14 /* host precedence + violation */ +#define ICMP_UNREACH_PREC_CUTOFF 15 /* precedence cutoff */ +#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */ +#define ICMP_REDIRECT 5 /* shorter route, codes: */ +#define ICMP_REDIRECT_NET 0 /* for network */ +#define ICMP_REDIRECT_HOST 1 /* for host */ +#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */ +#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */ +#define ICMP_ALTHOSTADDR 6 /* alternative host address */ +#define ICMP_ECHO 8 /* echo service */ +#define ICMP_ROUTERADVERT 9 /* router advertisement */ +#define ICMP_ROUTERADVERT_NORMAL 0 +#define ICMP_ROUTERADVERT_NOROUTE 16 +#define ICMP_ROUTERSOLICIT 10 /* router solicitation */ +#define ICMP_TIMXCEED 11 /* time exceeded, code: */ +#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */ +#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */ +#define ICMP_PARAMPROB 12 /* ip header bad */ +#define ICMP_PARAMPROB_ERRATPTR 0 +#define ICMP_PARAMPROB_OPTABSENT 1 +#define ICMP_PARAMPROB_LENGTH 2 +#define ICMP_TSTAMP 13 /* timestamp request */ +#define ICMP_TSTAMPREPLY 14 /* timestamp reply */ +#define ICMP_IREQ 15 /* information request */ +#define ICMP_IREQREPLY 16 /* information reply */ +#define ICMP_MASKREQ 17 /* address mask request */ +#define ICMP_MASKREPLY 18 /* address mask reply */ +#define ICMP_TRACEROUTE 30 /* traceroute */ +#define ICMP_DATACONVERR 31 /* data conversion error */ +#define ICMP_MOBILE_REDIRECT 32 /* mobile redirect */ +#define ICMP_IPV6_WHEREAREYOU 33 /* ipv6 where are you */ +#define ICMP_IPV6_IAMHERE 34 /* ipv6 i am here */ +#define ICMP_MOBILE_REGREQUEST 35 /* mobile registration req */ +#define ICMP_MOBILE_REGREPLY 36 /* mobile registration reply */ +#define ICMP_SKIP 39 /* SKIP */ +#define ICMP_PHOTURIS 40 /* security */ +#define ICMP_PHOTURIS_UNKNOWN_INDEX 0 /* unknown sec index */ +#define ICMP_PHOTURIS_AUTH_FAILED 1 /* auth failed */ +#define ICMP_PHOTURIS_DECOMPRESS_FAILED 2 /* decompress failed */ +#define ICMP_PHOTURIS_DECRYPT_FAILED 3 /* decrypt failed */ +#define ICMP_PHOTURIS_NEED_AUTHN 4 /* no authentication */ +#define ICMP_PHOTURIS_NEED_AUTHZ 5 /* no authorization */ + +#define ICMP_MAXTYPE 40 + +#ifdef ICMP_STRINGS +static const char *icmp_type[] = { + "echoreply", "unassigned_1", "unassigned_2", "unreach", + "sourcequench", "redirect", "althostaddr", "unassigned_7", + "echo", "routeradvert", "routersolicit", "timxceed", + "paramprob", "tstamp", "tstampreply", "ireq", + "ireqreply", "maskreq", "maskreply", "reserved_19", + "reserved_20", "reserved_21", "reserved_22", "reserved_23", + "reserved_24", "reserved_25", "reserved_26", "reserved_27", + "reserved_28", "reserved_29", "traceroute", "dataconverr", + "mobile_redirect", "ipv6_whereareyou", "ipv6_iamhere", + "mobile_regrequest", "mobile_regreply", "reserved_37", + "reserved_38", "skip", "photuris", NULL +}; +static const char *icmp_code_none[] = { "none", NULL }; +static const char *icmp_code_unreach[] = { + "net", "host", "oprt", "needfrag", "srcfail", "net_unknown", + "host_unknown", "isolated", "net_prohib", "host_prohib", + "tosnet", "toshost", "admin_prohibit", "host_prec", "prec_cutoff", NULL +}; +static const char *icmp_code_redirect[] = { + "net", "host", "tosnet", "toshost", NULL +}; +static const char *icmp_code_routeradvert[] = { + "normal", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", + "noroute", NULL +}; +static const char *icmp_code_timxceed[] = { + "intrans", "reass", NULL +}; +static const char *icmp_code_paramprob[] = { + "erratptr", "optabsent", "length", NULL +}; +static const char *icmp_code_photuris[] = { + "unknown_index", "auth_failed", "decompress_failed", + "decrypt_failed", "need_authn", "need_authz", NULL +}; +#endif + +#define ICMP_INFOTYPE(type) \ + ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \ + (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \ + (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \ + (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \ + (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY) + +#ifdef _KERNEL +void icmp_error(struct mbuf *, int, int, n_long, int); +void icmp_mtudisc(struct icmp *, struct in_addr); +void icmp_input(struct mbuf *, ...); +void icmp_init(void); +void icmp_reflect(struct mbuf *); +void icmp_send(struct mbuf *, struct mbuf *); +int icmp_sysctl(int *, u_int, void *, size_t *, void *, size_t); + +void icmp_mtudisc_callback_register(void (*)(struct in_addr)); +int icmp_ratelimit(const struct in_addr *, const int, const int); +#endif + + +#endif /* !_NETINET_IP_ICMP_H_ */ diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 68e48faac..93e7e707a 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -24,7 +24,7 @@ SUBDIR= \ \ rdate \ \ - \ + traceroute \ \ \ unlink user \ diff --git a/usr.sbin/traceroute/CHANGES b/usr.sbin/traceroute/CHANGES new file mode 100644 index 000000000..0fadff055 --- /dev/null +++ b/usr.sbin/traceroute/CHANGES @@ -0,0 +1,149 @@ +$NetBSD: CHANGES,v 1.2 2011/09/11 01:06:26 christos Exp $ +@(#) Id: CHANGES,v 1.23 2000/12/14 06:53:11 leres Exp (LBL) + +v1.4 Wed Dec 13 22:50:35 PST 2000 + +- Add an option to use icmp echo instead of udp datagrams. + +- Add HPUX and Linux support. + +- Handle hex argument values as suggested by John Hawkinson + (jhawk@mit.edu) + +- Added flags to set the first ttl and to set the don't fragment bit. + +- Add a local autoconf macro to check for routines in libraries; the + autoconf version is broken (it only puts the library name in the + cache variable name). Thanks to John Hawkinson. + +- Add a local autoconf macro to check for types; the autoconf version + is broken (it uses grep instead of actually compiling a code fragment). + +- Attempt to detect "egcs" versions of gcc. + +- Fix problems caused by savestr(). + +- Detect when there are more interfaces than we can deal with. Thanks + to Guy Harris guy@netapp.com. + +- Accommodate changes made to FreeBSD 3.2-RELEASE network headers files. + +- The ip header offset needs to be byte swapped under Solaris on the + intel as well. Reported by John McDermott (jjm@jkintl.com) Also byte + ip length and offset for linux. + +- In wait_for_reply(), use passed socket instead of global one. Thanks + to Richard Kettlewell (richard.kettlewell@kewill.com) + +- Check for IFF_LOOPBACK as a define or an enum (concession to linux). + Reported by Robert Bihlmeyer (robbe@orcus.priv.at) + +- Increase size of SIOCGIFCONF buffer to accommodate systems with lots + of virtual interfaces. Ignore sun virtual interfaces. Suggested by + Ian Donaldson (iand@aone.com.au) + +- Always calculate icmp checksums when using -I. Reported by Soumen + (sbiswas@novell.com) + +- Documentation fix for description of -p. Thanks to Jeffrey C Honig + (jch@bsdi.com) + +- Enable ip checksums for Solaris 2.6 and higher (since they seem to + work correctly now). + +- Avoid problems when broken routers return 0.0.0.0 as their source + address. Thanks to John Hawkinson (jhawk@bbnplanet.com) + +- Canonicalize hostname if gethostname() doesn't return a domain. + +- Add -z flag (pause msecs) to allow optional pause between probes. + Suggested by Dave Morrison (drmorris@uplanet.com) + +- Fix max packet length test. + +- Use "/dev/null" instead of "." for file descriptor fodder. + Suggested by Tim Robbins (fyre@box3n.gumbynet.org) + +- Watch for a NULL argv[0]. Suggested by Tim Robbins. + +- Be careful with hostname legnths. + +- Print RFC1191 Path MTU Discovery value on "needfrag" unreachables. + +- Limit port size to 16 bits. Suggested by Tim Robbins. + +- Limit wait time to 24 hours. + +- Modified linux specific struct ipovly definition to avoid problems + with 64 bit systems. Problem reported by Uros Prestor + (uros@turbolinux.com) + +- Use kernel routing tables to determine correct source address. + Three styles are supported: bsd/socket, solaris/mib and linux. + +- Fix configure to recognize older versions of Solaris. + +v1.3.2 Thu Sep 26 18:06:16 PDT 1996 + +- Rewrite source routing code to eliminate a number of problems on + systems using raw ip options. Also pad options with a noop so gateway + addresses are aligned. + +- Don't call inet_ntoa() twice in the same printf(). Thanks to NetBSD + via Bill Fenner (fenner@parc.xerox.com) + +- Decode "administratively prohibited filter" icmp code and print + numeric value for unknown codes. Thanks to Bill Fenner. + +v1.3.1 Wed Sep 18 21:08:16 PDT 1996 + +- Some systems (e.g. AIX) need sys/select.h. Thanks to Brett Hogden + (hogden@rge.com) + +- Byte swap ip header length under Solaris. (This has no effect on the + sparc but is required on the i386.) + +- Made optional packet length control total size of packet. + +v1.3 Mon Sep 16 14:55:44 PDT 1996 + +- Overrun buffer security fixes. Thanks to Bill Fenner + (fenner@parc.xerox.com) + +- Wait for response packet relative to start of probe. Thanks to Bill + Fenner. + +- Fix bug that prevented changing the packet size. Thanks to Gregory + Decker (gdecker@nate.dcrt.nih.gov) + +- Add support for RAW_OPTIONS (e.g. 4.4 BSD systems such as BSD/OS and + FreeBSD) thanks to Jeffrey C Honig (jch@bsdi.com) + +- Remove ip header byte swap fix from v1.2; most kernels swap the ip + header length in the kernel (and it causes OSF3 to crash). + +- Fix to not exit when the number of probes is set to 1 (i.e. "-q 1") + +- Improve autoconf configuration. + +v1.2 Tue Oct 17 23:50:05 PDT 1995 + +- Convert to autoconf and ansify. + +- Byte swap ip header length for little endian machines. Fix thanks to + Y Badri (yb@greybox.demon.co.uk). + +v1.1 Thu Jun 15 02:32:55 PDT 1995 + +- Check for too many arguments. + +- Recode to make timing of packet's round trip more obvious and to + tighten up code. + +- Ifdef IP_OPTIONS code. + +- Display time in microseconds. + +v1.0 Tue Feb 28 23:50:05 PDT 1989 + +- Initial public release. diff --git a/usr.sbin/traceroute/Makefile b/usr.sbin/traceroute/Makefile new file mode 100644 index 000000000..4685ef6b0 --- /dev/null +++ b/usr.sbin/traceroute/Makefile @@ -0,0 +1,29 @@ +# $NetBSD: Makefile,v 1.19 2011/09/11 01:06:26 christos Exp $ + +USE_FORT?= yes # network client + +RUMPPRG=traceroute +MAN= traceroute.8 + +CPPFLAGS+=-DHAVE_MALLOC_H=1 -DHAVE_SYS_SELECT_H=1 -DHAVE_SYS_SOCKIO_H=1 +CPPFLAGS+=-DHAVE_STRERROR=1 -DHAVE_SETLINEBUF=1 -DHAVE_SOCKADDR_SA_LEN=1 +CPPFLAGS+=-DHAVE_RAW_OPTIONS=1 +CPPFLAGS+=-DHAVE_ICMP_NEXTMTU=1 + +#CPPFLAGS+=-DIPSEC +#LDADD+= -lipsec +#DPADD+= ${LIBIPSEC} + +BINOWN= root +BINMODE=4555 + +SRCS= traceroute.c ifaddrlist.c +SRCS+= version.c as.c + +.PATH: ${.CURDIR}/../../lib/libc/net +RUMPSRCS= getifaddrs.c +CPPFLAGS+= -DRUMP_ACTION + +AWKS= median.awk mean.awk + +.include diff --git a/usr.sbin/traceroute/as.c b/usr.sbin/traceroute/as.c new file mode 100644 index 000000000..380837271 --- /dev/null +++ b/usr.sbin/traceroute/as.c @@ -0,0 +1,224 @@ +/* $NetBSD: as.c,v 1.4 2011/05/10 01:52:49 christos 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + if (asn) { + 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 000000000..324b89f58 --- /dev/null +++ b/usr.sbin/traceroute/as.h @@ -0,0 +1,34 @@ +/* $NetBSD: as.h,v 1.4 2011/05/10 01:52:49 christos 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/gnuc.h b/usr.sbin/traceroute/gnuc.h new file mode 100644 index 000000000..aadb5fa01 --- /dev/null +++ b/usr.sbin/traceroute/gnuc.h @@ -0,0 +1,36 @@ +/* $NetBSD: gnuc.h,v 1.2 2002/07/06 21:51:49 wiz Exp $ */ + +/* @(#) Header: gnuc.h,v 1.3 95/10/09 02:47:01 leres Exp (LBL) */ + +/* inline foo */ +#ifdef __GNUC__ +#define inline __inline +#else +#define inline +#endif + +/* + * Handle new and old "dead" routine prototypes + * + * For example: + * + * __dead void foo(void) __attribute__((volatile)); + * + */ +#ifdef __GNUC__ +#ifndef __dead +#define __dead volatile +#endif +#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) +#ifndef __attribute__ +#define __attribute__(args) +#endif +#endif +#else +#ifndef __dead +#define __dead +#endif +#ifndef __attribute__ +#define __attribute__(args) +#endif +#endif diff --git a/usr.sbin/traceroute/ifaddrlist.c b/usr.sbin/traceroute/ifaddrlist.c new file mode 100644 index 000000000..170f397b6 --- /dev/null +++ b/usr.sbin/traceroute/ifaddrlist.c @@ -0,0 +1,146 @@ +/* $NetBSD: ifaddrlist.c,v 1.10 2011/09/11 01:06:26 christos Exp $ */ + +/* + * 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 +#ifndef lint +#if 0 +static const char rcsid[] = + "@(#) Header: ifaddrlist.c,v 1.2 97/04/22 13:31:05 leres Exp (LBL)"; + "@(#) Id: ifaddrlist.c,v 1.9 2000/11/23 20:01:55 leres Exp (LBL)"; +#else +__RCSID("$NetBSD: ifaddrlist.c,v 1.10 2011/09/11 01:06:26 christos Exp $"); +#endif +#endif + +#include +#include +#include +#include +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif +#include /* concession to AIX */ + +struct mbuf; +struct rtentry; + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gnuc.h" +#ifdef HAVE_OS_PROTO_H +#include "os-proto.h" +#endif + +#include "ifaddrlist.h" + +/* Not all systems have IFF_LOOPBACK */ +#ifdef IFF_LOOPBACK +#define ISLOOPBACK(p) ((p)->ifa_flags & IFF_LOOPBACK) +#else +#define ISLOOPBACK(p) (strcmp((p)->ifa_name, "lo0") == 0) +#endif + +/* + * Return the interface list + */ +ssize_t +ifaddrlist(struct ifaddrlist **ipaddrp, char *errbuf, size_t buflen) +{ + struct sockaddr_in *sin; + struct ifaddrs *ifap = NULL, *ifa; + struct ifaddrlist *al = NULL, *nal; + size_t i = 0, maxal = 10; + + if (getifaddrs(&ifap) != 0) + goto out; + + if ((al = malloc(maxal * sizeof(*al))) == NULL) + goto out; + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + + /* Must be up */ + if ((ifa->ifa_flags & IFF_UP) == 0) + continue; + + /* + * Must not be a loopback address (127/8) + */ + sin = (struct sockaddr_in *)ifa->ifa_addr; + if (ISLOOPBACK(ifa)) + if (ntohl(sin->sin_addr.s_addr) == INADDR_LOOPBACK) + continue; + + if (i == maxal) { + maxal <<= 1; + if ((nal = realloc(al, maxal * sizeof(*al))) == NULL) + goto out; + al = nal; + } + + al[i].addr = sin->sin_addr.s_addr; + if ((al[i].device = strdup(ifa->ifa_name)) == NULL) + goto out; + i++; + } + if ((nal = realloc(al, i * sizeof(*al))) == NULL) + goto out; + freeifaddrs(ifap); + *ipaddrp = nal; + return (ssize_t)i; +out: + if (ifap) + freeifaddrs(ifap); + if (al) { + while (i > 0) + free(al[--i].device); + free(al); + } + (void)snprintf(errbuf, buflen, "%s: %s", __func__, strerror(errno)); + return -1; +} diff --git a/usr.sbin/traceroute/ifaddrlist.h b/usr.sbin/traceroute/ifaddrlist.h new file mode 100644 index 000000000..7e5427900 --- /dev/null +++ b/usr.sbin/traceroute/ifaddrlist.h @@ -0,0 +1,31 @@ +/* $NetBSD: ifaddrlist.h,v 1.3 2011/05/11 00:38:28 christos Exp $ */ + +/* + * 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 { + uint32_t addr; + char *device; +}; + +ssize_t ifaddrlist(struct ifaddrlist **, char *, size_t); diff --git a/usr.sbin/traceroute/mean.awk b/usr.sbin/traceroute/mean.awk new file mode 100644 index 000000000..75cc421a6 --- /dev/null +++ b/usr.sbin/traceroute/mean.awk @@ -0,0 +1,14 @@ +#!/bin/awk -f +# $NetBSD: mean.awk,v 1.5 1997/10/04 16:31:29 christos Exp $ +/^ *[0-9]/ { + # print out the average time to each hop along a route. + tottime = 0; n = 0; + for (f = 5; f <= NF; ++f) { + if ($f == "ms") { + tottime += $(f - 1) + ++n + } + } + if (n > 0) + print $1, tottime/n, median +} diff --git a/usr.sbin/traceroute/median.awk b/usr.sbin/traceroute/median.awk new file mode 100644 index 000000000..672e1150e --- /dev/null +++ b/usr.sbin/traceroute/median.awk @@ -0,0 +1,31 @@ +#!/bin/awk -f +# $NetBSD: median.awk,v 1.5 1997/10/04 16:31:32 christos Exp $ +/^ *[0-9]/ { + # print out the median time to each hop along a route. + tottime = 0; n = 0; + for (f = 5; f <= NF; ++f) { + if ($f == "ms") { + ++n + time[n] = $(f - 1) + } + } + if (n > 0) { + # insertion sort the times to find the median + for (i = 2; i <= n; ++i) { + v = time[i]; j = i - 1; + while (time[j] > v) { + time[j+1] = time[j]; + j = j - 1; + if (j < 0) + break; + } + time[j+1] = v; + } + if (n > 1 && (n % 2) == 0) + median = (time[n/2] + time[(n/2) + 1]) / 2 + else + median = time[(n+1)/2] + + print $1, median + } +} diff --git a/usr.sbin/traceroute/prog_ops.h b/usr.sbin/traceroute/prog_ops.h new file mode 100644 index 000000000..9811d6f26 --- /dev/null +++ b/usr.sbin/traceroute/prog_ops.h @@ -0,0 +1,72 @@ +/* $NetBSD: prog_ops.h,v 1.1 2010/12/15 00:09:41 pooka Exp $ */ + +/* + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * 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 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. + */ + +#ifndef _PROG_OPS_H_ +#define _PROG_OPS_H_ + +#include +#include +#include + +struct prog_ops { + int (*op_init)(void); + + int (*op_socket)(int, int, int); + int (*op_setsockopt)(int, int, int, const void *, socklen_t); + int (*op_shutdown)(int, int); + + int (*op_poll)(struct pollfd *, nfds_t, int); + + ssize_t (*op_recvfrom)(int, void *, size_t, int, + struct sockaddr *, socklen_t *); + ssize_t (*op_sendto)(int, const void *, size_t, int, + const struct sockaddr *, socklen_t); + + int (*op_close)(int); + + int (*op_connect)(int, const struct sockaddr *, socklen_t); + int (*op_getsockname)(int, struct sockaddr *, socklen_t *); + + int (*op_sysctl)(const int *, u_int, void *, size_t *, + const void *, size_t); +}; +extern const struct prog_ops prog_ops; + +#define prog_init prog_ops.op_init +#define prog_socket prog_ops.op_socket +#define prog_setsockopt prog_ops.op_setsockopt +#define prog_shutdown prog_ops.op_shutdown +#define prog_poll prog_ops.op_poll +#define prog_recvfrom prog_ops.op_recvfrom +#define prog_sendto prog_ops.op_sendto +#define prog_close prog_ops.op_close +#define prog_connect prog_ops.op_connect +#define prog_getsockname prog_ops.op_getsockname +#define prog_sysctl prog_ops.op_sysctl + +#endif /* _PROG_OPS_H_ */ diff --git a/usr.sbin/traceroute/traceroute.8 b/usr.sbin/traceroute/traceroute.8 new file mode 100644 index 000000000..0ed6f2b59 --- /dev/null +++ b/usr.sbin/traceroute/traceroute.8 @@ -0,0 +1,419 @@ +.\" $NetBSD: traceroute.8,v 1.29 2011/09/11 01:06:26 christos Exp $ +.\" +.\" Copyright (c) 1989, 1995, 1996, 1997 +.\" 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +.\" +.\" Id: traceroute.8,v 1.19 2000/09/21 08:44:19 leres Exp +.\" +.TH TRACEROUTE 8 "21 September 2000" +.UC 6 +.SH NAME +traceroute \- print the route packets take to network host +.SH SYNOPSIS +.na +.B traceroute +[ +.B \-aDFPIdlMnrvx +] [ +.B \-f +.I first_ttl +] +.br +.ti +8 +[ +.B \-g +.I gateway +] [ +.B \-i +.I iface +] [ +.B \-m +max_ttl +] +.br +.ti +8 +[ +.B \-p +.I port +] [ +.B \-q +.I nqueries +] [ +.B \-s +.I src_addr +] +.br +.ti +8 +[ +.B \-t +.I tos +] [ +.B \-w +.I waittime +] [ +.B \-z +.I pausemsecs + ] +] [ +.B \-A +.I as_server +] +.br +.ti +8 +.I host +[ +.I packetlen +] +.ad +.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. +.I Traceroute +uses 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: +.TP +.B \-a +Turn on AS# lookups for each hop encountered. +.TP +.B \-A +Turn on AS# lookups and use the given server instead of the default. +.TP +.B \-d +Turn on socket-level debugging. +.TP +.B \-D +Dump the packet data to standard error before transmitting it. +.TP +.B \-f +Set the initial time-to-live used in the first outgoing probe packet. +.TP +.B \-F +Set the "don't fragment" bit. +.TP +.B \-g +Specify a loose source route gateway (8 maximum). +.TP +.B \-i +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 +.B \-s +flag for another way to do this.) +.TP +.B \-I +Use ICMP ECHO instead of UDP datagrams. +.TP +.B \-l +Display the ttl value of the returned packet. This is useful for +checking for asymmetric routing. +.TP +.B \-m +Set the max time-to-live (max number of hops) used in outgoing probe +packets. The default value is taken from the +.I net.inet.ip.ttl +sysctl(3) variable. +.TP +.B \-M +If found, show the MPLS Label and the Experimental (EXP) bit for the hop. +.TP +.B \-n +Print hop addresses numerically rather than symbolically and numerically +(saves a nameserver address-to-name lookup for each gateway found on the +path). +.TP +.B \-p +Set the base UDP port number used in probes (default is 33434). +Traceroute hopes that nothing is listening on UDP ports +.I base +to +.I base + nhops \- 1 +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. +.TP +.B \-P +Set the "don't fragment" bit, and use the next hop mtu each time we get +the "need fragmentation" error, thus probing the path MTU. +.TP +.B \-q +Set the number of probe packets sent for each hop. By default, traceroute +sends three probe packets. +.TP +.B \-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 +.IR routed (8)). +.TP +.B \-s +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 +.B \-i +flag for another way to do this.) +.TP +.B \-t +Set the +.I 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. (If you +are not running 4.4BSD, this may be academic since the normal network +services like telnet and ftp don't let you control the TOS). +Not all values of TOS are legal or +meaningful \- see the IP spec for definitions. Useful values are +probably +.RB ` -t +.IR 16 ' +(low delay) and +.RB ` -t +.IR 8 ' +(high throughput). +.TP +.B \-v +Verbose output. Received ICMP packets other than TIME_EXCEEDED and +UNREACHABLEs are listed. +.TP +.B \-w +Set the time (in seconds) to wait for a response to a probe (default 5 +sec.). +.TP +.B \-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 +.B \-x +causes them to be calculated). Note that checksums are usually required +for the last hop when using ICMP ECHO probes +.RB ( \-I ). +So they are always calculated when using ICMP. +.TP +.B \-z +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 this is 500 (e.g. 1/2 second). +.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 30 hops \*[Am] can be changed with the +.B \-m +flag). Three +probes (change with +.B \-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 +.B \-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 +.B \-p +flag). +.PP +A sample use and output might be: + +.RS +.nf +[yak 71]% traceroute nis.nsf.net. +traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 38 byte packet + 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 +.fi +.RE + +Note that lines 2 \*[Am] 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: + +.RS +.nf +[yak 72]% traceroute allspice.lcs.mit.edu. +traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max + 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 +.fi +.RE + +Note that the gateways 12, 14, 15, 16 \*[Am] 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: + +.RS +.nf + 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 ! +.fi +.RE + +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 (HP-UX) 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 +.BR !H , +.BR !N , +or +.B !P +(host, network or protocol unreachable), +.B !S +(source route failed), +.B !F\- +(fragmentation needed \- the RFC1191 Path MTU Discovery value is displayed), +.B !X +(communication administratively prohibited), +.B !V +(host precedence violation), +.B !C +(precedence cutoff in effect), or +.B !\*[Lt]N\*[Gt] +(ICMP unreachable code \*[Lt]num\*[Gt]). +These are defined by RFC1812 (which supersedes RFC1716). +If almost all the probes result in some kind of unreachable, traceroute +will give up and exit. + +.RS +.nf +traceroute \-g 10.3.0.5 128.182.0.0 +.fi +.RE + +will show the path from the Cambridge Mailbridge to PSC, while + +.RS +.nf +traceroute \-g 192.5.146.4 \-g 10.3.0.5 35.0.0.0 +.fi +.RE + +will show the path from the Cambridge Mailbridge to Merit, using PSC to +reach the Mailbridge. +.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 +.I traceroute +during normal operations or from automated scripts. +.SH SEE ALSO +netstat(1), ping(8) +.SH AUTHOR +Implemented by 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. +.LP +The current version is available via anonymous ftp: +.LP +.RS +.I ftp://ftp.ee.lbl.gov/traceroute.tar.gz +.RE +.SH BUGS +Please send bug reports to traceroute@ee.lbl.gov. +.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 000000000..a4b10f963 --- /dev/null +++ b/usr.sbin/traceroute/traceroute.c @@ -0,0 +1,1905 @@ +/* $NetBSD: traceroute.c,v 1.81 2012/08/16 00:40:28 zafer Exp $ */ + +/* + * 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. + */ + +#include +#ifndef lint +#if 0 +static const char rcsid[] = + "@(#)Id: traceroute.c,v 1.68 2000/12/14 08:04:33 leres Exp (LBL)"; +#else +__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997,\ + 1998, 1999, 2000\ + The Regents of the University of California. All rights reserved."); +__RCSID("$NetBSD: traceroute.c,v 1.81 2012/08/16 00:40:28 zafer Exp $"); +#endif +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 30 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), 30 hops max, 56 byte packet + * 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), 30 hops max + * 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 a 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef IPSEC +#include +#include +#endif + +#include "gnuc.h" +#ifdef HAVE_OS_PROTO_H +#include "os-proto.h" +#endif + +/* 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 "ifaddrlist.h" +#include "as.h" +#include "prog_ops.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 + +/* 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 tv32 { + int32_t tv32_sec; + int32_t tv32_usec; + } tv; /* time packet left */ +}; + +/* + * Support for ICMP extensions + * + * http://www.ietf.org/proceedings/01aug/I-D/draft-ietf-mpls-icmp-02.txt + */ +#define ICMP_EXT_OFFSET 8 /* ICMP type, code, checksum, unused */ + \ + 128 /* original datagram */ +#define ICMP_EXT_VERSION 2 +/* + * ICMP extensions, common header + */ +struct icmp_ext_cmn_hdr { +#if BYTE_ORDER == BIG_ENDIAN + unsigned char version:4; + unsigned char reserved1:4; +#else + unsigned char reserved1:4; + unsigned char version:4; +#endif + unsigned char reserved2; + unsigned short checksum; +}; + +/* + * ICMP extensions, object header + */ +struct icmp_ext_obj_hdr { + u_short length; + u_char class_num; +#define MPLS_STACK_ENTRY_CLASS 1 + u_char c_type; +#define MPLS_STACK_ENTRY_C_TYPE 1 +}; + +struct mpls_header { +#if BYTE_ORDER == BIG_ENDIAN + uint32_t label:20; + unsigned char exp:3; + unsigned char s:1; + unsigned char ttl:8; +#else + unsigned char ttl:8; + unsigned char s:1; + unsigned char exp:3; + uint32_t label:20; +#endif +}; + +#ifndef HAVE_ICMP_NEXTMTU +/* Path MTU Discovery (RFC1191) */ +struct my_pmtu { + u_short ipm_void; + u_short ipm_nextmtu; +}; +#endif + +static u_char packet[512]; /* last inbound (icmp) packet */ + +static struct ip *outip; /* last output (udp) packet */ +static struct udphdr *outudp; /* last output (udp) packet */ +static void *outmark; /* packed location of struct outdata */ +static struct outdata outsetup; /* setup and copy for alignment */ + +static struct icmp *outicmp; /* last output (icmp) packet */ + +/* loose source route gateway list (including room for final destination) */ +static u_int32_t gwlist[NGATEWAYS + 1]; + +static int s; /* receive (icmp) socket file descriptor */ +static int sndsock; /* send (udp/icmp) socket file descriptor */ + +static struct sockaddr whereto; /* Who to try to reach */ +static struct sockaddr wherefrom; /* Who we are */ +static int packlen; /* total length of packet */ +static int minpacket; /* min ip packet size */ +static int maxpacket = 32 * 1024; /* max ip packet size */ +static int printed_ttl = 0; +static int pmtu; /* Path MTU Discovery (RFC1191) */ +static u_int pausemsecs; + +static const char *prog; +static char *source; +static char *hostname; +static char *device; +#ifdef notdef +static const char devnull[] = "/dev/null"; +#endif + +static int nprobes = 3; +static int max_ttl = 30; +static int first_ttl = 1; +static u_int16_t ident; +static in_port_t port = 32768 + 666; /* start udp dest port # for probe packets */ + +static int options; /* socket options */ +static int verbose; +static int waittime = 5; /* time to wait for response (in seconds) */ +static int nflag; /* print addresses numerically */ +static int dump; +static int Mflag; /* show MPLS labels if any */ +static int as_path; /* print as numbers for each hop */ +static char *as_server = NULL; +static void *asn; +static int useicmp = 0; /* use icmp echo instead of udp packets */ +#ifdef CANT_HACK_CKSUM +static int doipcksum = 0; /* don't calculate checksums */ +#else +static int doipcksum = 1; /* calculate checksums */ +#endif +static int optlen; /* length of ip options */ + +static int mtus[] = { + 17914, + 8166, + 4464, + 4352, + 2048, + 2002, + 1536, + 1500, + 1492, + 1480, + 1280, + 1006, + 576, + 552, + 544, + 512, + 508, + 296, + 68, + 0 +}; +static int *mtuptr = &mtus[0]; +static int mtudisc = 0; +static int nextmtu; /* from ICMP error, set by packet_ok(), might be 0 */ + +/* Forwards */ +static double deltaT(struct timeval *, struct timeval *); +static void freehostinfo(struct hostinfo *); +static void getaddr(u_int32_t *, char *); +static struct hostinfo *gethostinfo(char *); +static u_int16_t in_cksum(u_int16_t *, int); +static u_int16_t in_cksum2(u_int16_t, u_int16_t *, int); +static char *inetname(struct in_addr); +static int packet_ok(u_char *, ssize_t, struct sockaddr_in *, int); +static const char *pr_type(u_char); +static void print(u_char *, int, struct sockaddr_in *); +static void resize_packet(void); +static void dump_packet(void); +static void send_probe(int, int, struct timeval *); +static void setsin(struct sockaddr_in *, u_int32_t); +static int str2val(const char *, const char *, int, int); +static void tvsub(struct timeval *, struct timeval *); +static void usage(void) __attribute__((__noreturn__)); +static ssize_t wait_for_reply(int, struct sockaddr_in *, const struct timeval *); +static void decode_extensions(unsigned char *buf, int ip_len); +static void frag_err(void); +static int find_local_ip(struct sockaddr_in *, struct sockaddr_in *); +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC +static int setpolicy(int, const char *); +#endif +#endif + +int +main(int argc, char **argv) +{ + int op, code, n; + u_char *outp; + u_int32_t *ap; + struct sockaddr_in *from = (struct sockaddr_in *)&wherefrom; + struct sockaddr_in *to = (struct sockaddr_in *)&whereto; + struct hostinfo *hi; + int on = 1; + int ttl, probe, i; + int seq = 0; + int tos = 0, settos = 0, ttl_flag = 0; + int lsrr = 0; + u_int16_t off = 0; + struct ifaddrlist *al, *al2; + char errbuf[132]; + int mib[4] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_DEFTTL }; + size_t size = sizeof(max_ttl); + + setprogname(argv[0]); + prog = getprogname(); + + if (prog_init && prog_init() == -1) + err(1, "init failed"); + +#ifdef notdef + /* Kernel takes care of it */ + /* 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) + err(1, "Cannot open `%s'", devnull); +#endif + if ((s = prog_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) + err(1, "icmp socket"); + + /* + * XXX 'useicmp' will always be zero here. I think the HP-UX users + * running our traceroute code will forgive us. + */ +#ifndef __hpux +#ifdef __minix + sndsock = prog_socket(AF_INET, SOCK_RAW, IPPROTO_UDP); +#else + sndsock = prog_socket(AF_INET, SOCK_RAW, IPPROTO_RAW); +#endif +#else + sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW + useicmp ? IPPROTO_ICMP : IPPROTO_UDP); +#endif + if (sndsock < 0) + err(1, "raw socket"); + + (void) prog_sysctl(mib, sizeof(mib)/sizeof(mib[0]), &max_ttl, &size, + NULL, 0); + + opterr = 0; + while ((op = getopt(argc, argv, "aA:dDFPIMnlrvxf:g:i:m:p:q:s:t:w:z:")) != -1) + 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': + dump = 1; + break; + + case 'f': + first_ttl = str2val(optarg, "first ttl", 1, 255); + break; + + case 'F': + off = IP_DF; + break; + + case 'g': + if (lsrr >= NGATEWAYS) + errx(1, "more than %d gateways", NGATEWAYS); + getaddr(gwlist + lsrr, optarg); + ++lsrr; + break; + + case 'i': + device = optarg; + break; + + case 'I': + ++useicmp; + break; + + case 'l': + ++ttl_flag; + break; + + case 'm': + max_ttl = str2val(optarg, "max ttl", 1, 255); + break; + + case 'M': + Mflag = 1; + break; + + case 'n': + ++nflag; + break; + + case 'p': + port = (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 '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", + 2, 24 * 60 * 60); + break; + + case 'z': + pausemsecs = str2val(optarg, "pause msecs", + 0, 60 * 60 * 1000); + + case 'P': + off = IP_DF; + mtudisc = 1; + break; + + default: + usage(); + } + + if (first_ttl > max_ttl) + errx(1, "first ttl (%d) may not be greater than max ttl (%d)", + first_ttl, max_ttl); + + if (!doipcksum) + warnx("ip checksums disabled"); + + if (lsrr > 0) + optlen = (lsrr + 1) * sizeof(gwlist[0]); + minpacket = sizeof(*outip) + sizeof(struct outdata) + optlen; + if (useicmp) + minpacket += 8; /* XXX magic number */ + else + minpacket += sizeof(*outudp); + packlen = minpacket; /* minimum sized packet */ + + if (mtudisc) + packlen = *mtuptr++; + + /* 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) + warnx("%s has multiple addresses; using %s", + hostname, inet_ntoa(to->sin_addr)); + hostname = hi->name; + hi->name = NULL; + freehostinfo(hi); + break; + + default: + usage(); + } + +#ifdef HAVE_SETLINEBUF + setlinebuf (stdout); +#else + setvbuf(stdout, NULL, _IOLBF, 0); +#endif + + outip = malloc((unsigned)packlen); + if (outip == NULL) + err(1, "malloc"); + memset(outip, 0, packlen); + + outip->ip_v = IPVERSION; + if (settos) + outip->ip_tos = tos; +#ifdef BYTESWAP_IP_HDR + outip->ip_len = htons(packlen); + outip->ip_off = htons(off); +#else + outip->ip_len = packlen; + outip->ip_off = off; +#endif + outp = (u_char *)(outip + 1); +#ifdef HAVE_RAW_OPTIONS + if (lsrr > 0) { + 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 +#endif + outip->ip_dst = to->sin_addr; + + outip->ip_hl = (outp - (u_char *)outip) >> 2; + ident = htons(arc4random() & 0xffff) | 0x8000; + if (useicmp) { + outip->ip_p = IPPROTO_ICMP; + + outicmp = (struct icmp *)outp; + outicmp->icmp_type = ICMP_ECHO; + outicmp->icmp_id = htons(ident); + + outmark = outp + 8; /* XXX magic number */ + } else { + outip->ip_p = IPPROTO_UDP; + + outudp = (struct udphdr *)outp; + outudp->uh_sport = htons(ident); + outudp->uh_ulen = + htons((u_int16_t)(packlen - (sizeof(*outip) + optlen))); + outmark = outudp + 1; + } + + if (options & SO_DEBUG) + (void)prog_setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on, + sizeof(on)); +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC + /* + * do not raise error even if setsockopt fails, kernel may have ipsec + * turned off. + */ + if (setpolicy(s, "in bypass") < 0) + exit(1); + if (setpolicy(s, "out bypass") < 0) + exit(1); +#else + { + int level = IPSEC_LEVEL_AVAIL; + + (void)prog_setsockopt(s, IPPROTO_IP, IP_ESP_TRANS_LEVEL, &level, + sizeof(level)); + (void)prog_setsockopt(s, IPPROTO_IP, IP_ESP_NETWORK_LEVEL, &level, + sizeof(level)); +#ifdef IP_AUTH_TRANS_LEVEL + (void)prog_setsockopt(s, IPPROTO_IP, IP_AUTH_TRANS_LEVEL, &level, + sizeof(level)); +#else + (void)prog_setsockopt(s, IPPROTO_IP, IP_AUTH_LEVEL, &level, + sizeof(level)); +#endif +#ifdef IP_AUTH_NETWORK_LEVEL + (void)prog_setsockopt(s, IPPROTO_IP, IP_AUTH_NETWORK_LEVEL, &level, + sizeof(level)); +#endif + } +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ + +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC + /* + * do not raise error even if setsockopt fails, kernel may have ipsec + * turned off. + */ + if (setpolicy(sndsock, "in bypass") < 0) + exit(1); + if (setpolicy(sndsock, "out bypass") < 0) + exit(1); +#else + { + int level = IPSEC_LEVEL_BYPASS; + + (void)prog_setsockopt(sndsock, IPPROTO_IP, IP_ESP_TRANS_LEVEL, &level, + sizeof(level)); + (void)prog_setsockopt(sndsock, IPPROTO_IP, IP_ESP_NETWORK_LEVEL, &level, + sizeof(level)); +#ifdef IP_AUTH_TRANS_LEVEL + (void)prog_setsockopt(sndsock, IPPROTO_IP, IP_AUTH_TRANS_LEVEL, &level, + sizeof(level)); +#else + (void)prog_setsockopt(sndsock, IPPROTO_IP, IP_AUTH_LEVEL, &level, + sizeof(level)); +#endif +#ifdef IP_AUTH_NETWORK_LEVEL + (void)prog_setsockopt(sndsock, IPPROTO_IP, IP_AUTH_NETWORK_LEVEL, &level, + sizeof(level)); +#endif + } +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ + +#if defined(IP_OPTIONS) && !defined(HAVE_RAW_OPTIONS) + if (lsrr > 0) { + u_char optlist[MAX_IPOPTLEN]; + + /* final hop */ + gwlist[lsrr] = to->sin_addr.s_addr; + ++lsrr; + + /* 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, i); + + if ((prog_setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS, optlist, + i + sizeof(gwlist[0]))) < 0) + err(1, "IP_OPTIONS"); + } +#endif + +#ifndef __minix +#ifdef SO_SNDBUF + if (prog_setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&packlen, + sizeof(packlen)) < 0) + err(1, "SO_SNDBUF"); +#endif +#ifdef IP_HDRINCL + if (prog_setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on, + sizeof(on)) < 0) + err(1, "IP_HDRINCL"); +#else +#ifdef IP_TOS + if (settos && prog_setsockopt(sndsock, IPPROTO_IP, IP_TOS, + &tos, sizeof(tos)) < 0) + err(1, "setsockopt tos %d", tos); +#endif +#endif + if (options & SO_DEBUG) + if (prog_setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, &on, + sizeof(on)) < 0) + err(1, "setsockopt debug %d", tos); + if (options & SO_DONTROUTE) + if (prog_setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, &on, + sizeof(on)) < 0) + err(1, "setsockopt dontroute %d", tos); +#endif + + /* Get the interface address list */ + n = ifaddrlist(&al, errbuf, sizeof errbuf); + al2 = al; + if (n < 0) + errx(1, "ifaddrlist (%s)", errbuf); + if (n == 0) + errx(1, "Can't find any network interfaces"); + + /* Look for a specific device */ + if (device != NULL) { + for (i = n; i > 0; --i, ++al2) + if (strcmp(device, al2->device) == 0) + break; + if (i <= 0) + errx(1, "Can't find interface %.32s", device); + } + + /* Determine our source address */ + if (source == NULL) { + /* + * If a device was specified, use the interface address. + * Otherwise, try to determine our source address. + * Warn if there are more than one. + */ + setsin(from, al2->addr); + if (n > 1 && device == NULL && !find_local_ip(from, to)) { + warnx("Multiple interfaces found; using %s @ %s", + inet_ntoa(from->sin_addr), al2->device); + } + } else { + hi = gethostinfo(source); + source = hi->name; + hi->name = NULL; + if (device == NULL) { + /* + * Use the first interface found. + * Warn if there are more than one. + */ + setsin(from, hi->addrs[0]); + if (hi->n > 1) + warnx("%s has multiple addresses; using %s", + source, inet_ntoa(from->sin_addr)); + } else { + /* + * Make sure the source specified matches the + * interface address. + */ + for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap) + if (*ap == al2->addr) + break; + if (i <= 0) + errx(1, "%s is not on interface %s", + source, device); + setsin(from, *ap); + } + freehostinfo(hi); + } + + /* Revert to non-privileged user after opening sockets */ + setgid(getgid()); + setuid(getuid()); + + /* + * If not root, make sure source address matches a local interface. + * (The list of addresses produced by ifaddrlist() automatically + * excludes interfaces that are marked down and/or loopback.) + */ + if (getuid()) { + al2 = al; + for (i = n; i > 0; --i, ++al2) + if (from->sin_addr.s_addr == al2->addr) + break; + if (i <= 0) + errx(1, "%s is not a valid local address " + "and you are not superuser.", + inet_ntoa(from->sin_addr)); + } + + outip->ip_src = from->sin_addr; +#ifndef IP_HDRINCL + if (bind(sndsock, (struct sockaddr *)from, sizeof(*from)) < 0) + err(1, "bind"); +#endif + + if (as_path) { + asn = as_setup(as_server); + if (asn == NULL) { + warnx("as_setup failed, AS# lookups disabled"); + (void)fflush(stderr); + as_path = 0; + } + } + + setuid(getuid()); + 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; + +again: + printed_ttl = 0; + for (probe = 0; probe < nprobes; ++probe) { + int cc; + struct timeval t1, t2; + struct ip *ip; + if (sentfirst && pausemsecs > 0) + usleep(pausemsecs * 1000); + (void)gettimeofday(&t1, NULL); + if (!useicmp && htons(port + seq + 1) == 0) + seq++; + send_probe(++seq, ttl, &t1); + ++sentfirst; + while ((cc = wait_for_reply(s, from, &t1)) != 0) { + (void)gettimeofday(&t2, NULL); + /* + * Since we'll be receiving all ICMP + * messages to this host above, we may + * never end up with cc=0, so we need + * an additional termination check. + */ + if (t2.tv_sec - t1.tv_sec > waittime) { + cc = 0; + break; + } + 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; + } + ip = (struct ip *)packet; + Printf(" %.3f ms", deltaT(&t1, &t2)); + if (ttl_flag) + Printf(" (ttl = %d)", ip->ip_ttl); + if (i == -2) { +#ifndef ARCHAIC + 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 + 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: + if (mtudisc) { + frag_err(); + goto again; + } else { + ++unreachable; + Printf(" !F-%d", pmtu); + } + break; + + case ICMP_UNREACH_SRCFAIL: + ++unreachable; + Printf(" !S"); + 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) + Printf(" *"); + else if (cc && probe == nprobes - 1 && Mflag) + decode_extensions(packet, cc); + (void)fflush(stdout); + } + putchar('\n'); + if (got_there || + (unreachable > 0 && unreachable >= ((nprobes + 1) / 2))) + break; + } + + if (as_path) + as_shutdown(asn); + + exit(0); +} + +static ssize_t +wait_for_reply(int sock, struct sockaddr_in *fromp, const struct timeval *tp) +{ + struct pollfd set[1]; + struct timeval now, wait; + ssize_t cc = 0; + socklen_t fromlen = sizeof(*fromp); + int retval; + + set[0].fd = sock; + set[0].events = POLLIN; + + 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 = 0; + } + + retval = prog_poll(set, 1, wait.tv_sec * 1000 + wait.tv_usec / 1000); + if (retval < 0) + /* If we continue, we probably just flood the remote host. */ + err(1, "poll"); + if (retval > 0) { + cc = prog_recvfrom(sock, (char *)packet, sizeof(packet), 0, + (struct sockaddr *)fromp, &fromlen); + } + + return cc; +} + +static void +decode_extensions(unsigned char *buf, int ip_len) +{ + struct icmp_ext_cmn_hdr *cmn_hdr; + struct icmp_ext_obj_hdr *obj_hdr; + union { + struct mpls_header mpls; + uint32_t mpls_h; + } mpls; + size_t datalen, obj_len; + struct ip *ip; + + ip = (struct ip *)buf; + + if (ip_len < (int)((ip->ip_hl << 2) + ICMP_EXT_OFFSET + + sizeof(struct icmp_ext_cmn_hdr))) { + /* + * No support for ICMP extensions on this host + */ + return; + } + + /* + * Move forward to the start of the ICMP extensions, if present + */ + buf += (ip->ip_hl << 2) + ICMP_EXT_OFFSET; + cmn_hdr = (struct icmp_ext_cmn_hdr *)buf; + + if (cmn_hdr->version != ICMP_EXT_VERSION) { + /* + * Unknown version + */ + return; + } + + datalen = ip_len - ((u_char *)cmn_hdr - (u_char *)ip); + + /* + * Check the checksum, cmn_hdr->checksum == 0 means no checksum'ing + * done by sender. + * + * If the checksum is ok, we'll get 0, as the checksum is calculated + * with the checksum field being 0'd. + */ + if (ntohs(cmn_hdr->checksum) && + in_cksum((u_short *)cmn_hdr, datalen)) { + + return; + } + + buf += sizeof(*cmn_hdr); + datalen -= sizeof(*cmn_hdr); + + while (datalen >= sizeof(struct icmp_ext_obj_hdr)) { + obj_hdr = (struct icmp_ext_obj_hdr *)buf; + obj_len = ntohs(obj_hdr->length); + + /* + * Sanity check the length field + */ + if (obj_len > datalen) + return; + + datalen -= obj_len; + + /* + * Move past the object header + */ + buf += sizeof(struct icmp_ext_obj_hdr); + obj_len -= sizeof(struct icmp_ext_obj_hdr); + + switch (obj_hdr->class_num) { + case MPLS_STACK_ENTRY_CLASS: + switch (obj_hdr->c_type) { + case MPLS_STACK_ENTRY_C_TYPE: + while (obj_len >= sizeof(uint32_t)) { + mpls.mpls_h = ntohl(*(uint32_t *)buf); + + buf += sizeof(uint32_t); + obj_len -= sizeof(uint32_t); + + printf(" [MPLS: Label %d Exp %d]", + mpls.mpls.label, mpls.mpls.exp); + } + if (obj_len > 0) { + /* + * Something went wrong, and we're at + * a unknown offset into the packet, + * ditch the rest of it. + */ + return; + } + break; + default: + /* + * Unknown object, skip past it + */ + buf += ntohs(obj_hdr->length) - + sizeof(struct icmp_ext_obj_hdr); + break; + } + break; + + default: + /* + * Unknown object, skip past it + */ + buf += ntohs(obj_hdr->length) - + sizeof(struct icmp_ext_obj_hdr); + break; + } + } +} + +static void +dump_packet(void) +{ + u_char *p; + int i; + + Fprintf(stderr, "packet data:"); + +#ifdef __hpux + for (p = useicmp ? (u_char *)outicmp : (u_char *)outudp, i = 0; i < + i < packlen - (sizeof(*outip) + optlen); i++) +#else + for (p = (u_char *)outip, i = 0; i < packlen; i++) +#endif + { + if ((i % 24) == 0) + Fprintf(stderr, "\n "); + Fprintf(stderr, " %02x", *p++); + } + Fprintf(stderr, "\n"); +} + +void +send_probe(int seq, int ttl, struct timeval *tp) +{ + int cc; + struct udpiphdr * ui, *oui; + int oldmtu = packlen; + struct ip tip; + +again: +#ifdef BYTESWAP_IP_LEN + outip->ip_len = htons(packlen); +#else + outip->ip_len = packlen; +#endif + outip->ip_ttl = ttl; +#ifndef __hpux + outip->ip_id = htons(ident + seq); +#endif + + /* + * In most cases, the kernel will recalculate the ip checksum. + * But we must do it anyway so that the udp checksum comes out + * right. + */ + if (doipcksum) { + outip->ip_sum = + in_cksum((u_int16_t *)outip, sizeof(*outip) + optlen); + if (outip->ip_sum == 0) + outip->ip_sum = 0xffff; + } + + /* Payload */ + outsetup.seq = seq; + outsetup.ttl = ttl; + outsetup.tv.tv32_sec = htonl(tp->tv_sec); + outsetup.tv.tv32_usec = htonl(tp->tv_usec); + memcpy(outmark,&outsetup,sizeof(outsetup)); + + if (useicmp) + outicmp->icmp_seq = htons(seq); + else + outudp->uh_dport = htons(port + seq); + + if (useicmp) { + /* Always calculate checksum for icmp packets */ + outicmp->icmp_cksum = 0; + outicmp->icmp_cksum = in_cksum((u_short *)outicmp, + packlen - (sizeof(*outip) + optlen)); + if (outicmp->icmp_cksum == 0) + outicmp->icmp_cksum = 0xffff; + } else if (doipcksum) { + /* Checksum (we must save and restore ip header) */ + tip = *outip; + ui = (struct udpiphdr *)outip; + oui = (struct udpiphdr *)&tip; + /* Easier to zero and put back things that are ok */ + memset(ui, 0, sizeof(ui->ui_i)); + ui->ui_src = oui->ui_src; + ui->ui_dst = oui->ui_dst; + ui->ui_pr = oui->ui_pr; + ui->ui_len = outudp->uh_ulen; + outudp->uh_sum = 0; + outudp->uh_sum = in_cksum((u_short *)ui, packlen); + if (outudp->uh_sum == 0) + outudp->uh_sum = 0xffff; + *outip = tip; + } + + /* XXX undocumented debugging hack */ + if (verbose > 1) { + const u_int16_t *sp; + int nshorts, i; + + sp = (u_int16_t *)outip; + nshorts = (u_int)packlen / sizeof(u_int16_t); + 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", *(const u_char *)sp); + } + Printf("]\n"); + } + +#ifndef __minix +#if !defined(IP_HDRINCL) && defined(IP_TTL) + if (prog_setsockopt(sndsock, IPPROTO_IP, IP_TTL, + (char *)&ttl, sizeof(ttl)) < 0) + err(1, "setsockopt ttl %d", ttl); +#endif +#else + { + nwio_ipopt_t ipopts; + memset(&ipopts, 0, sizeof(ipopts)); + ipopts.nwio_flags = NWIO_HDR_O_SPEC; + ipopts.nwio_ttl = ttl; + if(ioctl(sndsock, NWIOSIPOPT, &ipopts) < 0) { + err(1, "ttl ioctl"); + } + } +#endif + if (dump) + dump_packet(); + +#ifdef __hpux + cc = sendto(sndsock, useicmp ? (char *)outicmp : (char *)outudp, + packlen - (sizeof(*outip) + optlen), 0, &whereto, sizeof(whereto)); + if (cc > 0) + cc += sizeof(*outip) + optlen; +#else + cc = prog_sendto(sndsock, (char *)outip, + packlen, 0, &whereto, sizeof(whereto)); +#endif + if (cc < 0 || cc != packlen) { + if (cc < 0) { + /* + * An errno of EMSGSIZE means we're writing too big a + * datagram for the interface. We have to just + * decrease the packet size until we find one that + * works. + * + * XXX maybe we should try to read the outgoing if's + * mtu? + */ + if (errno == EMSGSIZE) { + packlen = *mtuptr++; + resize_packet(); + goto again; + } else + warn("sendto.."); + } + + Printf("%s: wrote %s %d chars, ret=%d\n", + prog, hostname, packlen, cc); + (void)fflush(stdout); + } + if (oldmtu != packlen) { + Printf("message too big, " + "trying new MTU = %d\n", packlen); + printed_ttl = 0; + } + if (!printed_ttl) { + Printf("%2d ", ttl); + printed_ttl = 1; + } + +} + +static double +deltaT(struct timeval *t1p, struct timeval *t2p) +{ + 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. + */ +static const char * +pr_type(u_char t) +{ + static const 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]; +} + +static int +packet_ok(u_char *buf, ssize_t cc, struct sockaddr_in *from, int seq) +{ + struct icmp *icp; + u_char type, code; + int hlen; +#ifndef ARCHAIC + struct ip *ip; + + ip = (struct ip *) buf; + hlen = ip->ip_hl << 2; + if (cc < hlen + ICMP_MINLEN) { + if (verbose) + Printf("packet too short (%zd 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 { +#ifdef HAVE_ICMP_NEXTMTU + pmtu = ntohs(icp->icmp_nextmtu); +#else + pmtu = ntohs(((struct my_pmtu *)&icp->icmp_void)->ipm_nextmtu); +#endif + } + if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) || + type == ICMP_UNREACH || type == ICMP_ECHOREPLY) { + struct ip *hip; + struct udphdr *up; + struct icmp *hicmp; + + hip = &icp->icmp_ip; + hlen = hip->ip_hl << 2; + + nextmtu = ntohs(icp->icmp_nextmtu); /* for frag_err() */ + + if (useicmp) { + /* XXX */ + if (type == ICMP_ECHOREPLY && + icp->icmp_id == htons(ident) && + icp->icmp_seq == htons(seq)) + return -2; + + hicmp = (struct icmp *)((u_char *)hip + hlen); + /* XXX 8 is a magic number */ + if (hlen + 8 <= cc && + hip->ip_p == IPPROTO_ICMP && + hicmp->icmp_id == htons(ident) && + hicmp->icmp_seq == htons(seq)) + return type == ICMP_TIMXCEED ? -1 : code + 1; + } else { + up = (struct udphdr *)((u_char *)hip + hlen); + /* XXX 8 is a magic number */ + if (hlen + 12 <= cc && + hip->ip_p == IPPROTO_UDP && + up->uh_sport == htons(ident) && + up->uh_dport == htons(port + seq)) + return type == ICMP_TIMXCEED ? -1 : code + 1; + } + } +#ifndef ARCHAIC + if (verbose) { + int i; + u_int32_t *lp = (u_int32_t *)&icp->icmp_ip; + + Printf("\n%zd 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 ; i += sizeof(*lp)) + Printf("%2d: x%8.8x\n", i, *lp++); + } +#endif + return(0); +} + +static void +resize_packet(void) +{ + if (useicmp) { + outicmp->icmp_cksum = 0; + outicmp->icmp_cksum = in_cksum((u_int16_t *)outicmp, + packlen - (sizeof(*outip) + optlen)); + if (outicmp->icmp_cksum == 0) + outicmp->icmp_cksum = 0xffff; + } else { + outudp->uh_ulen = + htons((u_int16_t)(packlen - (sizeof(*outip) + optlen))); + } +} + +static void +print(u_char *buf, int cc, struct sockaddr_in *from) +{ + struct ip *ip; + 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)); +} + +static u_int16_t +in_cksum(u_int16_t *addr, int len) +{ + + return ~in_cksum2(0, addr, len); +} + +/* + * Checksum routine for Internet Protocol family headers (C Version) + */ +static u_int16_t +in_cksum2(u_int16_t seed, u_int16_t *addr, int len) +{ + int nleft = len; + u_int16_t *w = addr; + union { + u_int16_t w; + u_int8_t b[2]; + } answer; + int32_t sum = seed; + + /* + * 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) { + answer.b[0] = *(u_char *)w; + answer.b[1] = 0; + sum += answer.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.w = sum; /* truncate to 16 bits */ + return answer.w; +} + +/* + * Subtract 2 timeval structs: out = out - in. + * Out is assumed to be >= in. + */ +static void +tvsub(struct timeval *out, 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. + */ +static char * +inetname(struct in_addr in) +{ + char *cp; + 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) { + hp = gethostbyname(domain); + if (hp != NULL) + cp = strchr(hp->h_name, '.'); + } + if (cp == NULL) + domain[0] = '\0'; + else { + ++cp; + (void)strlcpy(domain, cp, sizeof(domain)); + } + } + } + if (!nflag && in.s_addr != INADDR_ANY) { + 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)strlcpy(line, hp->h_name, sizeof(line)); + return line; + } + } + return inet_ntoa(in); +} + +static struct hostinfo * +gethostinfo(char *hname) +{ + int n; + struct hostent *hp; + struct hostinfo *hi; + char **p; + u_int32_t *ap; + struct in_addr addr; + + hi = calloc(1, sizeof(*hi)); + if (hi == NULL) + err(1, "calloc"); + if (inet_aton(hname, &addr) != 0) { + hi->name = strdup(hname); + if (!hi->name) + err(1, "strdup"); + hi->n = 1; + hi->addrs = calloc(1, sizeof(hi->addrs[0])); + if (hi->addrs == NULL) + err(1, "calloc"); + hi->addrs[0] = addr.s_addr; + return hi; + } + + hp = gethostbyname(hname); + if (hp == NULL) + errx(1, "unknown host %s", hname); + if (hp->h_addrtype != AF_INET || hp->h_length != 4) + errx(1, "bad host %s", hname); + hi->name = strdup(hp->h_name); + if (!hi->name) + err(1, "strdup"); + 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) + err(1, "calloc"); + for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p) + memcpy(ap, *p, sizeof(*ap)); + return hi; +} + +static void +freehostinfo(struct hostinfo *hi) +{ + if (hi->name != NULL) { + free(hi->name); + hi->name = NULL; + } + free(hi->addrs); + free(hi); +} + +static void +getaddr(u_int32_t *ap, char *hname) +{ + struct hostinfo *hi; + + hi = gethostinfo(hname); + *ap = hi->addrs[0]; + freehostinfo(hi); +} + +static void +setsin(struct sockaddr_in *sin, u_int32_t addr) +{ + + memset(sin, 0, sizeof(*sin)); +#ifdef HAVE_SOCKADDR_SA_LEN + sin->sin_len = sizeof(*sin); +#endif + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = addr; +} + +/* String to value with optional min and max. Handles decimal and hex. */ +static int +str2val(const char *str, const char *what, int mi, int ma) +{ + const char *cp; + long val; + char *ep; + + errno = 0; + ep = NULL; + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { + cp = str + 2; + val = strtol(cp, &ep, 16); + } else + val = strtol(str, &ep, 10); + if (errno || str[0] == '\0' || *ep != '\0') + errx(1, "\"%s\" bad value for %s", str, what); + if (val < mi && mi >= 0) { + if (mi == 0) + errx(1, "%s must be >= %d", what, mi); + else + errx(1, "%s must be > %d", what, mi - 1); + } + if (val > ma && ma >= 0) + errx(1, "%s must be <= %d", what, ma); + return (int)val; +} + +__dead void +usage(void) +{ + extern char version[]; + + Fprintf(stderr, "Version %s\n", version); + Fprintf(stderr, "Usage: %s [-adDFPIlMnrvx] [-g gateway] [-i iface] \ +[-f first_ttl]\n\t[-m max_ttl] [-p port] [-q nqueries] [-s src_addr] [-t tos]\n\t\ +[-w waittime] [-z pausemsecs] [-A as_server] host [packetlen]\n", + getprogname()); + exit(1); +} + +/* + * Received ICMP unreachable (fragmentation required and DF set). + * If the ICMP error was from a "new" router, it'll contain the next-hop + * MTU that we should use next. Otherwise we'll just keep going in the + * mtus[] table, trying until we hit a valid MTU. + */ + + +void +frag_err() +{ + int i; + + if (nextmtu > 0 && nextmtu < packlen) { + Printf("\nfragmentation required and DF set, " + "next hop MTU = %d\n", + nextmtu); + packlen = nextmtu; + for (i = 0; mtus[i] > 0; i++) { + if (mtus[i] < nextmtu) { + mtuptr = &mtus[i]; /* next one to try */ + break; + } + } + } else { + Printf("\nfragmentation required and DF set. "); + if (nextmtu) + Printf("\nBogus next hop MTU = %d > last MTU = %d. ", + nextmtu, packlen); + packlen = *mtuptr++; + Printf("Trying new MTU = %d\n", packlen); + } + resize_packet(); +} + +int +find_local_ip(struct sockaddr_in *from, struct sockaddr_in *to) +{ + int sock; + struct sockaddr_in help; + socklen_t help_len; + + sock = prog_socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) return 0; + + help.sin_family = AF_INET; + /* + * At this point the port number doesn't matter + * since it only has to be greater than zero. + */ + help.sin_port = 42; + help.sin_addr.s_addr = to->sin_addr.s_addr; + if (prog_connect(sock, (struct sockaddr *)&help, sizeof(help)) < 0) { + (void)prog_close(sock); + return 0; + } + + help_len = sizeof(help); + if (prog_getsockname(sock, (struct sockaddr *)&help, &help_len) < 0 || + help_len != sizeof(help) || + help.sin_addr.s_addr == INADDR_ANY) { + (void)prog_close(sock); + return 0; + } + + (void)prog_close(sock); + setsin(from, help.sin_addr.s_addr); + return 1; +} + +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC +static int +setpolicy(int so, const char *policy) +{ + char *buf; + + buf = ipsec_set_policy(policy, strlen(policy)); + if (buf == NULL) { + warnx("%s", ipsec_strerror()); + return -1; + } + (void)prog_setsockopt(so, IPPROTO_IP, IP_IPSEC_POLICY, + buf, ipsec_get_policylen(buf)); + + free(buf); + + return 0; +} +#endif +#endif + diff --git a/usr.sbin/traceroute/traceroute_hostops.c b/usr.sbin/traceroute/traceroute_hostops.c new file mode 100644 index 000000000..98016b8e2 --- /dev/null +++ b/usr.sbin/traceroute/traceroute_hostops.c @@ -0,0 +1,55 @@ +/* $NetBSD: traceroute_hostops.c,v 1.1 2010/12/15 00:09:41 pooka Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * 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 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 +#ifndef lint +__RCSID("$NetBSD: traceroute_hostops.c,v 1.1 2010/12/15 00:09:41 pooka Exp $"); +#endif /* !lint */ + +#include +#include +#include +#include + +#include +#include + +#include "prog_ops.h" + +const struct prog_ops prog_ops = { + .op_socket = socket, + .op_setsockopt = setsockopt, + .op_shutdown = shutdown, + .op_poll = poll, + .op_recvfrom = recvfrom, + .op_sendto = sendto, + .op_close = close, + .op_connect = connect, + .op_getsockname = getsockname, + .op_sysctl = sysctl, +}; diff --git a/usr.sbin/traceroute/traceroute_rumpops.c b/usr.sbin/traceroute/traceroute_rumpops.c new file mode 100644 index 000000000..f90cd17f6 --- /dev/null +++ b/usr.sbin/traceroute/traceroute_rumpops.c @@ -0,0 +1,59 @@ +/* $NetBSD: traceroute_rumpops.c,v 1.1 2010/12/15 00:09:42 pooka Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * 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 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 +#ifndef lint +__RCSID("$NetBSD: traceroute_rumpops.c,v 1.1 2010/12/15 00:09:42 pooka Exp $"); +#endif /* !lint */ + +#include +#include + +#include +#include + +#include +#include +#include + +#include "prog_ops.h" + +const struct prog_ops prog_ops = { + .op_init = rumpclient_init, + + .op_socket = rump_sys_socket, + .op_setsockopt= rump_sys_setsockopt, + .op_shutdown = rump_sys_shutdown, + .op_poll = rump_sys_poll, + .op_recvfrom = rump_sys_recvfrom, + .op_sendto = rump_sys_sendto, + .op_close = rump_sys_close, + .op_connect = rump_sys_connect, + .op_getsockname=rump_sys_getsockname, + .op_sysctl = rump_sys___sysctl, +}; diff --git a/usr.sbin/traceroute/trrt2netbsd b/usr.sbin/traceroute/trrt2netbsd new file mode 100755 index 000000000..bfe88f9c1 --- /dev/null +++ b/usr.sbin/traceroute/trrt2netbsd @@ -0,0 +1,272 @@ +#!/usr/local/bin/perl +# +# $NetBSD: trrt2netbsd,v 1.4 1999/06/16 20:47:57 is Exp $ +# +# Perl script to convert a standard distribution directory for traceroute into +# a NetBSD source tree. +# +# This is done as a script so that as each distribution is released, +# only changes from the previous one need to be dealt with as +# modifications to this script and related files. This should +# reduce the cost of updating from a new release of traceroute by an +# order of magnitude (or more?) +# +# This script requires two environment variables set: +# SRCDIR - traceroute source directory +# TARGETDIR - name of the high level directory to make +# +# Written by Christos Zoulas Oct 2nd, 1997 for traceroute-1.4a5 +# + +$version = "1.4a5"; + +# definitions ... + +@subdirs = ("usr.sbin/traceroute"); + +@trrtf = ("ifaddrlist.c", "savestr.c", "traceroute.c"); + +@trrthf = ("gnuc.h", "ifaddrlist.h", "savestr.h"); + +@trrtmf = ("traceroute.8"); +@trrtdf = ("CHANGES", "README"); +@trrtaf = ("mean.awk", "median.awk"); + + +# sed edit list: file, sed-program +%sedlist = (); + +# +# Utility Subroutines +# + +sub makedir { + system("mkdir -p @_"); +} + +# &fixrcs (fromfile, tofile); +sub fixrcs +{ + my ($f, $t) = @_; + my @keywords = ("Author", "Date", "Header", "Id", "Locker", "Log", + "Name", "RCSfile", "Revision", "Source", "State"); + my $state = 0; + my $hdr = 0; + + open(IFILE, "<$f") || die "Cannot open $f"; + open(OFILE, ">$t") || die "Cannot create $t"; + + if ($t =~ /.*\.[0-9]/) { + print OFILE '.\\" $', 'NetBSD', '$', "\n.\\\"", "\n"; + } + elsif ($t =~ /.*\.[ch]/) { + print OFILE "/*\t", '$', 'NetBSD', '$', "\t*/\n\n"; + } + elsif ($t =~ /.*\.[yl]/) { + $hdr = 1; + } + else { + print OFILE '$', 'NetBSD', '$', "\n"; + } + while () { + if ($hdr == 1) { + if (/%{/) { + print OFILE "%{\n/*\t", '$', 'NetBSD', '$', "\t*/\n\n"; + $hdr = 0; + next; + } + } + if ($state == 2) { + if (/#endif/) { + print OFILE "#else\n__RCSID(", '"$', 'NetBSD', '$"', + ");\n#endif\n"; + $state = 0; + } + } + if ($state == 1) { + print OFILE "#if 0\n"; + $state = 2; + } + if (/#ifndef lint/) { + print OFILE "#include \n"; + $state = 1; + } + foreach $key (@keywords) { + s/\$$key\$/$key/g; + s/\$$key:(.*)\$/$key:$1/g; + } + print OFILE $_; + } + close(IFILE) || die "closing input file"; + close(OFILE) || die "closing output file"; +} + +# ©files (fromdir, todir, list of files); +sub copyfiles { + local ($fdir, $tdir, @list) = @_; + local ($f); + + foreach $f (@list) { + print " $fdir/$f --> $tdir/$f\n"; + &fixrcs("$fdir/$f", "$tdir/$f"); + } +} + +# ©file (fromfile, tofile); +sub copyfile { + local ($f, $t) = @_; + + print " $f --> $t\n"; + system ("cp $f $t"); +} + +sub uniq { + local (@inlist) = @_; + local (@outlist); + + @outlist = ($inlist[0]); + for ( $i=1; $i < @inlist; $i++ ) { + if ($inlist[$i] ne $inlist[$i-1]) { + push (@outlist, $inlist[$i]); + } + } + + @outlist; +} + +sub dumpsrcs { + local (@names) = @_; + local ($count); + + $count = 0; + while ($f = pop(@names)) { + print ODATA "$f "; + if ($count == 5 && @names > 0) { + print ODATA "\\\n"; + $count = 0; + } else { + $count += 1; + } + } + if ($count != 0) { + print ODATA "\n"; + } +} + +# +# Main program. +# + +$srcdir = $ENV{'SRCDIR'}; +$targetdir = $ENV{'TARGETDIR'}; +$incdirs = "-I. -I$srcdir/config -I$srcdir"; + +if (!$srcdir | !targetdir) { + die "You must define the environment variables SRCDIR and TARGETDIR.\n" +} +print "Making the NetBSD directory tree.\n"; +foreach $f (@subdirs) { + print " -->$f\n"; + makedir ("$targetdir/$f"); +} + +print "Populating the usr.sbin/traceroute directory.\n"; +©files ("$srcdir", "$targetdir/usr.sbin/traceroute", @trrtf, @trrthf, @trrtdf, + @trrtmf, @trrtaf); + +# +# Build makefiles +# + +$first = "True"; +while ($line = ) { + chop ($line); + if (substr($line,0,2) eq "%%") { + @cmd = split (/ /,$line); + if ($cmd[1] eq "file") { + print "Building $targetdir/$cmd[2]\n"; + if ($first eq "") { + close (ODATA); + } else { + $first = ""; + } + open (ODATA, ">$targetdir/$cmd[2]") || + die "Could not create $targetdir/$cmd[2]"; + } elsif ($cmd[1] eq "awks") { + print " Defining AWKS\n"; + if ($first) { + die "Data file must start with a %% file!"; + } + print ODATA "AWKS=\t"; + &dumpsrcs (@trrtaf); + } elsif ($cmd[1] eq "srcs") { + print " Defining SRCS\n"; + if ($first) { + die "Data file must start with a %% file!"; + } + print ODATA "SRCS=\t"; + &dumpsrcs (@trrtf); + } elsif ($cmd[1] eq "man") { + print " Defining MAN\n"; + if ($first) { + die "Data file must start with a %% file!"; + } + print ODATA "MAN=\t"; + &dumpsrcs (@trrtmf); + } elsif ($cmd[1] eq "version") { + print " Defining VERSION\n"; + print ODATA "char version[] = \"$version\";"; + } elsif ($cmd[1] eq "NetBSD") { + if ($first) { + die "Data section must start with a %% file!"; + } + print ODATA "$cmd[2] \$"."NetBSD".": \$ $cmd[3]\n"; + } + } else { + if ($first) { + die "Data file must start with a %% file!"; + } + print ODATA "$line\n"; + } +} +close (ODATA); + +# +# Sed transformations of files +# +foreach $n (keys(%sedlist)) { + print "Modifying $n\n"; + system ("cd $targetdir; sed $sedlist{$n} $n > tmp; mv -f tmp $n"); +} + +# +# end of the script +# + +# what follows is the data for makefiles and other special files +# that need to be created. + +__END__ +%% file usr.sbin/traceroute/Makefile +%% NetBSD # + +WARNS?= 1 +PROG= traceroute +%% man + +CPPFLAGS+=-DHAVE_MALLOC_H=1 -DHAVE_SYS_SELECT_H=1 -DHAVE_SYS_SOCKIO_H=1 +CPPFLAGS+=-DHAVE_STRERROR=1 -DHAVE_SETLINEBUF=1 -DHAVE_SOCKADDR_SA_LEN=1 +CPPFLAGS+=-DHAVE_RAW_OPTIONS=1 + +BINOWN= root +BINMODE=4555 + +%% srcs +SRCS+= version.c + +%% awks + +.include +%% file usr.sbin/traceroute/version.c +%% NetBSD /* */ +%% version diff --git a/usr.sbin/traceroute/version.c b/usr.sbin/traceroute/version.c new file mode 100644 index 000000000..8f987c150 --- /dev/null +++ b/usr.sbin/traceroute/version.c @@ -0,0 +1,2 @@ +/* $NetBSD: version.c,v 1.3 2011/09/11 01:06:26 christos Exp $ */ +char version[] = "1.4a12";