Subject: LSRR traceroute
To: None <tech-net@netbsd.org>
From: John Hawkinson <jhawk@panix.com>
List: tech-net
Date: 12/17/1994 20:28:16
Attached are patches adding loose source routing (-g) to
NetBSD's traceroute; this also adds a few miscellaneous
cleanups, a -D option to print out packets before sending
them.

In case anyone is curious, this is different from the standard
traceroute -g as 4.4 no longer supports setsockopt(,,IP_OPTIONS,,)
on raw IP sockets.

--
John Hawkinson
jhawk@panix.com

===================================================================
RCS file: /afs/sipb/user/jhawk/CVS/traceroute/traceroute.8,v
retrieving revision 1.1
diff -c -r1.1 traceroute.8
*** 1.1	1994/12/14 07:54:09
--- traceroute.8	1994/12/18 01:15:04
***************
*** 33,39 ****
  .\" SUCH DAMAGE.
  .\"
  .\"	@(#)traceroute.8	8.1 (Berkeley) 6/6/93
! .\"	$Id: traceroute.8,v 1.1 1994/12/14 07:54:09 jhawk Exp $
  .\"
  .Dd June 6, 1993
  .Dt TRACEROUTE 8
--- 33,39 ----
  .\" SUCH DAMAGE.
  .\"
  .\"	@(#)traceroute.8	8.1 (Berkeley) 6/6/93
! .\"	$Id: traceroute.8,v 1.2 1994/12/18 01:15:03 jhawk Exp $
  .\"
  .Dd June 6, 1993
  .Dt TRACEROUTE 8
***************
*** 43,48 ****
--- 43,51 ----
  .Nd print the route packets take to network host
  .Sh SYNOPSIS
  .Nm traceroute
+ .Op Fl d
+ .Op Fl D
+ .Op Fl g Ar addr
  .Op Fl m Ar max_ttl
  .Op Fl n
  .Op Fl p Ar port
***************
*** 74,79 ****
--- 77,93 ----
  .Pp
  Other options are:
  .Bl -tag -width Ds
+ .It Fl d
+ Turns on socket-level debugging.
+ .It Fl D
+ Causes
+ .Nm Traceroute
+ to dump each packet to standard error before sending it.
+ .It Fl g Ar addr
+ Enable the IP LSRR (Loose Source Record Route) option in addition to the
+ TTL tests. This is useful for asking how somebody else, at IP address
+ .Ar addr ,
+ reaches a particular target.
  .It Fl m Ar max_ttl
  Set the max time-to-live (max number of hops) used in outgoing probe
  packets.  The default is 30 hops (the same default used for
***************
*** 316,321 ****
--- 330,347 ----
  almost all the probes result in some kind of unreachable,
  .Nm traceroute
  will give up and exit.
+ .Pp
+ .Bd -literal
+ traceroute \-g 10.3.0.5 128.182.0.0
+ 
+ .Ed
+ will show the path from the Cambridge Mailbridge to PSC while
+ .Bd -literal
+ traceroute \-g 192.5.146.4 \-g 10.3.0.5 35.0.0.0
+ 
+ .Ed
+ shows how the Cambridge Mailbrige reaches Merit,
+ by using PSC to reach the Mailbridge.
  .Pp
  This program is intended for use in network testing, measurement
  and management.
===================================================================
RCS file: /afs/sipb/user/jhawk/CVS/traceroute/traceroute.c,v
retrieving revision 1.1
diff -c -r1.1 traceroute.c
*** 1.1	1994/12/14 07:54:09
--- traceroute.c	1994/12/18 01:15:06
***************
*** 42,48 ****
  
  #ifndef lint
  /*static char sccsid[] = "from: @(#)traceroute.c	8.1 (Berkeley) 6/6/93";*/
! static char *rcsid = "$Id: traceroute.c,v 1.1 1994/12/14 07:54:09 jhawk Exp $";
  #endif /* not lint */
  
  /*
--- 42,48 ----
  
  #ifndef lint
  /*static char sccsid[] = "from: @(#)traceroute.c	8.1 (Berkeley) 6/6/93";*/
! static char *rcsid = "$Id: traceroute.c,v 1.6 1994/12/18 01:15:05 jhawk Exp $";
  #endif /* not lint */
  
  /*
***************
*** 226,235 ****
--- 226,237 ----
  #include <netinet/in.h>
  #include <netinet/ip.h>
  #include <netinet/ip_icmp.h>
+ #include <netinet/ip_var.h>
  #include <netinet/udp.h>
  
  #include <arpa/inet.h>
  
+ #include <ctype.h>
  #include <netdb.h>
  #include <stdio.h>
  #include <errno.h>
***************
*** 237,255 ****
  #include <string.h>
  #include <unistd.h>
  
! #define	MAXPACKET	65535	/* max ip packet size */
! #ifndef MAXHOSTNAMELEN
! #define MAXHOSTNAMELEN	64
! #endif
! 
! #ifndef FD_SET
! #define NFDBITS         (8*sizeof(fd_set))
! #define FD_SETSIZE      NFDBITS
! #define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
! #define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
! #define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
! #define FD_ZERO(p)      bzero((char *)(p), sizeof(*(p)))
! #endif
  
  #define Fprintf (void)fprintf
  #define Sprintf (void)sprintf
--- 239,245 ----
  #include <string.h>
  #include <unistd.h>
  
! #define	HEADERSIZE	(sizeof(struct ip) + lsrrlen + sizeof(struct udphdr) + sizeof(struct packetdata))
  
  #define Fprintf (void)fprintf
  #define Sprintf (void)sprintf
***************
*** 258,276 ****
  /*
   * format of a (udp) probe packet.
   */
! struct opacket {
! 	struct ip ip;
! 	struct udphdr udp;
  	u_char seq;		/* sequence number of this packet */
  	u_char ttl;		/* ttl packet left with */
  	struct timeval tv;	/* time packet left */
  };
  
! u_char	packet[512];		/* last inbound (icmp) packet */
! struct opacket	*outpacket;	/* last output (udp) packet */
  
  int wait_for_reply __P((int, struct sockaddr_in *));
! void send_probe __P((int, int));
  double deltaT __P((struct timeval *, struct timeval *));
  int packet_ok __P((u_char *, int, struct sockaddr_in *, int));
  void print __P((u_char *, int, struct sockaddr_in *));
--- 248,268 ----
  /*
   * format of a (udp) probe packet.
   */
! struct packetdata {
  	u_char seq;		/* sequence number of this packet */
  	u_char ttl;		/* ttl packet left with */
  	struct timeval tv;	/* time packet left */
  };
  
! #define	MAX_LSRR	((MAX_IPOPTLEN-3)/4)
! struct in_addr gateway[MAX_LSRR + 1];
! 
! u_char packet[512];		/* last inbound (icmp) packet */
! char *outpacket;
! int lsrrlen = 0;
  
  int wait_for_reply __P((int, struct sockaddr_in *));
! void send_probe __P((int, int, struct sockaddr_in *));
  double deltaT __P((struct timeval *, struct timeval *));
  int packet_ok __P((u_char *, int, struct sockaddr_in *, int));
  void print __P((u_char *, int, struct sockaddr_in *));
***************
*** 282,288 ****
  int sndsock;			/* send (udp) socket file descriptor */
  struct timezone tz;		/* leftover */
  
- struct sockaddr whereto;	/* Who to try to reach */
  int datalen;			/* How much data */
  
  char *source = 0;
--- 274,279 ----
***************
*** 296,301 ****
--- 287,294 ----
  int verbose;
  int waittime = 5;		/* time to wait for response (in seconds) */
  int nflag;			/* print addresses numerically */
+ int dump;
+ 
  
  int
  main(argc, argv)
***************
*** 306,327 ****
  	extern int optind;
  	struct hostent *hp;
  	struct protoent *pe;
! 	struct sockaddr_in from, *to;
! 	int ch, i, on, probe, seq, tos, ttl;
  
  	on = 1;
! 	seq = tos = 0;
! 	to = (struct sockaddr_in *)&whereto;
! 	while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
  		switch(ch) {
  		case 'd':
  			options |= SO_DEBUG;
  			break;
  		case 'm':
  			max_ttl = atoi(optarg);
! 			if (max_ttl <= 1) {
  				Fprintf(stderr,
! 				    "traceroute: max ttl must be >1.\n");
  				exit(1);
  			}
  			break;
--- 299,346 ----
  	extern int optind;
  	struct hostent *hp;
  	struct protoent *pe;
! 	struct sockaddr_in from, to;
! 
! 	int ch, i, lsrr, on, probe, seq, tos, ttl;
! 	u_long gw;    /* LSRR gateway host */
! 	struct ip *ip;
  
  	on = 1;
! 	lsrr = seq = tos = 0;
! 	while ((ch = getopt(argc, argv, "dDg:m:np:q:rs:t:w:v")) != EOF)
  		switch(ch) {
  		case 'd':
  			options |= SO_DEBUG;
  			break;
+ 		case 'D':
+ 			dump = 1;
+ 			break;
+ 		case 'g':
+ 			if (lsrr > MAX_LSRR) {
+ 		  	  Fprintf(stderr, "too many gateways; only %d allowed\n",
+ 				  MAX_LSRR);
+ 			  exit(1);
+ 			}
+ 			gw = inet_addr(optarg);
+ 			if (gw == -1) {
+ 			  hp = gethostbyname(optarg);
+ 			  if (hp == 0) {
+ 			    Fprintf(stderr, "Unknown host %s\n", optarg);
+ 			    exit(1);
+ 			  }
+ 			  bcopy(hp->h_addr, &gw, 4);
+ 			}
+ 			gateway[lsrr].s_addr = gw;
+ 			if (lsrr++ == 0)
+ 				lsrrlen = 4;
+ 			lsrrlen += 4;
+ 			break;
  		case 'm':
  			max_ttl = atoi(optarg);
! 			if (max_ttl < 1 || max_ttl > MAXTTL) {
  				Fprintf(stderr,
! 					"traceroute: max ttl must be 1 to %d\n",
! 					MAXTTL);
  				exit(1);
  			}
  			break;
***************
*** 332,338 ****
  			port = atoi(optarg);
  			if (port < 1) {
  				Fprintf(stderr,
! 				    "traceroute: port must be >0.\n");
  				exit(1);
  			}
  			break;
--- 351,357 ----
  			port = atoi(optarg);
  			if (port < 1) {
  				Fprintf(stderr,
! 				    "traceroute: port must be > 0.\n");
  				exit(1);
  			}
  			break;
***************
*** 340,346 ****
  			nprobes = atoi(optarg);
  			if (nprobes < 1) {
  				Fprintf(stderr,
! 				    "traceroute: nprobes must be >0.\n");
  				exit(1);
  			}
  			break;
--- 359,365 ----
  			nprobes = atoi(optarg);
  			if (nprobes < 1) {
  				Fprintf(stderr,
! 				    "traceroute: nprobes must be > 0.\n");
  				exit(1);
  			}
  			break;
***************
*** 369,375 ****
  			waittime = atoi(optarg);
  			if (waittime <= 1) {
  				Fprintf(stderr,
! 				    "traceroute: wait must be >1 sec.\n");
  				exit(1);
  			}
  			break;
--- 388,394 ----
  			waittime = atoi(optarg);
  			if (waittime <= 1) {
  				Fprintf(stderr,
! 				    "traceroute: wait must be > 1 sec.\n");
  				exit(1);
  			}
  			break;
***************
*** 384,425 ****
  
  	setlinebuf (stdout);
  
! 	(void) bzero((char *)&whereto, sizeof(struct sockaddr));
! 	to->sin_family = AF_INET;
! 	to->sin_addr.s_addr = inet_addr(*argv);
! 	if (to->sin_addr.s_addr != -1)
  		hostname = *argv;
  	else {
  		hp = gethostbyname(*argv);
! 		if (hp) {
! 			to->sin_family = hp->h_addrtype;
! 			bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
! 			hostname = hp->h_name;
! 		} else {
  			(void)fprintf(stderr,
  			    "traceroute: unknown host %s\n", *argv);
  			exit(1);
  		}
  	}
  	if (*++argv)
  		datalen = atoi(*argv);
! 	if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket)) {
  		Fprintf(stderr,
  		    "traceroute: packet size must be 0 <= s < %ld.\n",
! 		    MAXPACKET - sizeof(struct opacket));
  		exit(1);
  	}
! 	datalen += sizeof(struct opacket);
! 	outpacket = (struct opacket *)malloc((unsigned)datalen);
! 	if (! outpacket) {
  		perror("traceroute: malloc");
  		exit(1);
  	}
! 	(void) bzero((char *)outpacket, datalen);
! 	outpacket->ip.ip_dst = to->sin_addr;
! 	outpacket->ip.ip_tos = tos;
! 	outpacket->ip.ip_v = IPVERSION;
! 	outpacket->ip.ip_id = 0;
  
  	ident = (getpid() & 0xffff) | 0x8000;
  
--- 403,460 ----
  
  	setlinebuf (stdout);
  
! 	(void) bzero((char *)&to, sizeof(struct sockaddr));
! 	to.sin_family = AF_INET;
! 	to.sin_addr.s_addr = inet_addr(*argv);
! 	if (to.sin_addr.s_addr != -1)
  		hostname = *argv;
  	else {
  		hp = gethostbyname(*argv);
! 		if (hp == 0) {
  			(void)fprintf(stderr,
  			    "traceroute: unknown host %s\n", *argv);
  			exit(1);
  		}
+ 		to.sin_family = hp->h_addrtype;
+ 		bcopy(hp->h_addr, (caddr_t)&to.sin_addr, hp->h_length);
+ 		hostname = hp->h_name;
  	}
  	if (*++argv)
  		datalen = atoi(*argv);
! 	if (datalen < 0 || datalen >= IP_MAXPACKET - HEADERSIZE) {
  		Fprintf(stderr,
  		    "traceroute: packet size must be 0 <= s < %ld.\n",
! 		    IP_MAXPACKET - HEADERSIZE);
  		exit(1);
  	}
! 	datalen += HEADERSIZE;
! 	outpacket = (char *)malloc(datalen);
! 	if (outpacket == 0) {
  		perror("traceroute: malloc");
  		exit(1);
  	}
! 
! 	(void) bzero(outpacket, datalen);
! 	ip = (struct ip *)outpacket;
!  	if (lsrr != 0) {
! 		u_char *p;
! 		p = (u_char *)(ip + 1);
! 		*p++ = IPOPT_NOP;
! 		*p++ = IPOPT_LSRR;
! 		*p++ = 3 + lsrr * 4;
! 		*p++ = IPOPT_MINOFF;
! 		gateway[lsrr] = to.sin_addr;
! 		for (i = 1; i <= lsrr; i++)
! 			*((struct in_addr *)p)++ = gateway[i];
! 		ip->ip_dst = gateway[0];
!  	} else
! 		ip->ip_dst = to.sin_addr;
! 	ip->ip_off = 0;
! 	ip->ip_hl = (sizeof(struct ip) + lsrrlen) >> 2;
! 	ip->ip_p = IPPROTO_UDP;
! 	ip->ip_v = IPVERSION;
! 	ip->ip_tos = tos;
! 	ip->ip_id = 0;
  
  	ident = (getpid() & 0xffff) | 0x8000;
  
***************
*** 442,447 ****
--- 477,483 ----
  		perror("traceroute: raw socket");
  		exit(5);
  	}
+ 
  #ifdef SO_SNDBUF
  	if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
  		       sizeof(datalen)) < 0) {
***************
*** 471,477 ****
  			Printf("traceroute: unknown host %s\n", source);
  			exit(1);
  		}
! 		outpacket->ip.ip_src = from.sin_addr;
  #ifndef IP_HDRINCL
  		if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0) {
  			perror ("traceroute: bind:");
--- 507,513 ----
  			Printf("traceroute: unknown host %s\n", source);
  			exit(1);
  		}
! 		ip->ip_src = from.sin_addr;
  #ifndef IP_HDRINCL
  		if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0) {
  			perror ("traceroute: bind:");
***************
*** 481,487 ****
  	}
  
  	Fprintf(stderr, "traceroute to %s (%s)", hostname,
! 		inet_ntoa(to->sin_addr));
  	if (source)
  		Fprintf(stderr, " from %s", source);
  	Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
--- 517,523 ----
  	}
  
  	Fprintf(stderr, "traceroute to %s (%s)", hostname,
! 		inet_ntoa(to.sin_addr));
  	if (source)
  		Fprintf(stderr, " from %s", source);
  	Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
***************
*** 497,506 ****
  			int cc;
  			struct timeval t1, t2;
  			struct timezone tz;
- 			struct ip *ip;
  
  			(void) gettimeofday(&t1, &tz);
! 			send_probe(++seq, ttl);
  			while (cc = wait_for_reply(s, &from)) {
  				(void) gettimeofday(&t2, &tz);
  				if ((i = packet_ok(packet, cc, &from, seq))) {
--- 533,541 ----
  			int cc;
  			struct timeval t1, t2;
  			struct timezone tz;
  
  			(void) gettimeofday(&t1, &tz);
! 			send_probe(++seq, ttl, &to);
  			while (cc = wait_for_reply(s, &from)) {
  				(void) gettimeofday(&t2, &tz);
  				if ((i = packet_ok(packet, cc, &from, seq))) {
***************
*** 547,553 ****
  			(void) fflush(stdout);
  		}
  		putchar('\n');
! 		if (got_there || unreachable >= nprobes-1)
  			exit(0);
  	}
  }
--- 582,588 ----
  			(void) fflush(stdout);
  		}
  		putchar('\n');
! 		if (got_there || unreachable >= nprobes)
  			exit(0);
  	}
  }
***************
*** 573,607 ****
  	return(cc);
  }
  
  
  void
! send_probe(seq, ttl)
  	int seq, ttl;
  {
! 	struct opacket *op = outpacket;
! 	struct ip *ip = &op->ip;
! 	struct udphdr *up = &op->udp;
  	int i;
  
- 	ip->ip_off = 0;
- 	ip->ip_hl = sizeof(*ip) >> 2;
- 	ip->ip_p = IPPROTO_UDP;
  	ip->ip_len = datalen;
  	ip->ip_ttl = ttl;
- 	ip->ip_v = IPVERSION;
  	ip->ip_id = htons(ident+seq);
  
  	up->uh_sport = htons(ident);
  	up->uh_dport = htons(port+seq);
! 	up->uh_ulen = htons((u_short)(datalen - sizeof(struct ip)));
  	up->uh_sum = 0;
  
  	op->seq = seq;
  	op->ttl = ttl;
  	(void) gettimeofday(&op->tv, &tz);
  
! 	i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
! 		   sizeof(struct sockaddr));
  	if (i < 0 || i != datalen)  {
  		if (i<0)
  			perror("sendto");
--- 608,658 ----
  	return(cc);
  }
  
+ void
+ dump_packet()
+ {
+ 	u_char *p;
+ 	int i;
+ 
+ 	Fprintf(stderr,"packet data:");
+ 	for (p = outpacket, i = 0; i < datalen; i++) {
+ 		if ((i % 24) == 0)
+ 			Fprintf(stderr,"\n");
+ 		Fprintf(stderr," %02x", *p++);
+ 	}
+ 	Fprintf(stderr,"\n");
+ }
  
  void
! send_probe(seq, ttl, to)
  	int seq, ttl;
+ 	struct sockaddr_in *to;
  {
! 	struct ip *ip = (struct ip *)outpacket;
! 	u_char *p = (u_char *)(ip + 1);
! 	struct udphdr *up = (struct udphdr *)(p + lsrrlen);
! 	struct packetdata *op = (struct packetdata *)(up + 1);
! 	/*	u_char *opts = &op->ipoptions;  Do we need this? */
  	int i;
  
  	ip->ip_len = datalen;
  	ip->ip_ttl = ttl;
  	ip->ip_id = htons(ident+seq);
  
  	up->uh_sport = htons(ident);
  	up->uh_dport = htons(port+seq);
! 	up->uh_ulen = htons((u_short)(datalen - sizeof(struct ip) - lsrrlen));
  	up->uh_sum = 0;
  
  	op->seq = seq;
  	op->ttl = ttl;
  	(void) gettimeofday(&op->tv, &tz);
  
! 	if (dump)
! 		dump_packet();
! 
! 	i = sendto(sndsock, outpacket, datalen, 0, (struct sockaddr *)to,
! 		   sizeof(struct sockaddr_in));
  	if (i < 0 || i != datalen)  {
  		if (i<0)
  			perror("sendto");
***************
*** 830,836 ****
  usage()
  {
  	(void)fprintf(stderr,
! "usage: traceroute [-dnrv] [-m max_ttl] [-p port#] [-q nqueries]\n\t\
! [-s src_addr] [-t tos] [-w wait] host [data size]\n");
  	exit(1);
  }
--- 881,887 ----
  usage()
  {
  	(void)fprintf(stderr,
! "usage: traceroute [-dDnrv] [-m max_ttl] [-p port#] [-q nqueries]\n\t\
! [-s src_addr] [-t tos] [-w wait] [-g gateway] host [data size]\n");
  	exit(1);
  }