diff -ru tcptraceroute-1.1/Makefile tcptraceroute-1.2/Makefile --- tcptraceroute-1.1/Makefile Sun Jul 1 23:57:29 2001 +++ tcptraceroute-1.2/Makefile Tue Jul 31 23:52:40 2001 @@ -1,5 +1,3 @@ -# vim:set ts=4 sw=4 ai: - # tcptraceroute -- A traceroute implementation using TCP packets # Copyright (c) 2001, Michael C. Toren @@ -20,6 +18,9 @@ distrib: clean changelog man +clean: + rm -f core a.out tcptraceroute *~ + changelog: tcptraceroute.c Makefile perl -000 -ne 'next unless (/\*\s+Revision\s+history:/); \ print "Extracted from tcptraceroute.c:\n\n$$_"; exit;' \ @@ -28,6 +29,3 @@ man: tcptraceroute.8.html Makefile tcptraceroute.8.html: tcptraceroute.8 rman -fHTML -r- tcptraceroute.8 > tcptraceroute.8.html - -clean: - rm -f core a.out tcptraceroute diff -ru tcptraceroute-1.1/changelog tcptraceroute-1.2/changelog --- tcptraceroute-1.1/changelog Sun Jul 1 23:57:44 2001 +++ tcptraceroute-1.2/changelog Tue Jul 31 23:53:11 2001 @@ -3,6 +3,46 @@ /* * Revision history: * + * Version 1.2 (2001-07-31) + * + * Contains large portions of code and ideas contributed by + * Scott Gifford + * + * Attempt to determine what outgoing interface to use based on the + * destination address and the local system's interface list. Could + * still use a good deal of work on BSD systems, though, especially + * when it comes to virtual addresses which reside on subnets + * different than the primary address. + * + * The timeout code has been reworked significantly, and should now + * be much more reliable. + * + * Added -E command line argument to send ECN (RFC2481) packets. + * Requested by Christophe Barb and + * Jim Penny + * + * Added -l command line argument to set the total packet length, + * including IP header. + * + * Added support for sending more than one probe to each hop, and + * the -q command line option to specify the number of probes. + * + * Added -F command line argument to set the IP_DF bit. + * + * Added -t command line argument to set the IP TOS. + * + * Now properly checks the length of the packets returned by libpcap + * before blindly assuming that the entire header structure we happen + * to be looking for is there. This could have been very ugly had the + * snaplen not been set so conservatively. + * + * Print banner information to stderr, not stdout, to be compatible + * with traceroute(8). Reported by Scott Fenton + * + * Fixed an endian bug reported by Zoran Dzelajlija , + * which prevented users from specifying the destination port number by + * name. + * * Version 1.1 (2001-06-30) * * Now drops root privileges after sockets have been opened. @@ -14,7 +54,5 @@ * Version 1.0 (2001-04-10) * * Initial Release - * - * Updates are available from http://michael.toren.net/code/tcptraceroute/ */ Only in tcptraceroute-1.1/debian: README.Debian diff -ru tcptraceroute-1.1/debian/changelog tcptraceroute-1.2/debian/changelog --- tcptraceroute-1.1/debian/changelog Sun Jul 1 13:33:52 2001 +++ tcptraceroute-1.2/debian/changelog Wed Aug 1 00:46:33 2001 @@ -1,3 +1,10 @@ +tcptraceroute (1.2-0potato1) stable; urgency=low + + * New upstream version. + * Closes: #106666: /etc/services ports read wrong + + -- Michael C. Toren Tue, 31 Jul 2001 23:22:04 -0400 + tcptraceroute (1.1-1) unstable; urgency=low * Initial Release. diff -ru tcptraceroute-1.1/debian/control tcptraceroute-1.2/debian/control --- tcptraceroute-1.1/debian/control Sun Jul 1 21:10:07 2001 +++ tcptraceroute-1.2/debian/control Tue Jul 31 23:45:05 2001 @@ -2,14 +2,13 @@ Section: net Priority: optional Maintainer: Michael C. Toren -Standards-Version: 3.0.1 +Build-Depends: perl, libnet0-dev, debhelper +Standards-Version: 3.5.5 Package: tcptraceroute Architecture: any Depends: ${shlibs:Depends} Description: A traceroute implementation using TCP packets - A traceroute implementation using TCP packets - . The more traditional traceroute(8) sends out either UDP or ICMP ECHO packets with a TTL of one, and increments the TTL until the destination has been reached. By printing the gateways that generate ICMP time diff -ru tcptraceroute-1.1/debian/rules tcptraceroute-1.2/debian/rules --- tcptraceroute-1.1/debian/rules Sun Jul 1 23:57:04 2001 +++ tcptraceroute-1.2/debian/rules Wed Aug 1 00:31:34 2001 @@ -8,14 +8,16 @@ # This is the debhelper compatability version to use. export DH_COMPAT=1 +ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) +export CFLAGS = -O2 -Wall -g +endif + build: build-stamp build-stamp: dh_testdir - # Add here commands to compile the package. - $(MAKE) tcptraceroute changelog man - + $(MAKE) tcptraceroute touch build-stamp clean: @@ -60,13 +62,14 @@ # dh_undocumented dh_installchangelogs changelog dh_link +ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) dh_strip +endif dh_compress dh_fixperms # You may want to make some executables suid here. chmod 4755 debian/tmp/usr/sbin/tcptraceroute chown root.root debian/tmp/usr/sbin/tcptraceroute - dh_suidregister # dh_makeshlibs dh_installdeb # dh_perl diff -ru tcptraceroute-1.1/examples.txt tcptraceroute-1.2/examples.txt --- tcptraceroute-1.1/examples.txt Sun Jul 1 22:54:33 2001 +++ tcptraceroute-1.2/examples.txt Wed Aug 1 00:21:35 2001 @@ -1,8 +1,8 @@ -A few real-life examples of using tcptraceroute to trace through firewalls -that traceroute(8) has trouble with. These are all sites that pass TCP SYN -packets on to hosts sitting on the clean side of the firewall, and which -don't filter ICMP time exceeded messages leaving their network. All examples -listed below were captured Sunday Night, July 1, 2001. +A few real world examples of using tcptraceroute to trace through +firewalls that traceroute(8) has trouble with. These are all sites +that pass TCP SYN packets on to hosts sitting on the clean side of the +firewall, and which don't filter ICMP time exceeded messages leaving +their network. All examples listed below were captured on July 1st. -- Michael C. Toren Sun, 1 Jul 2001 21:25:26 -0400 diff -ru tcptraceroute-1.1/tcptraceroute.8 tcptraceroute-1.2/tcptraceroute.8 --- tcptraceroute-1.1/tcptraceroute.8 Sun Jul 1 22:55:21 2001 +++ tcptraceroute-1.2/tcptraceroute.8 Tue Jul 31 21:08:33 2001 @@ -1,13 +1,21 @@ -.TH TCPTRACEROUTE 8 "2001 June 30" +.TH TCPTRACEROUTE 8 "2001 July 31" .SH NAME tcptraceroute \- A traceroute implementation using TCP packets .SH SYNOPSIS -.B tcptraceroute [\-n] [ \-i +.B tcptraceroute [\-nFE] [ \-i .I interface .B ] [ \-f .I first ttl .B ] .br +.B [ \-l +.I length +.B ] [ \-q +.I number of queries +.B ] [ \-t +.I tos +.B ] +.br .B [ \-m .I max ttl .B ] [ \-p @@ -23,6 +31,9 @@ .B [ .I destination port .B ] +.B [ +.I length +.B ] .SH DESCRIPTION .B tcptraceroute is a traceroute implementation using TCP packets. @@ -32,8 +43,7 @@ sends out either UDP or ICMP ECHO packets with a TTL of one, and increments the TTL until the destination has been reached. By printing the gateways that generate ICMP time exceeded messages along the way, it is able to determine the -path packets are taking to reach the destination. It is a very useful network -diagnostic tool. +path packets are taking to reach the destination. .P The problem is that with the widespread use of firewalls on the modern Internet, many of the packets that @@ -50,13 +60,14 @@ .B tcptraceroute never completely establishes a TCP connection with the destination host. If the host is not listening for incoming connections, it will respond with -an RST indicating that the port is closed. If the hosts instead responds +an RST indicating that the port is closed. If the host instead responds with a SYN|ACK, the port is known to be open, and an RST is sent by the -kernel that +kernel .B tcptraceroute -is running on to tear down the connection before the three\-way handshake has -been completed. This is the same half\-open scanning technique that -.IR namp (1) +is running on to tear down the connection without completing +three\-way handshake. This is the same half\-open scanning technique +that +.IR nmap (1) uses when passed the .BR \-sS flag. @@ -65,10 +76,6 @@ Display numeric output, rather than doing a reverse DNS lookup for each hop. Reverse lookups are never attempted on RFC1918 address space, regardless of the \-n flag. -.IP \-i -Use the specified interface for outgoing packets. The default is to use -the interface retuned by pcap_lookupdev(), which may or may not be -appropriate on a multihomed host. .IP \-f Set the initial TTL used in the first outgoing packet. The default is 1. .IP \-m @@ -77,40 +84,48 @@ Use the specified local TCP port in outgoing packets. The default is to obtain a free port from the kernel using .IR bind (2). -Unlike with traditionial +Unlike with traditional .IR traceroute (8), this number will not increase with each hop. .IP \-s Set the source address for outgoing packets. See also the \-i flag. -.IP \-d -Enable debugging, which probably isn't very useful to you. +.IP \-i +Use the specified interface for outgoing packets. +.IP \-q +Set the number of probes to be sent to each hop. The default is 3. +.IP \-t +Set the IP type of service to be used in outgoing packets. The default is +to not set any type of service option. +.IP \-F +Set the "don't fragment" bit in outgoing packets. +.IP \-E +Send ECN SYN packets, as described in RFC2481. .IP \-w Set the timeout, in seconds, to wait for a response for each probe. The default is 3. +.IP \-l +Set the total packet length to be used in outgoing packets. If the length +is greater than the minimum size required to assemble the necessary probe +packet headers, this value is automatically increased. +.IP \-d +Enable debugging, which may or may not be useful. .SH EXAMPLES Please see the .I examples.txt file included in the .B tcptraceroute -distribution for a few real\-life examples. +distribution for a few real world examples. .P -To trace the path to a webserver listening for connections on port 80: +To trace the path to a web server listening for connections on port 80: .P .RS .B tcptraceroute webserver .RE .P -To trace the path to a nameserver listening for connections on port 53: -.P -.RS -.B tcptraceroute nameserver 53 -.RE -.P -If the machine you are tracing from has more than one interface, and -the incorrect interface is being selected by default: +To trace the path to a mail server listening for connections on port 25: .P .RS -.B tcptraceroute \-i eth1 host +.B tcptraceroute mailserver 25 .RE .P .SH BUGS @@ -120,9 +135,6 @@ tcptraceroute to send out TCP SYN packets for which it has no chance of seeing a response to. -.P -Sending more than one probe to each hop is not currently supported, but -should be trivial to implement in the future if desired. .P Complete portability to other Unix systems has not been tested; specifically, diff -ru tcptraceroute-1.1/tcptraceroute.8.html tcptraceroute-1.2/tcptraceroute.8.html --- tcptraceroute-1.1/tcptraceroute.8.html Sun Jul 1 23:48:44 2001 +++ tcptraceroute-1.2/tcptraceroute.8.html Tue Jul 31 21:09:16 2001 @@ -12,91 +12,106 @@ tcptraceroute - A traceroute implementation using TCP packets

Synopsis

tcptraceroute -[-n] [ -i interface ] [ -f first ttl ]
+[-nFE] [ -i interface ] [ -f first ttl ]
+[ -l length ] [ -q number of queries ] [ -t tos ]
[ -m max ttl ] [ -p source port ] [ -s source address ]
-[ -w wait time ] host [ destination port ] +[ -w wait time ] host [ destination port ] [ length ]

Description

-tcptraceroute is a -traceroute implementation using TCP packets.

+tcptraceroute +is a traceroute implementation using TCP packets.

The more traditional traceroute(8) sends out either UDP or ICMP ECHO packets with a TTL of one, and increments the TTL until the destination has been reached. By printing the gateways that generate ICMP time exceeded messages along the way, it is able to -determine the path packets are taking to reach the destination. It is a -very useful network diagnostic tool.

-The problem is that with the widespread -use of firewalls on the modern Internet, many of the packets that traceroute(8) -sends out end up being filtered, making it impossible to completely trace -the path to the destination. However, in many cases, these firewalls will -permit inbound TCP packets to specific ports that hosts sitting behind -the firewall are listening for connections on. By sending out TCP SYN packets -instead of UDP or ICMP ECHO packets, tcptraceroute is able to bypass the -most common firewall filters.

-It is worth noting that tcptraceroute never -completely establishes a TCP connection with the destination host. If the -host is not listening for incoming connections, it will respond with an -RST indicating that the port is closed. If the hosts instead responds with -a SYN|ACK, the port is known to be open, and an RST is sent by the kernel -that tcptraceroute is running on to tear down the connection before the -three-way handshake has been completed. This is the same half-open scanning -technique that namp(1) uses when passed the -sS flag. +determine the path packets are taking to reach the destination.

+The problem +is that with the widespread use of firewalls on the modern Internet, many +of the packets that traceroute(8) sends out end up being filtered, making +it impossible to completely trace the path to the destination. However, +in many cases, these firewalls will permit inbound TCP packets to specific +ports that hosts sitting behind the firewall are listening for connections +on. By sending out TCP SYN packets instead of UDP or ICMP ECHO packets, +tcptraceroute is able to bypass the most common firewall filters.

+It is +worth noting that tcptraceroute never completely establishes a TCP connection +with the destination host. If the host is not listening for incoming connections, +it will respond with an RST indicating that the port is closed. If the +host instead responds with a SYN|ACK, the port is known to be open, and +an RST is sent by the kernel tcptraceroute is running on to tear down the +connection without completing three-way handshake. This is the same half-open +scanning technique that nmap(1) uses when passed the -sS flag.

Options

-n
-
Display numeric -output, rather than doing a reverse DNS lookup for each hop. Reverse lookups -are never attempted on RFC1918 address space, regardless of the -n flag. -
- -
-i
-
Use the specified interface for outgoing packets. The default is to use -the interface retuned by pcap_lookupdev(), which may or may not be appropriate -on a multihomed host.
+
Display +numeric output, rather than doing a reverse DNS lookup for each hop. Reverse +lookups are never attempted on RFC1918 address space, regardless of the +-n flag.
-f
-
Set the initial TTL used in the first outgoing packet. - The default is 1.
+
Set the initial TTL used in the first outgoing packet. The default +is 1.
-m
-
Set the maximum TTL used in outgoing packets. The default -is 30.
+
Set the maximum TTL used in outgoing packets. The default is 30.
-p
-
Use the specified local TCP port in outgoing packets. The default -is to obtain a free port from the kernel using bind(2). Unlike with traditionial -traceroute(8), this number will not increase with each hop.
+
Use +the specified local TCP port in outgoing packets. The default is to obtain +a free port from the kernel using bind(2). Unlike with traditional traceroute(8), +this number will not increase with each hop.
-s
-
Set the source -address for outgoing packets. See also the -i flag.
+
Set the source address for +outgoing packets. See also the -i flag.
-
-d
-
Enable debugging, which -probably isn't very useful to you.
+
-i
+
Use the specified interface for +outgoing packets.
+ +
-q
+
Set the number of probes to be sent to each hop. The +default is 3.
+ +
-t
+
Set the IP type of service to be used in outgoing packets. + The default is to not set any type of service option.
+ +
-F
+
Set the "don't fragment" +bit in outgoing packets.
+ +
-E
+
Send ECN SYN packets, as described in RFC2481. +
-w
-
Set the timeout, in seconds, to wait -for a response for each probe. The default is 3.
+
Set the timeout, in seconds, to wait for a response for each probe. The +default is 3.
+ +
-l
+
Set the total packet length to be used in outgoing packets. + If the length is greater than the minimum size required to assemble the +necessary probe packet headers, this value is automatically increased.
+ +
-d
+
Enable +debugging, which may or may not be useful.

Examples

-Please see the -examples.txt file included in the tcptraceroute distribution for a few real-life -examples.

-To trace the path to a webserver listening for connections on -port 80:

-

tcptraceroute webserver
+Please see the examples.txt +file included in the tcptraceroute distribution for a few real world examples. +

+To trace the path to a web server listening for connections on port 80:

-To trace the path to a nameserver listening -for connections on port 53:

-

tcptraceroute nameserver 53
+
tcptraceroute webserver

-If the machine -you are tracing from has more than one interface, and the incorrect interface -is being selected by default:

-

tcptraceroute -i eth1 host
+To trace the path to a mail server listening for +connections on port 25:

+

tcptraceroute mailserver 25

Bugs

@@ -104,20 +119,17 @@ is performed on the source address specified by the -s flag, and it is therefore possible for tcptraceroute to send out TCP SYN packets for which it has no chance of seeing a response to.

-Sending more than one probe to each hop -is not currently supported, but should be trivial to implement in the future -if desired.

-Complete portability to other Unix systems has not been tested; -specifically, tcptraceroute will not function on systems which modify -the IP ID field of packets written to a raw socket. As of the time of this -writing, tcptraceroute is known to compile and function properly on Linux, -OpenBSD, and FreeBSD systems. If you run into complications on another -platform, please let me know. +Complete portability to other Unix systems +has not been tested; specifically, tcptraceroute will not function on +systems which modify the IP ID field of packets written to a raw socket. + As of the time of this writing, tcptraceroute is known to compile and +function properly on Linux, OpenBSD, and FreeBSD systems. If you run into +complications on another platform, please let me know.

Author

-Michael C. Toren <mct@toren.net> +Michael C. Toren +<mct@toren.net>

Availability

-For -updates, please see:
+For updates, please see:
http://michael.toren.net/code/tcptraceroute/

See Also

diff -ru tcptraceroute-1.1/tcptraceroute.c tcptraceroute-1.2/tcptraceroute.c --- tcptraceroute-1.1/tcptraceroute.c Sun Jul 1 15:37:57 2001 +++ tcptraceroute-1.2/tcptraceroute.c Tue Jul 31 21:05:52 2001 @@ -1,3 +1,4 @@ +/* -*- Mode: c; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4; -*- */ /* vim:set ts=4 sw=4 ai nobackup nocindent: */ /* @@ -26,11 +27,53 @@ * * gcc -O2 -Wall `libnet-config --defines` \ * -o tcptraceroute tcptraceroute.c `libnet-config --libs` -lpcap + * + * Updates are available from http://michael.toren.net/code/tcptraceroute/ */ /* * Revision history: * + * Version 1.2 (2001-07-31) + * + * Contains large portions of code and ideas contributed by + * Scott Gifford + * + * Attempt to determine what outgoing interface to use based on the + * destination address and the local system's interface list. Could + * still use a good deal of work on BSD systems, though, especially + * when it comes to virtual addresses which reside on subnets + * different than the primary address. + * + * The timeout code has been reworked significantly, and should now + * be much more reliable. + * + * Added -E command line argument to send ECN (RFC2481) packets. + * Requested by Christophe Barb and + * Jim Penny + * + * Added -l command line argument to set the total packet length, + * including IP header. + * + * Added support for sending more than one probe to each hop, and + * the -q command line option to specify the number of probes. + * + * Added -F command line argument to set the IP_DF bit. + * + * Added -t command line argument to set the IP TOS. + * + * Now properly checks the length of the packets returned by libpcap + * before blindly assuming that the entire header structure we happen + * to be looking for is there. This could have been very ugly had the + * snaplen not been set so conservatively. + * + * Print banner information to stderr, not stdout, to be compatible + * with traceroute(8). Reported by Scott Fenton + * + * Fixed an endian bug reported by Zoran Dzelajlija , + * which prevented users from specifying the destination port number by + * name. + * * Version 1.1 (2001-06-30) * * Now drops root privileges after sockets have been opened. @@ -42,40 +85,66 @@ * Version 1.0 (2001-04-10) * * Initial Release - * - * Updates are available from http://michael.toren.net/code/tcptraceroute/ */ /* * TODO: * - * - There needs to be a better way to detect a timeout from pcap_next() - * - Add support for sending more than one probe. * - RESOLVE_1918 should be a runtime, command line option. + * - Command line arguments could be handled better. + * - Display if the remote host is ECN capable when using o_ecn? + * - Currently it is not possible to traceroute to yourself. + * - finddev() doesn't detect virtual addresses on BSD systems. + * - We really should be using GNU autoconf. */ -#define VERSION "tcptraceroute 1.1 (2001-06-30)" -#define BANNER "\ -Copyright (c) 2001, Michael C. Toren -Updates are available from http://michael.toren.net/code/tcptraceroute/ -" - -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include +#include +#include +#include +#include +#include + +#ifndef SIOCGIFCONF +#include /* Solaris, maybe others? */ +#endif + +#ifndef AF_LINK +#define AF_LINK AF_INET /* BSD defines some AF_INET network interfaces as AF_LINK */ +#endif + +/* ECN (RFC2481) */ +#ifndef TH_ECN +#define TH_ECN 0x40 +#endif +#ifndef TH_CWR +#define TH_CWR 0x80 +#endif + +#define VERSION "tcptraceroute 1.2 (2001-07-31)" +#define BANNER "Copyright (c) 2001, Michael C. Toren \n\ +Updates are available from http://michael.toren.net/code/tcptraceroute/\n" /* Buffer size used for a few strings, including the pcap filter */ #define TEXTSIZE 1024 +/* Should we attempt to resolve RFC1918 address space? */ +#undef RESOLVE_1918 + /* * How many bytes should we examine on every packet that comes off the - * wire? This doesn't include the link layer, which is accounted for + * wire? This doesn't include the link layer which is accounted for * later. We're looking only for ICMP and TCP packets, so this should * work. For ICMP, we also examine the quoted IP header, which is why * there's a *2 there. The +32 is just to be safe. @@ -84,23 +153,24 @@ #define SNAPLEN (LIBNET_IP_H * 2 + \ (LIBNET_TCP_H > LIBNET_ICMP_H ? LIBNET_TCP_H : LIBNET_ICMP_H) + 32) -/* pcap error buffer */ -char errbuf[PCAP_ERRBUF_SIZE]; - -/* various globals */ +/* Various globals */ u_long dst_ip, src_ip; u_short src_prt, dst_prt; -int sockfd, datalink, offset, minttl, maxttl, timeout; -int o_debug, o_numeric; char *device, *name, *dst, *src; -char dst_name[TEXTSIZE+1], dst_prt_name[TEXTSIZE+1], filter[TEXTSIZE+1]; +char dst_name[TEXTSIZE], dst_prt_name[TEXTSIZE], filter[TEXTSIZE]; +char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *pcap; -u_char *buf; struct timeval t1, t2; +int sockfd, datalink, offset; +int o_minttl, o_maxttl, o_timeout, o_debug, o_numeric, o_pktlen, + o_nqueries, o_dontfrag, o_tos, o_forceport, o_ecn; + +extern char pcap_version[]; +extern int errno; /* - * fatal() and pfatal() are useful stdarg functions from namp. debug() is - * based on them. + * fatal() and pfatal() are useful stdarg functions from + * namp. debug() and warn() are based on them. */ void fatal(char *fmt, ...) @@ -113,40 +183,159 @@ exit(1); } -void pfatal(char *err) +void debug(char *fmt, ...) { + va_list ap; + if (! o_debug) return; fflush(stdout); - perror(err); - exit(1); + fprintf(stderr, "debug: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fflush(stderr); } -void debug(char *fmt, ...) +void warn(char *fmt, ...) { va_list ap; - if (! o_debug) return; fflush(stdout); - fprintf(stderr, "debug: "); + fprintf(stderr, "Warning: "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fflush(stderr); } +void pfatal(char *err) +{ + debug("errno == %d\n", errno); + fflush(stdout); + perror(err); + exit(1); +} + void usage(void) { printf("\n%s\n%s\n", VERSION, BANNER); - fatal("Usage: %s [-n] [-i ] [-f ] - [-m ] [-p ] [-s ] - [-w ] [destination port]\n\n", name); + fatal("Usage: %s [-nFE] [-i ] [-f ] + [-l ] [-q ] [-t ] + [-m ] [-[pP] ] [-s ] + [-w ] [destination port] [packet length]\n\n", name); } void about(void) { printf("\n%s\n%s\n", VERSION, BANNER); + debug("Compiled with libpcap version %s\n\n",pcap_version); exit(0); } /* + * realloc(3) or bust! + */ + +void *xrealloc(void *oldp, int size) +{ + void *p; + + if (!oldp) + { + /* Kludge for SunOS, which doesn't allow realloc on a NULL pointer */ + oldp = malloc(1); + if (!oldp) + fatal("Out of memory! Could not allocate 1 byte!\n"); + } + + if (! (p = realloc(oldp, size))) + fatal("Out of memory! Could not reallocate %d bytes!X\n", size); + + return p; +} + +/* + * Same as strncpy, but always be sure the result is terminated. + */ + +char *safe_strncpy(char *dst, const char *src, int size) +{ + dst[size-1] = '\0'; + return strncpy(dst, src, size-1); +} + +/* + * return a pointer to a string containing only the + * printable characters of the string passed to it. + */ + +char *sprintable(char *s) +{ + static char buf[TEXTSIZE]; + int i; + + for (i = 0; s[i]; i++) + { + if (i == TEXTSIZE) + break; + + buf[i] = isprint(s[i]) ? s[i] : '?'; + } + + buf[i] = '\0'; + + if (i == 0) + safe_strncpy(buf, "(empty)", TEXTSIZE); + + return buf; +} + +/* + * Compute the difference between two timeval structures. + */ + +struct timeval tvdiff(struct timeval *tv1, struct timeval *tv2) +{ + struct timeval tvdiff; + + tvdiff.tv_sec = tv1->tv_sec - tv2->tv_sec; + tvdiff.tv_usec = tv1->tv_usec - tv2->tv_usec; + + if ((tvdiff.tv_sec > 0) && (tvdiff.tv_usec < 0)) + { + tvdiff.tv_usec += 1000000L; + tvdiff.tv_sec--; + } + + else if ((tvdiff.tv_sec < 0) && (tvdiff.tv_usec > 0)) + { + tvdiff.tv_usec -= 1000000L; + tvdiff.tv_sec++; + } + + return tvdiff; +} + + +/* + * Is the timeval less than, equal to, or greater than zero? + */ + +int tvsign(struct timeval *tv) +{ + if (tv->tv_sec < 0) return -1; + + if (tv->tv_sec == 0) + { + if (tv->tv_usec < 0) return -1; + if (tv->tv_usec == 0) return 0; + if (tv->tv_usec > 0) return 1; + } + + if (tv->tv_sec > 0) return 1; + + return -1; +} + +/* * Inspired by libnet_host_lookup(), but I needed more than 2 buffers while * I was debugging. I really could get by with only 2 now, but *shrug*. */ @@ -165,9 +354,9 @@ } /* - * A wrapper for libnet_host_lookup() which doesn't attempt to resolve - * RFC1918 space. If you don't want this feature, compile with RESOLVE_1918 - * defined. + * A wrapper for libnet_host_lookup() with the option not to resolve + * 1918 space. This #define should be moved to a command line argument + * at some point. */ char *iptohost(u_long in) @@ -189,10 +378,146 @@ } /* + * Determines the source address that should be used to reach the + * given destination address. + */ + +u_long findsrc(u_long dest) +{ + struct sockaddr_in sinsrc, sindest; + int s, size; + + if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + pfatal("socket error"); + + memset(&sinsrc, 0, sizeof(struct sockaddr_in)); + memset(&sindest, 0, sizeof(struct sockaddr_in)); + + sindest.sin_family = AF_INET; + sindest.sin_addr.s_addr = dest; + sindest.sin_port = htons(53); /* can be anything */ + + if (connect(s, (struct sockaddr *)&sindest, sizeof(sindest)) < 0) + pfatal("connect"); + + size = sizeof(sinsrc); + if (getsockname(s, (struct sockaddr *)&sinsrc, &size) < 0) + pfatal("getsockname"); + + close(s); + debug("Determined source address of %s to reach %s\n", + iptos(sinsrc.sin_addr.s_addr), iptos(dest)); + return sinsrc.sin_addr.s_addr; +} + +/* + * Locates the device name matching the given source address. For + * virtual hosts under Linux and Solaris, returns the portions of the + * name before the first ":" character. Unfortunately, this won't + * match virtual hosts under OpenBSD. + */ + +char *finddev(u_long src) +{ + struct ifconf ifc; + struct ifreq *ifrp, ifr; + int numreqs, n, i, s; + char *device; + + device = NULL; + ifc.ifc_buf = NULL; + + /* + * The initial request length needs to be somewhat large, because + * the incrementing technique doesn't work on BSD(?) But, it can't + * be too large, or ioct() will complain (on Linux at least) about + * not being able to allocate enough memory for the request. + */ + + numreqs = 1024; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + fatal("socket error"); + + debug("ifreq buffer set to %d\n", numreqs); + + for (;;) + { + ifc.ifc_len = sizeof(struct ifreq) * numreqs; + ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len); + + if (ioctl(s, SIOCGIFCONF, &ifc) < 0) + pfatal("ioctl"); + + if (ifc.ifc_len >= sizeof(struct ifreq) * numreqs) + { + /* Assume it overflowed and try again */ + numreqs += 1024; + debug("ifreq buffer grown to %d\n", numreqs); + continue; + } + + break; + } + + debug("successfully retrieved interface list\n"); + + for (n = 0, ifrp = ifc.ifc_req; + n < ifc.ifc_len; + n += sizeof(struct ifreq), ifrp++) + { + u_long addr; + + memset(&ifr, 0, sizeof(struct ifreq)); + strcpy(ifr.ifr_name, ifrp->ifr_name); + + if (ifrp->ifr_addr.sa_family != AF_INET && + ifrp->ifr_addr.sa_family != AF_LINK) + { + debug("ignoring non-AF_INET interface %s\n", sprintable(ifr.ifr_name)); + continue; + } + + if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) + pfatal("ioctl(SIOCGIFFLAGS)"); + + if ((ifr.ifr_flags & IFF_UP) == 0) + { + debug("Ignoring down interface %s\n", sprintable(ifr.ifr_name)); + continue; + } + + if (ioctl(s, SIOCGIFADDR, &ifr) < 0) + pfatal("ioctl(SIOCGIFADDR)"); + + addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; + + debug("Discovered interface %s with address %s\n", + sprintable(ifr.ifr_name), iptos(addr)); + + if (addr == src) + { + debug("Interface %s matches source address %s\n", + sprintable(ifr.ifr_name), iptos(src)); + device = xrealloc(NULL, sizeof(ifr.ifr_name+1)); + strcpy(device, ifr.ifr_name); + + /* Deal with virtual hosts */ + for (i = 0; device[i]; i++) + if (device[i] == ':') + device[i] = '\0'; + } + } + + free(ifc.ifc_buf); + return device; +} + +/* * To add support for additional link layers, add entries to datalinkoffset() * and datalinkname(). The numbers I have in here now I believe are correct, - * and were obtained by looking through other pcap programs, however I've - * only tested tcptraceroute on ethernet interfaces. + * and were obtained by looking through other pcap programs, however I have + * only tested tcptraceroute on ethernet and Linux PPP interfaces. */ int datalinkoffset(int type) @@ -213,6 +538,8 @@ char *datalinkname(int type) { + static char name[TEXTSIZE]; + switch (type) { case DLT_EN10MB: return "ETHERNET"; @@ -224,25 +551,126 @@ case DLT_IEEE802: return "IEEE802"; case DLT_ATM_RFC1483: return "ATM"; case DLT_RAW: return "RAW"; - default: return "UNKNOWN"; + + default: + snprintf(name, TEXTSIZE, "#%d", type); + return name; } } +/* + * What a kludge, but it works. The aim is to be as compatible as possible + * with traceroute(8), with the one exception that if for the same hop we + * receive a response from two different hosts, display the second host on + * a new line, as Cisco does. This drastically improves readability when + * tracing through links which have per-packet, round-robin load balancing. + */ + +void showprobe(int ttl, int q, u_long addr, char *state, char *fmt, ...) +{ + /* Variables to keep state between calls */ + static char laststate[TEXTSIZE]; + static int lastttl; + static u_long lastaddr; + static int everprint; + + int printflag = 0; + va_list ap; + + /* ttl */ + if (lastttl != ttl) + { + printf("%2d ", ttl); + printflag = 1; + everprint = 0; + safe_strncpy(laststate, "", TEXTSIZE); + } + else if (lastaddr != addr && addr != INADDR_ANY && lastaddr != INADDR_ANY) + { + printf("\n "); + printflag = 1; + } + + /* host */ + if ((printflag || !everprint) && addr != INADDR_ANY) + { + if (q > 1 && lastaddr == INADDR_ANY) + printf(" "); + + printf("%s (%s)", iptohost(addr), iptos(addr)); + everprint = 1; + } + + /* tcp state */ + if ( ((ttl != lastttl) && state) || + ((ttl == lastttl) && state && (strncmp(laststate, state, TEXTSIZE) != 0))) + { + printf(" [%s]", state); + } + + /* space before ms */ + if (! (addr == INADDR_ANY && q == 1)) + { + /* if timeout, only print one space. otherwise, two */ + if ((addr == INADDR_ANY) || (lastaddr == INADDR_ANY)) + printf(" "); + else + printf(" "); + } + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + + if (q == o_nqueries) + printf("\n"); + + lastttl = ttl; + lastaddr = addr; + if (state) + safe_strncpy(laststate, state, TEXTSIZE); + + fflush(stdout); +} + + /* * Check command line arguments for sanity, and fill in the blanks. */ void defaults(void) { - struct libnet_link_int l; - struct protoent *proto; struct sockaddr_in in; struct servent *serv; int insize; + u_long recommended_src; + + if ((dst_ip = libnet_name_resolve(dst, 1)) == 0xFFFFFFFF) + fatal("Bad destination address: %s\n", dst); + + recommended_src = findsrc(dst_ip); + + if (src) + { + if ((src_ip = libnet_name_resolve(src, 1)) == 0xFFFFFFFF) + fatal("Bad source address: %s\n", src); + } + else + src_ip = recommended_src; if (device == NULL) - if ((device = pcap_lookupdev(errbuf)) == NULL) - fatal("Could not determine device: %\n", errbuf); + /* not specified on command line */ + device = finddev(recommended_src); + + if (device == NULL) + { + /* couldn't find an interface matching recommended_src */ + warn("Could not determine appropriate device; resorting to pcap_lookupdev()\n"); + device = pcap_lookupdev(errbuf); + } + + if (device == NULL) + fatal("Could not determine device via pcap_lookupdev(): %\n", errbuf); if ((pcap = pcap_open_live(device, 0, 0, 0, errbuf)) == NULL) fatal("error opening device %s: %s\n", device, errbuf); @@ -256,65 +684,54 @@ pcap_close(pcap); - if ((dst_ip = libnet_name_resolve(dst, 1)) == 0xFFFFFFFF) - fatal("Bad destination address: %s\n", dst); - - if (src) - { - if ((src_ip = libnet_name_resolve(src, 1)) == 0xFFFFFFFF) - fatal("Bad source address: %s\n", src); - } - else - { - if (! (src_ip = libnet_get_ipaddr(&l, device, errbuf))) - fatal("Could not determine IP address of device %s: %s\n", - device, errbuf); - - /* hmm, do I need to use htonl() here because of a bug in libnet? */ - src_ip = htonl(src_ip); - } - - if ((proto = getprotobyname("tcp")) == NULL) - fatal("Could not determine protcol number for TCP?\n"); - - if (src_prt == 0) + if (! o_forceport) { - if ((sockfd = socket(PF_INET, SOCK_STREAM, proto->p_proto)) < 0) - pfatal("Could not allocate socket\n"); + if ((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + pfatal("socket error"); insize = sizeof(in); - bzero(&in, insize); + memset(&in, 0, insize); - if ((bind(sockfd, &in, insize)) < 0) - pfatal("bind"); + in.sin_family = AF_INET; + in.sin_port = htons(src_prt); - if ((getsockname(sockfd, &in, &insize)) < 0) + if ((bind(sockfd, (struct sockaddr *)&in, insize)) < 0) + fatal("Sorry, could not bind to port %d. Use -P instead of -p to override.\n", src_prt); + + if ((getsockname(sockfd, (struct sockaddr *)&in, &insize)) < 0) pfatal("getsockname"); - src_prt = in.sin_port; + src_prt = ntohs(in.sin_port); + close(sockfd); } - if (minttl <= 0 || maxttl <= 0) + if (o_minttl <= 0 || o_maxttl <= 0) fatal("TTL must be greater than 0\n"); - if (minttl >= 256 || maxttl >= 256) + if (o_minttl >= 256 || o_maxttl >= 256) fatal("TTL must be less than 256\n"); - - if (minttl >= maxttl) + + if (o_minttl > o_maxttl) fatal("Minimum TTL (%d) must be less than maximum TTL (%d)\n", - minttl, maxttl); - - if (timeout <= 0) - fatal("Timeout must be greater than zero\n"); + o_minttl, o_maxttl); + + if (o_timeout <= 0) + fatal("Timeout must be at least 1\n"); + + if (o_pktlen < LIBNET_TCP_H + LIBNET_IP_H) + { + if (o_pktlen != 0) + warn("Increasing packet length to %d bytes\n", LIBNET_TCP_H + LIBNET_IP_H); + o_pktlen = 0; + } + else + o_pktlen -= (LIBNET_TCP_H + LIBNET_IP_H); libnet_seed_prand(); - if ((sockfd = libnet_open_raw_sock(IPPROTO_RAW)) == -1) + if ((sockfd = libnet_open_raw_sock(IPPROTO_RAW)) < 0) pfatal("socket allocation"); - if ((buf = malloc(LIBNET_TCP_H + LIBNET_IP_H)) == NULL) - pfatal("malloc"); - if (strcmp(dst, iptos(dst_ip)) == 0) snprintf(dst_name, TEXTSIZE, "%s", dst); else @@ -325,7 +742,7 @@ else snprintf(dst_prt_name, TEXTSIZE, "%d (%s)", dst_prt, serv->s_name); - printf("Selected device %s, address %s, port %d for outgoing packets\n", + fprintf(stderr, "Selected device %s, address %s, port %d for outgoing packets\n", device, iptos(src_ip), src_prt); } @@ -338,13 +755,14 @@ struct bpf_program fcode; bpf_u_int32 localnet, netmask; - if (! (pcap = pcap_open_live(device, offset + SNAPLEN, 0, 1, errbuf))) + if (! (pcap = pcap_open_live(device, offset + SNAPLEN, 0, 10, errbuf))) fatal("pcap_open_live failed: %s", errbuf); snprintf(filter, TEXTSIZE, "(tcp and src host %s and src port %d and dst host %s and dst port %d) or ((icmp[0] == 11 or icmp[0] == 3) and dst host %s)", iptos(dst_ip), dst_prt, iptos(src_ip), src_prt, iptos(src_ip)); + debug("pcap filter is:\n'%s'\n",filter); localnet = 0; netmask = 0; @@ -360,22 +778,41 @@ } /* - * Sends out a TCP SYN packet with the specified TTL, and returns the IP - * ID that was sent so we know which one to listen for later. + * Sends out a TCP SYN packet with the specified TTL, and returns + * the IP ID that was sent so we know which one to listen for later. */ u_short probe(int ttl) { + static u_char *payload, *buf; u_short id; + int i, size, ret; + + if (o_pktlen && !payload) + { + debug("Initializing payload of %d bytes\n", o_pktlen); + payload = xrealloc(payload, o_pktlen); + + for(i = 0; i < o_pktlen; i++) + payload[i] = '!' + (i % ('~' - '!')); + } + size = LIBNET_IP_H + LIBNET_TCP_H + o_pktlen; + + if (!buf) + { + debug("Initializing probe buffer\n"); + buf = xrealloc(buf, size); + } + + memset(buf, 0, size); id = libnet_get_prand(PRu32); - bzero(buf, LIBNET_TCP_H + LIBNET_IP_H); libnet_build_ip( - LIBNET_TCP_H, /* len */ - 0, /* tos */ + LIBNET_TCP_H+o_pktlen, /* len */ + o_tos, /* tos */ id, /* id */ - 0, /* frag */ + o_dontfrag ? IP_DF : 0, /* frag */ ttl, /* ttl */ IPPROTO_TCP, /* proto */ src_ip, /* saddr */ @@ -389,22 +826,22 @@ dst_prt, /* dest port */ 0, /* seq number */ 0, /* ack number */ - TH_SYN, /* control */ + TH_SYN | (o_ecn ? TH_CWR|TH_ECN : 0), /* control */ 0, /* window */ 0, /* urgent? */ - NULL, /* data */ - 0, /* datasize */ + payload, /* data */ + o_pktlen, /* datasize */ buf + LIBNET_IP_H); /* buffer */ - libnet_do_checksum(buf, IPPROTO_TCP, LIBNET_TCP_H); + libnet_do_checksum(buf, IPPROTO_TCP, LIBNET_TCP_H + o_pktlen); - if (gettimeofday(&t1, NULL) != 0) + if (gettimeofday(&t1, NULL) < 0) pfatal("gettimeofday"); - if (libnet_write_ip(sockfd, buf, LIBNET_TCP_H + LIBNET_IP_H) - < LIBNET_TCP_H + LIBNET_IP_H) - fatal("libnet_write_ip failed?\n"); - + if ((ret = libnet_write_ip(sockfd, buf, size)) < size) + fatal("libnet_write_ip failed? Attempted to write %d bytes, only wrote %d\n", + size, ret); + return id; } @@ -414,31 +851,78 @@ * increment the TTL some more. */ -int capture(int ttl, u_short id) +int capture(int ttl, int q, u_short id) { - u_char *packet; + u_char *packet; struct pcap_pkthdr packet_hdr; struct libnet_ip_hdr *ip_hdr, *old_ip_hdr; struct libnet_tcp_hdr *tcp_hdr, *old_tcp_hdr; struct libnet_icmp_hdr *icmp_hdr; - time_t start; + struct timeval start, now, timepassed, timeout_tv, timeleft; + int pcap_fd, firstpass, ret, len; double delta; + fd_set sfd; - start = time(NULL); + firstpass = 1; + timeout_tv.tv_sec = o_timeout; + timeout_tv.tv_usec = 0; + + if (gettimeofday(&start, NULL) < 0) + pfatal("gettimeofday"); for(;;) { + if (firstpass) + { + firstpass = 0; + timeleft = timeout_tv; + } + else + { + if (gettimeofday(&now, NULL) < 0) + pfatal("gettimeofday"); + + timepassed = tvdiff(&now, &start); + + if (tvsign(&timepassed) < 0) + { + /* Deal with weird clock skew */ + timepassed.tv_sec = 0; + timepassed.tv_usec = 0; + } + + timeleft = tvdiff(&timeout_tv, &timepassed); + + if (tvsign(&timeleft) <= 0) + { + showprobe(ttl, q, INADDR_ANY, NULL, "*"); + return 0; + } + } + /* - * This doesn't always work -- pcap_next() doesn't always return - * quickly, which means that the timeout check doesn't always happen. - * There needs to be a better way to go about this, perhaps with - * alarm(). + * The libpcap documentation is wrong; pcap_fileno actually + * returns the fd of the live capture device, not the save + * file. References: + * + * http://www.tcpdump.org/lists/workers/2001/01/msg00223.html + * http://www.tcpdump.org/lists/workers/2001/03/msg00107.html + * http://www.tcpdump.org/lists/workers/2001/03/msg00109.html + * http://www.tcpdump.org/lists/workers/2001/03/msg00110.html */ - if (time(NULL) - start >= timeout) + pcap_fd = pcap_fileno(pcap); + FD_ZERO(&sfd); + FD_SET(pcap_fd, &sfd); + + if ((ret = select(pcap_fd + 1, &sfd, NULL, NULL, &timeleft)) < 0) { - printf("%2d *\n", ttl); - return 0; + fatal("select"); + } + else if (ret == 0) + { + debug("select() timeout\n"); + continue; } if ((packet = (u_char *)pcap_next(pcap, &packet_hdr)) == NULL) @@ -447,35 +931,53 @@ continue; } - debug("packet recieved from pcap_next()\n"); - packet += offset; + len = packet_hdr.caplen - offset; + debug("received %d byte packet from pcap_next()\n", len); + + if (len < LIBNET_IP_H) + { + debug("ignoring partial ip packet\n"); + continue; + } + ip_hdr = (struct libnet_ip_hdr *)packet; - if (gettimeofday(&t2, NULL) != 0) + if (gettimeofday(&t2, NULL) < 0) pfatal("gettimeofday"); delta = (double)(t2.tv_sec - t1.tv_sec) * 1000 + - (double)(t2.tv_usec - t1.tv_usec) / 1000; + (double)(t2.tv_usec - t1.tv_usec) / 1000; if (ip_hdr->ip_p == IPPROTO_ICMP) { + if (len < LIBNET_IP_H + LIBNET_ICMP_H + 4) + { + debug("Ignoring partial icmp packet\n"); + continue; + } + icmp_hdr = (struct libnet_icmp_hdr *)(packet + LIBNET_IP_H); debug("received icmp packet\n"); if (icmp_hdr->icmp_type != ICMP_TIMXCEED && icmp_hdr->icmp_type != ICMP_UNREACH) { - printf("%2d %s (%s) %.3f ms -- Unexpected ICMP\n", - ttl, iptohost(ip_hdr->ip_src.s_addr), - iptos(ip_hdr->ip_src.s_addr), delta); + showprobe(ttl, q, ip_hdr->ip_src.s_addr, NULL, + "%.3f ms -- Unexpected ICMP\n", delta); return 0; } /* * The IP header that generated the ICMP packet is quoted - * here. I don't know what the +4 is, but it works. - */ + * here. I don't know what the +4 is, but it works. */ + + if (len < LIBNET_IP_H + LIBNET_ICMP_H + 4 + LIBNET_IP_H + 4) + { + debug("Ignoring icmp packet with incomplete payload\n"); + continue; + } + old_ip_hdr = (struct libnet_ip_hdr *)(packet + LIBNET_IP_H + LIBNET_ICMP_H + 4); @@ -550,17 +1052,15 @@ s = "!?"; break; } - printf("%2d %s (%s) %.3f ms %s\n", ttl, - iptohost(ip_hdr->ip_src.s_addr), - iptos(ip_hdr->ip_src.s_addr), delta, s); + showprobe(ttl, q, ip_hdr->ip_src.s_addr, NULL, + "%.3f ms %s", delta, s); return 1; } if (icmp_hdr->icmp_type == ICMP_TIMXCEED) { - printf("%2d %s (%s) %.3f ms\n", ttl, - iptohost(ip_hdr->ip_src.s_addr), - iptos(ip_hdr->ip_src.s_addr), delta); + showprobe(ttl, q, ip_hdr->ip_src.s_addr, NULL, + "%.3f ms", delta); return 0; } @@ -570,6 +1070,12 @@ if (ip_hdr->ip_p == IPPROTO_TCP) { char *s; + + if (len < LIBNET_IP_H + LIBNET_TCP_H) + { + debug("Ignoring partial tcp packet\n"); + continue; + } tcp_hdr = (struct libnet_tcp_hdr *)(packet + LIBNET_IP_H); debug("received tcp packet\n"); @@ -581,9 +1087,8 @@ else s = "unknown"; - printf("%2d %s (%s) [%s] %.3f ms\n", - ttl, iptohost(ip_hdr->ip_src.s_addr), - iptos(ip_hdr->ip_src.s_addr), s, delta); + showprobe(ttl, q, ip_hdr->ip_src.s_addr, s, + "%.3f ms", delta); return 1; } @@ -594,20 +1099,27 @@ void trace(void) { - int ttl, done; + int ttl, q, done; u_short id; - printf("Tracing the path to %s on TCP port %s, %d hops max\n", - dst_name, dst_prt_name, maxttl); + fprintf(stderr, "Tracing the path to %s on TCP port %s, %d hops max", + dst_name, dst_prt_name, o_maxttl); - for (ttl = minttl, done = 0; !done && ttl <= maxttl; ttl++) + if (o_pktlen) + fprintf(stderr, ", %d byte packets", o_pktlen + LIBNET_TCP_H + LIBNET_IP_H); + fprintf(stderr, "\n"); + + for (ttl = o_minttl, done = 0; !done && ttl <= o_maxttl; ttl++) { - id = probe(ttl); - done = capture(ttl, id); + for (q = 0; q < o_nqueries; q++) + { + id = probe(ttl); + done |= capture(ttl, q + 1, id); + } } if (!done) - printf("Destination not reached\n"); + fprintf(stderr, "Destination not reached\n"); } int main(int argc, char *argv[]) @@ -615,19 +1127,23 @@ struct servent *serv; char *s; - if (getuid() & geteuid()) - fatal("Got root?\n"); - src_ip = 0; src_prt = 0; + dst_prt = 0; src = NULL; - minttl = 1; - maxttl = 30; + device = NULL; + + o_minttl = 1; + o_maxttl = 30; o_debug = 0; o_numeric = 0; - device = NULL; - dst_prt = 0; - timeout = 3; + o_nqueries = 3; + o_forceport = 0; + o_pktlen = 0; + o_tos = 0; + o_ecn = 0; + o_dontfrag = 0; + o_timeout = 3; /* strip out path from argv[0] */ for (name = s = argv[0]; s[0]; s++) @@ -677,18 +1193,31 @@ argc--, argv++; break; + case 'l': + if (argc < 2) fatal("Argument required for -l\n"); + o_pktlen = atoi(argv[1]); + argc--, argv++; + break; + case 'f': if (argc < 2) fatal("Argument required for -f\n"); - minttl = atoi(argv[1]); + o_minttl = atoi(argv[1]); argc--, argv++; break; + case 'F': + o_dontfrag = 1; + debug("Will set DF bit in outgoing packets\n"); + break; + case 'm': if (argc < 2) fatal("Argument required for -m\n"); - maxttl = atoi(argv[1]); + o_maxttl = atoi(argv[1]); argc--, argv++; break; + case 'P': + o_forceport = 1; case 'p': if (argc < 2) fatal("Argument required for -p\n"); if (getuid()) fatal("Sorry, must be root to use -p\n"); @@ -696,9 +1225,15 @@ argc--, argv++; break; + case 'q': + if (argc < 2) fatal("Argument required for -q\n"); + o_nqueries = atoi(argv[1]); + argc--,argv++; + break; + case 'w': if (argc < 2) fatal("Argument required for -w\n"); - timeout = atoi(argv[1]); + o_timeout = atoi(argv[1]); argc--, argv++; break; @@ -709,18 +1244,30 @@ argc--, argv++; break; + case 't': + if (argc < 2) fatal("Argument required for -t\n"); + o_tos = atoi(argv[1]); + debug("TOS set to %d\n", o_tos); + argc--, argv++; + break; + + case 'E': + o_ecn = 1; + debug("Enabled ECN support\n"); + break; + default: - printf("Unknown command line argument: -%c\n", s[0]); + fprintf(stderr, "Unknown command line argument: -%c\n", s[0]); usage(); } } - if (argc < 1 || argc > 2) + if (argc < 1 || argc > 3) usage(); dst = argv[0]; - if (argc == 2) + if (argc > 1) { dst_prt = atoi(argv[1]); @@ -729,18 +1276,23 @@ if ((serv = getservbyname(argv[1], "tcp")) == NULL) fatal("Unknown port: %s\n", argv[1]); else - dst_prt = serv->s_port; + dst_prt = ntohs(serv->s_port); } } + if (argc > 2) + o_pktlen = atoi(argv[2]); + if (dst_prt == 0) dst_prt = 80; + if (getuid() & geteuid()) + fatal("Got root?\n"); + defaults(); initcapture(); seteuid(getuid()); trace(); - free(buf); return 0; } diff -ru tcptraceroute-1.1/tcptraceroute.lsm tcptraceroute-1.2/tcptraceroute.lsm --- tcptraceroute-1.1/tcptraceroute.lsm Sun Jul 1 21:09:05 2001 +++ tcptraceroute-1.2/tcptraceroute.lsm Tue Jul 31 21:09:29 2001 @@ -1,7 +1,7 @@ Begin4 Title: tcptraceroute -Version: 1.1 -Entered-date: 2001-06-30 +Version: 1.2 +Entered-date: 2001-07-31 Description: A traceroute implementation using TCP packets Keywords: network, traceroute, firewall Author: mct@toren.net (Michael C. Toren)