#!/bin/sh
# This is a shell archive (produced by GNU shar 4.0).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 1995-08-11 14:28 EDT by <root@zippy>.
# Source directory was `/afs/psc.edu/projects/networking/tools/treno/.wbin/sgi_52'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#   1082 -rw-r--r-- Makefile
#   3896 -rw-rw-rw- treno.1
#  20775 -rw-r----- treno.c
#   3064 -rw-r--r-- mplib.h
#   5944 -rw-r--r-- mplib.c
#  10255 -rw-r--r-- netprintf.c
#    252 -rw-r--r-- version.c
#     42 -rw-r--r-- sharversion.h
#     71 -rw-r--r-- version.h
#
# ============= Makefile ==============
if test -f 'Makefile' && test X"$1" != X"-c"; then
  echo 'x - skipping Makefile (File already exists)'
else
  echo 'x - extracting Makefile (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
# minimal makefile
X
TARGETS=treno
OBJS=treno.o mplib.o version.o netprintf.o
X
# Choose a signal paradigm.
# Default to the old signal() interface which works on most platforms.
# Note that most systems attempt to emulate most foreign signal mechanisms, so
# they appear to work even when non-optimal.
X
# If treno hangs, uncomment one of the following:
# SF=-DPOSIX_SIGNALS
# SF=-DBSD44_SIGNALS
# SF=-DSYSV_SIGNALS
X
CFLAGS=-O ${SF}
# version.h must be last
SRC=Makefile treno.1 treno.c mplib.h mplib.c netprintf.c version.c sharversion.h
X
all: ${TARGETS}
X
shar:
X	rm -f sharversion.h version.h
X	echo '#define SVERSION "Shar:'`date +"%D_%T"`'"' > sharversion.h
X	cp sharversion.h version.h
X	echo '#define VERSION "Edit:Never"' >> version.h
X	shar -T -m ${SRC} version.h > treno.shar
X
clean:
X	rm -rf ${OBJS} ${TARGETS}
X
treno:  ${OBJS}
X	${CC} ${OBJS} -o treno
X
treno.o: treno.c mplib.h
X
mplib.o: mplib.c mplib.h
X
version.h: ${SRC}
X	cp sharversion.h version.h
X	echo '#define VERSION "Edit:'`date +"%D_%T"`'"' >> version.h
X
version.o: version.c version.h
X
netprintf.o: netprintf.c
X
showev:
SHAR_EOF
  chmod 0644 'Makefile' ||
  echo 'restore of Makefile failed'
  shar_count="`wc -c < 'Makefile'`"
  test 1082 -eq "$shar_count" ||
    echo "Makefile: original size 1082, current size $shar_count"
fi
# ============= treno.1 ==============
if test -f 'treno.1' && test X"$1" != X"-c"; then
  echo 'x - skipping treno.1 (File already exists)'
else
  echo 'x - extracting treno.1 (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'treno.1' &&
X.TH treno 1
X.SH Name
treno \- Traceroute-Reno - Emulate Reno TCP over traceroute
X.SH Description
X.NXR "treno command"
X.NXR "IP performance diagnostic"
X.NXR "ICMP TTL"
Treno is an IP performance diagnostic, designed to measure 
any specified path through the public Internet.  It implements an idealized
version of Reno TCP congestion dynamics, using UDP (traceroute) or ICMP echo
packets.   In UDP mode, it reports the measured psuedo-TCP performance to each
hop along a specified path.   Generally as the path gets longer, the
expected performance declines.  IP bottlenecks are typically revealed as
downward steps in the performance.
X.PP
In ICMP mode, it can not measure the hop-by-hop performance, but it does
measure both the forward and return paths with large packets.  This could be
viewed as a "loopback" TCP connection.
X.SH Method
Each transmitted packet (ICMP echo or UDP) is labeled with a sequence number.
It propagates out into the network until it generates an ICMP message (one of
echo reply, ttl exceeded, or network, host or port unreachable).  The ICMP
message always includes the transcribed header of the original packet,
containing its sequence number.  The ICMP message propagates back to the treno
diagnostic which transmits zero, one or more packets to maintain a TCP style
congestion window.
X.SH Syntax
X.B treno [\fI\-options ...\fR] [\fIrouter1 ... routerN\fR]
\fITarget\fR
X.SH Options
X.TP 8
X.BI \-p " time"
Specify the measurement interval in seconds (defaults to 10).
X.TP 8
X.BI \-t " ttl"
Send UDP packets with the specified TTL.
X.TP 8
X.BI \-a " ttlmax"
Auto-increment TTL up to ttlmax.   Implys \-t 1 if not otherwise specified.
If neither \-t nor \-a switches are present treno
uses ICMP echo.
X.TP 8
X.BI \-b " length"
Initial MTU (Maximum Transmission Unit) in bytes, including TCP+IP headers,
options, and data payload, but not Mac or LLC layer headers.  In ICMP mode,
the length defaults to 576 bytes.  In UDP mode, the length defaults to
8166 bytes and RFC1191 MTU discovery is applyed.
X.TP 8
X.B \-D
Suppress emulating delayed ACK in the receiver.
X.TP 8
X.B \-T
Use more thrust, so we get our cycles even in the presence of greedy users.
Set nice(-max) (as well as real time on Cray systems).  In general, treno will
get most accurate results if the diagnostic (treno) platform is quiescent.
However with \-T reasonable results can be obtained on loaded systems.
X.TP 8
X.B \-n
Don't convert addresses to host names.
X.TP 8
X.B \-d
Debug mode: give detailed information about all unexpected events.  Also set
SO_DEBUG.
X.TP 8
X.BI \-v "[<digit>]"
Set the verbosity of the output.  If \-v is not present, treno
displays minimal path information: MTU and bandwidth to each hop.  If \-v is
specified without a digit, treno
also shows other (currently under development) measures of the path.  If a
digit is included, other levels can be selected.  \-v6 gives more detail on
exceptions, \-v8 displays packet contents.
X.TP 8
X.B -E
Enable event logging.  Creates the binary file event.log,
containing internal diagnostic information.
X.B \-V
Display the version string.
X.TP 8
X.SH Restrictions
Source routing does not work on some platforms.
X.PP
X.SH CAUTIONS and WARNINGS
Most routers in the production Internet can not process ICMP or IP potions
options as quickly as they forward packets.  Do not operate for extended
periods on other people's equipment.
X.PP
Please do not redistribute treno sources: current versions can be obtained at
the location below.
X.PP
If treno does not seem to function, read the comments in the Makefile about
signal compatibility.
X.PP
X.SH See Also
ping(1), traceroute(1), The minutes of the IP Provider Metrics BOF (32rd IETF,
5-April-1995, Danvers MA).
X.PP
In the future usage information can be obtained from RFCs and via the
Benchmarking Working Group (BMWG) of the IETF.
X.PP
See ftp://ftp.psc.edu/pub/net_tools/treno.shar for current sources.
X
SHAR_EOF
  chmod 0666 'treno.1' ||
  echo 'restore of treno.1 failed'
  shar_count="`wc -c < 'treno.1'`"
  test 3896 -eq "$shar_count" ||
    echo "treno.1: original size 3896, current size $shar_count"
fi
# ============= treno.c ==============
if test -f 'treno.c' && test X"$1" != X"-c"; then
  echo 'x - skipping treno.c (File already exists)'
else
  echo 'x - extracting treno.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'treno.c' &&
/* Run Idealized Reno over traceroute.
X
X * Interim copyright notice:
X *
X * (c) Pittsburgh Supercomputing Center, Matthew B Mathis, 1995.
X * This software is under active development and may contain bugs.
X * PLEASE DO NOT REDISTRIBUTE.  Current copies can be obtained
X * from ftp://ftp.psc.edu/pub/net_tools/treno.shar.
X *
X * This software is being developed in connection with "IP Provider Metrics"
X * For more information see: http://pscinfo.psc.edu/~mathis/ippm
X *
X * The final version will be published under an unrestricted copyright.
X * 
X * Comments, suggestions and patches welcome.
X *   - Matt Mathis <mathis@psc.edu>
X *
X * Contributors:
X *	Van Jacobson, and all authors of Reno TCP
X *	Van Jacobson, and all authors of traceroute
X *	Jamshid Mahdavi - MTU discovery for traceroute
X *	Sean Doran - updated signals, bug fixes
X */
X
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <strings.h>
X
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/time.h>
X
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/ip_var.h>
X
#include "mplib.h"
X
#define MINBUF 1500	/* For unexpected messages */
X
char usage[] = "\
Usage:  treno [<switch> [<val>]]* [<GW1>...<GWn>] <host>\n\
X      -p <s>      Set the test precision (default to 10 sec elapsed time)\n\
X      -t <ttl>    Send UDP packets (instead of ICMP) with a TTL of <ttl>\n\
X      -a <ttl>    Auto increment TTL through <ttl> (forces -t 1 if needed)\n\
X      -b <mtu>    Use this initial MTU\n\
X      -D          Turn off the receiver's delayed ACK emulation\n\
X      -T          Use more thrust (Cray only: set realtime)\n\
X      -n          Show addresses only\n\
X      -v[<digit>] Verbosity (defaults to 0 or 4)\n\
X      -E          Enable event logging\n\
X      -V, -d      Version, Debug\n\
X      -F <addr>   Select a source interface\n\
X      [<GW1>...<GW>] Gateways to be traversed\n\
X      <host>     Target host\n";
X
char lbanner[] = "\
X hop       packets   min rtt BW\n\
(ttl)     in    out   (mS)  k bps   Address\n";
char lfmt[] = "\n\
= %2d %6d %6d %6.2f = %6g %d.%d.%d.%d %s\n";
X
char sbanner[] = "\
X hop     BW\n\
(ttl)   k bps   Address\n";
char sfmt[] = "\n\
= %2d = %6g %d.%d.%d.%d %s\n";
X
/* global state */
extern int errno;
extern int numeric;			/* don't format names */
void ring(), halt();			/* signal handlers */
u_short checksum();
int haltf = 0;				/* signal happened */
long starttick, tick=0;			/* current second */
struct timeval now;
u_short ident;
u_short port = 32768+666;                /* UDP (raw) port # */
int on = 1;
int npdepth = 0;
X
/* command line options */
int duration = 10;
int ttl= -1;
int autottl=0;
int bbytes= 0;		/* default after parsing */
#define BBYTES 8166	/* FDDI MTU */
int dack=1;		/* delayed ack? */
int thrust=0;
int verbose = 0;
#define DEF_VERB 4
/* Verbosity:
X 0 = show BW
X 2 = show XT
X 4 = loss, other instruments
X 6 = detail on unexpected events
X 8 = show packets
*/
int debug = 0;
int setfrom = 0;
int tos=0;	/* not supported */
struct sockaddr_in ToHost;	/* the remote end */
struct sockaddr_in FromHost;	/* my selected interface */
X
/* event logger */
#define EVSHIFT 24
#define EVMASK	0xFFFFFF
#define EVRING	1024
int eventbuff[EVRING];
int *evp=eventbuff;
int evfd=0;
#define EVPUT(val) {*evp++ =htonl(val);\
X	if (evp >= &eventbuff[EVRING])evflush();}
#define Event(c, v) {if (evfd) {EVPUT(((c)<<EVSHIFT) | ((v) & EVMASK))}}
#define Event2(c, v, v2) {if (evfd) {\
X	if (evp >= &eventbuff[EVRING-1]) EVPUT(0); \
X	Event(c, v); EVPUT(v2);}}
#define EventTime(c, t) Event2(c, (t)->tv_sec, (t)->tv_usec)
X
void evflush()
{
X  struct timeval stamp;
X  NOERROR(write(evfd, eventbuff, (char *)evp - (char *)eventbuff),
X	    "evflush");
X  evp = eventbuff;
X
X  gettimeofday(&stamp, 0);
X  EventTime('L', &stamp);	/* Flush the log (how long did it take?) */
}
X
/* output buffer and goop */
struct opacket {		/* a (udp) probe packet. */
X	struct ip ip;
X	struct udphdr udp;
};
#define UDP_MINLEN (sizeof(struct opacket)-SIZEOFstructip)
int sndsock;				/* send socket */
u_int mtu;				/* Path MTU */
u_int maxseg;				/* TCP mss (slightly less than mtu) */
u_int optlen;				/* option length on the probes */
u_int tcpiplen = (20+20+12);		/* Presumed TCP/IP header w/ options */
u_int probelen;				/* probe packet length */
u_int buflen;				/* largest possible packet */
int udp;
char *outbuf = 0;
struct opacket *op;
struct ip *ip;
struct udphdr *up;
struct icmp *ic;
u_short cksm;	/* precomputed */
#ifdef sgi
/*
X * The SGI uses virtual memory page flips to save copies.  Unfortunately
X * it is broken in the sense that VM copy on write is far slower than 
X * it would have been to do conventional copy to an mbuf in the first place.
X * Work around by not re-using buffers until we think the device driver
X * is done with them.
X */
char **bufflist, **bfl;
#undef IP_HDRINCL
#endif /* sgi */
X
/* input buffer and goop */
int is;				/* icmp (listen) socket */
char *inbuf;
char *buf;
struct sockaddr_in from, goodfrom;
X
/* send a probe packet at the specified sequence */
/* NB: the ICMP checksum calculation assumes that these are consecutive */
int sendseq(sq)
u_short *sq;
{
X  register u_short seq;
X  int erf;
X
again: ;
#ifdef sgi
X  if (!*bfl) bfl = bufflist;
X  outbuf = *bfl++;
X  op = (struct opacket *)outbuf;
X  ip = &op->ip;
X  up = &op->udp;
X  ic = (struct icmp *)op;
#endif /* sgi */
X  seq = *sq = SHORT((*sq) + 1);
X  Event('O', seq);	/* packet out */
X  if (verbose >= 8){
X    int bcnt = mtu;
X    char *outb = outbuf;
X    printf("Sending:\n");
X    netprintip(&outb, &bcnt, 2);
X  }
X  if (udp) {
#ifndef CRAY
X    up->uh_dport = htons(SHORT(port+seq));
#else
X    ((struct udpiphdr *)ip)->ui_dport = htons(SHORT(port+seq));
#endif
X    erf = sendto(sndsock, outbuf, probelen, 0,
X		 (struct sockaddr *) &ToHost, sizeof(ToHost));
X  } else {
X    ic->icmp_seq = seq;
X    if (seq && !cksm--) cksm--; /* ones comp magic */
X    ic->icmp_cksum = cksm;
X    erf = sendto(sndsock, outbuf, probelen, 0,
X		 (struct sockaddr *) &ToHost, sizeof(ToHost));
X  }
X  if (erf != probelen) {
X    if (errno == ENOBUFS) {
X      Event('x', 0);			/* sendto ENOBUFS */
X      return 0;
X    } else if (errno == EMSGSIZE) {
X      setbuffers(drop_mtu(mtu), 0);
X      goto again;
X    } else NOERROR(erf, "sendto");
X  }
X  return 1;
}
X
/*
X * (P)reload send buffers with precomputed packets
X */
X
setbuffers(sz, tl)
int sz, tl;
{
X
X  /* refine the ttl */
X  if (tl) ttl = tl;
X
X  /* refine the size */
X  if (sz && (sz != mtu)) {
X    if (mtu && (sz > mtu)) {
X      printf("Invalid resize: %d\n", sz);
X      exit(1);
X    }
X    mtu = sz;
X    maxseg = mtu-tcpiplen;
/*  if (maxseg > 2048) maxseg &= ~1023;     XXX no rounding */
X    printf(" MTU=%d ", mtu);
X    Event('M', maxseg);		/* set/adjust MTU and mss */
X  }
X
X  /* allocate input and output buffers, if needed */
X  if (!outbuf) {
#define slop 100
X    buflen = maxseg+tcpiplen+optlen+slop;	 /* largest possible message */
X    if (buflen < MINBUF) buflen = MINBUF+slop;
X    inbuf = (char *)malloc(buflen);
X    outbuf = (char *)malloc(buflen);
X    (void) bzero(outbuf, buflen);
X    op = (struct opacket *)outbuf;
X    ip = &op->ip;
X    up = &op->udp;
X    ic = (struct icmp *)op;
X    if (udp) { 
X      ip->ip_hl = SIZEOFstructip >> 2;
X      ip->ip_v = IPVERSION;
X      ip->ip_tos = tos;
X      /*  ip->ip_len = ?? see below */
X      ip->ip_off = (short) IP_DF;			/* always DF */
X      ip->ip_ttl = ttl;
X      ip->ip_p = IPPROTO_UDP;
#ifndef CRAY
X      if (setfrom) ip->ip_src = FromHost.sin_addr;
X      ip->ip_dst = ToHost.sin_addr;
X      up->uh_sport = htons(ident);
#else
X      if (setfrom) ip->ip_src = FromHost.sin_addr.s_addr;
X      ip->ip_dst = ToHost.sin_addr.s_addr;
X      ((struct udpiphdr *)ip)->ui_sport = htons(ident);
#endif
X      /* up->uh_ulen = ?? see below */
X      up->uh_sum = 0;
X    } else {
X      /* set up ICMP packet */
X      ic->icmp_type = ICMP_ECHO;
X      ic->icmp_seq = 1; /* Must match sseq initialization elsewhere */
X      ic->icmp_id = ident;
X      ic->icmp_cksum = 0;
X      /* cksm = checksum(outbuf, probelen); see below */
X    }
#ifdef sgi
#define MAXTRAN 100
X    { int i;
X      bufflist = (char **) malloc((MAXTRAN+2)*sizeof(char *));
X      bufflist[0] = outbuf;
X      for (i = 1; i <= MAXTRAN; i++) {
X	bufflist[i] = (char *)malloc(buflen);
X	bcopy(outbuf, bufflist[i], buflen);
X      }
X      bufflist[MAXTRAN+1] = 0;
X      bfl = bufflist;
X    }
#endif /* sgi */
X  }
X
X  /* "reload buffers" and put in the final touches */
X  if (udp) {
X    probelen = maxseg+tcpiplen-optlen;   /* probe size  w/o options */
#ifdef sgi
X    for (bfl = bufflist; *bfl; ) {
X      outbuf = *bfl++;
X      op = (struct opacket *)outbuf;
X      ip = &op->ip;
X      up = &op->udp;
X      ic = (struct icmp *)op;
#endif /* sgi */
X      ip->ip_len = probelen;
X      ip->ip_ttl = ttl;
X      up->uh_ulen = htons((u_short)maxseg+tcpiplen-SIZEOFstructip);
/*sizeof(struct opacket));*/
#ifdef sgi
X    }
#endif /* sgi */
X  } else {
X    probelen = maxseg+tcpiplen-optlen-SIZEOFstructip;
X    ic = (struct icmp *)outbuf;
X    ic->icmp_cksum = 0;
X    cksm = checksum(outbuf, probelen);
X  }
}
X
/* Search for the next smaller valid mtu */
int drop_mtu(os)
int os;
{
X  static int mtulist[] =	/* from RFC1191 */
X    {32000, 17914, 8166, 4352, 2002, 1492, 1006, 508, 296, 68, 0};
X  int i=0, ns;
X
X  while ((os <= (ns = mtulist[i++])) && ns) ;
X  return (ns);
}
X
int recvseq(sq)
u_short *sq;
{
X  int fromlen, bcnt;
X  int seq, type, code, rident;
X  struct udphdr32o *up;
X
X again:
X  fromlen = sizeof(from);
X  bcnt = recvfrom(is, inbuf, buflen, 0,
X		  (struct sockaddr *) &from, &fromlen);
X  if (bcnt<0) {
X    if (errno != EINTR) perror("recvfrom");
X    return 0;
X  }
X  type = code = rident = -1;
X  buf = inbuf;
X  crackip(inbuf);
X  up = STRUCTALIGN(struct udphdr32o *, cipclient, 0);
X  if (cicmp) {
X    type = cicmp->icmp_type;
X    code = cicmp->icmp_code;
X  };
X  if (udp && cip2 && up &&
X      (type == ICMP_TIMXCEED || type == ICMP_UNREACH) &&
X      (cip2->ip_p == IPPROTO_UDP) ){
X    seq = SHORT(ntohs(up->uh_dport) - port);
X    rident = ntohs(up->uh_sport);
X    if ((type == ICMP_UNREACH) && (rident == ident)) {
X      if (code == ICMP_UNREACH_NEEDFRAG) {
X	int m = ntohl(cicmp->icmp_void) & 0xFFFF;
X	if (!m) m = drop_mtu(mtu);	/* XXX */
X	if (m != mtu) setbuffers(m, 0);
X	goto again;			/* XXX */
X      }
X      autottl = -1;			/* quit soon */
X    }
X  } else {
X    if (!udp &&
X	(ToHost.sin_addr.s_addr == from.sin_addr.s_addr) &&
X	(type == ICMP_ECHOREPLY) ) {
X      seq = SHORT(cicmp->icmp_seq);
X      rident = cicmp->icmp_id;
X    }
X  }
X  if (!cicmp || (rident != ident)) {
X    if (debug || (verbose >= 6)) {
X      printf ("Unexpected message:\n");
X      printf("Ident?, %x != %x\n", rident, ident);
X      netprintip(&buf, &bcnt, npdepth);
X    }
X    else {
X      Event('u', rident);
X      if (verbose >= 4) putchar('u');
X    }
X    goto again;
X  } else {
X    goodfrom = from;
X    if (verbose >= 8) {
X      printf ("Received message:\n");
X      netprintip(&buf, &bcnt, npdepth);
X    }
X  }
X  *sq = seq;
X  return 1;
}
X 
main(argc, argv)
char *argv[];
{
X  char *sv, **av = argv;
X  int nropts= -1;
X	/* ladding - route and data size */
X  int rlen=0;				/* The current route length */
X  int nbix;
X
X  /* sequence numbers.  Unlike TCP, these are always "forward sequence" - 
X     they are never resent (except when wrapped). */
X  u_short sseq;			/* current send sequence */
X  u_short rseq;			/* current receive sequence */
X  u_short erseq;		/* expected (in sequence) receive sequence */
X  u_short rrseq;		/* retran sequence */
X  u_short mrseq;		/* maximum receive sequence */
X
X  /* TCP state variables */
X  u_long cwnd;
X  u_long ssthresh;
X  int dupacks, tcprexmtthresh = 3;
X
X  /* statistics */
X  int in, out;
X  u_short rttseq;
X  u_int minrtt;
X  struct timeval rttstamp;
X
X  /* no buffering on stdout */
X  setvbuf(stdout, 0, _IONBF, 0);
X  numeric=0; /* Since this is for civilians, we format names by default */
X
X  /* general switch cracker */
X  argc--, av++;
X  while (argc > 0 && *av[0] == '-') {
X    sv = *av++; argc--;
X    while (*++sv) switch (*sv) {
X    case 'p':
X      if ((argc > 0) &&
X	  ((duration = atoi(*av++)) > 0) ) {
X	argc--;
X	break;
X      }
X      goto punt;
X    case 't':
X      udp++;
X      if ((argc > 0) &&
X	  ((ttl = atoi(*av++)) > 0) ) {
X	argc--;
X	break;
X      }
X      goto punt;
X    case 'a':
X      if ((argc > 0) &&
X	  ((autottl = atoi(*av++)) > 0) ) {
X	argc--;
X	break;
X      }
X      goto punt;
X    case 'b':
X      if (argc > 0) {
X	bbytes = atoi(*av++); argc--;
X	/* check for legal later */
X	break;
X      }
X      goto punt;
X    case 'T':
X      thrust=1;
X      break;
X    case 'D':
X      dack=0;
X      break;
X    case 'n':
X      numeric=1;
X      break;
X    case 'v':
X      if (isdigit(sv[1])) 
X	verbose = *++sv - '0';
X      else
X	verbose = DEF_VERB;
X      break;	/* No -v is verbose = 0 */
X    case 'E':
X      evfd = open("event.log", O_WRONLY|O_CREAT|O_TRUNC, 0666);	/* lazy */
X      break;
X    case 'V':
X      printversion();
X      exit(0);
X    case 'd':
X      debug++;
X      break;
X    case 'F':
X      if (argc > 0) {
X	setfrom++;
X	setsin(&FromHost, *av++); argc--;
X	break;
X      }
punt:
X      printf(usage);
X      printf("Invalid argument to -%c\n", *sv);
X      exit(1);
X    default:
X      printf(usage);
X      printf("Unknown switch: -%c\n", *sv);
X      exit(1);
X    }
X  }
X  
X  /* Parse and arrange the route */
X  while (argc > 0) {
X    setsin(&route[rlen++], *av++); argc--;
X  }
X  if (rlen < 1)  {
X    printf(usage);
X    printf("You must supply at least one host name\n");
X    exit(1);
X  }
X  bcopy(&route[rlen-1], &ToHost, sizeof(struct sockaddr_in));  /* ICMP */
X
X  /* misc configuration */
X  Event('V', 950308);	/* Event logger version */
X  if (thrust) setthrust();
X  ident = (getpid() & 0xffff) | 0x8000;
X  npdepth = debug?-1:(udp?3:2);
X  if (autottl && !udp) {
X    udp = ttl = 1;
X  }
X  if (!autottl) autottl = ttl;
X  if (!udp) printf("ICMP mode!/n");
X
X  /* set up the icmp socket */
X  NOERROR(is = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), "icmp socket");
X  if (debug) setsockopt(is, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on));
X
X  /* set up the send socket - UDP if we are using it */
X  if (udp) {
X    NOERROR(sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW), "sndsock");
X    if (debug) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
X			  (char *)&on, sizeof(on));
#ifdef IP_HDRINCL
X    NOERROR(setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL,
X		       (char *)&on, sizeof(on)), "IP_HDRINCL");
#endif
X  }
X  else sndsock = is;	/* use the ICMP socket for sending */
X
#define BIGBUF 524288
#define TINYBUF 32768
X  { int sz;
X    for (sz=BIGBUF; sz >= TINYBUF; sz >>= 1) {
X      if (setsockopt(is, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)) >= 0) break;
X    }
X    if (sz != BIGBUF) printf("RCVBUF=%d\n", sz);
X    for (sz=BIGBUF; sz >= TINYBUF; sz >>= 1) {
X      if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &sz, sizeof(sz)) >= 0) break;
X    }
X    if (sz != BIGBUF) printf("SNDBUF=%d\n", sz);
X  }
X
#ifndef IP_HDRINCL
X  if (setfrom) {
X    NOERROR(bind(sndsock, (struct sockaddr *)&FromHost, sizeof(FromHost)),
X	    "Bind from");
X  }
#endif
X
X  /* set the route and check the MTU */
X  optlen = setroute(sndsock, IPOPT_LSRR, rlen, route, 0);
X  { int minlen = optlen + SIZEOFstructip + (udp?UDP_MINLEN:ICMP_MINLEN);
X    if (bbytes) {
X      if (bbytes<minlen) {
X	printf(usage);
X	printf("MTU (-b) must be greater than %d\n", minlen);
X	exit(1);
X      }
X    } else
X      bbytes = udp?BBYTES:576;	/* udp default is FDDI, ICMP is 576 */
X  }
X
X  /* pre loop setup */
X  MP_SIGNAL(SIGALRM,ring);
X  sseq = SHORT(1);
X  haltf = 0;
X  printf((verbose >= 4)?lbanner:sbanner);  
X
X  /* Set up input and output buffers (pre compute output packets) */
X  for (; ttl <= autottl; ttl++) {
X    Event('H', ttl);		/* which hop */
X    setbuffers(bbytes, ttl);
X    bbytes = 0;
X    in = out = 0;
X      
X    /* sync to the system clock */
X    gettimeofday(&now, 0);
X    tick = now.tv_sec;
X    while(tick == now.tv_sec) gettimeofday(&now, 0);
X    starttick = tick = now.tv_sec;
X    EventTime('T', &now);	/* starting time stamp */
X
X    /* TCP's initial state and first packet */
#define RETRAN {sendseq(&sseq); out++; rrseq = sseq;}
X    mrseq = sseq;
X    RETRAN;		/* If mtu discovery, this sets maxseg */
X    rttseq = erseq = sseq;
X    rttstamp = now;
X    minrtt = (u_int) -1;
X    RETRAN;		/* cwnd = 2 packets */
X    dupacks = 0;
X    cwnd = 2 * maxseg;
X    ssthresh = ((u_long) -1) >> 1;
X
X    /* loop for the test duration */
X    while ((++tick <= (starttick+duration)) && ! haltf) {
X      alarm(2);		/* XXX fixed timout: 2 sec HEADWAY */
X      if (verbose < 2) putchar('.');	/* minimal chatter */
X	
X      /* loop for one second */
X      while (tick > now.tv_sec) {
X	u_short tseq;
X	int ev = recvseq(&rseq);
X
X	gettimeofday(&now, 0);
X	EventTime('T', &now);	/* one timestamp per iteration */
X
X	/* emulate the receiver's reassembly queue
X	       and the sender's dupack counter */
X
X	if (!ev) {			/* timout */
X	  dupacks = -1;
X	  if (verbose >= 2) putchar('T');
X	} else {
X	  Event('I', rseq);			/* packet input */
X	  if (NEG(sseq - rseq)){
X	    Event('s', rseq);		/* way out of sequence */
X	    if (debug) printf ("%d:%d:%d", mrseq, rseq, sseq);
X	    continue;
X	  }
X	  in++;
X	  if (NEG(mrseq - rseq)) mrseq = rseq;
X
X	  /* do the rtt estimator */
X	  if (rseq == rttseq) {
X	    u_int rtt = (now.tv_sec - rttstamp.tv_sec) * 1000000 +
X			(now.tv_usec - rttstamp.tv_usec);
X	    if (rtt < minrtt) minrtt = rtt;
X	  }
X
X	  if (dupacks) {			/* already out of order */
X	    dupacks++;
X	    if (dupacks < tcprexmtthresh) {
X	      if ((rseq == erseq) &&
X		  (SHORT(mrseq - erseq) == (dupacks - 1))) {
X		dupacks = 0;	    /* self repaired simple reordering */
X		erseq = SHORT(mrseq + 1);
X	      }
X	    } else if (dupacks > tcprexmtthresh) {
X	      if (!NEG(mrseq - rrseq)) {		/* full recovery */
X		int loss = SHORT(mrseq - erseq) - dupacks +1;
X		if (verbose >= 6) printf("%d ",loss);
X		dupacks = 0;
X		erseq = SHORT(mrseq + 1);
X	      }
X	    } else { /* dupacks == tcprexmtthresh */
X	    }
X	  } else {
X	    if (rseq == erseq) {			/* no holes */
X	      erseq = SHORT(mrseq + 1);
X	      /* emulate the receivers delayed ACK behavior */
X	      if (dack && (mrseq & 0x1)) continue;
X	    } else if (NEG(rseq-erseq)) {	      /* miss-ordered ACK... */
X	      /* XXX */
X	      continue;
X	    } else {
X	      dupacks++;				/* a new hole */
X	      if (verbose >= 2) putchar('X');
X	    }
X	  }
X	}
X
X	/* At this point dupacks != 0 if there is a problem,
X					 otherwise all is well */
X
X	/* Emulate the sender's window dynamics
X	 * This code is is a composite of several TCP implementation.
X	 */
X	ev = '?';		/* unknown Event */
X	if (dupacks) {
X	  if (dupacks < 0) {
X	    u_int win=cwnd / 2 / maxseg;
X	    ev = 't';				/* Event: timeout */
X	    if (win < 2) win = 2;
X	    cwnd = win * maxseg;		/* use old ssthresh */
X	    dupacks = 0;
X	    RETRAN;
X	    goto drop;
X	  }
X
X	  if (dupacks == tcprexmtthresh) {
X	    u_int win;
X	    ev = 'R';				/* Event: fast retran */
X	    if (cwnd <= (ssthresh+maxseg)) cwnd /= 2; /* NEW feature */
X	    win=cwnd / 2 / maxseg;
X	    if (win < 2) win = 2;
X	    cwnd = ssthresh = win * maxseg;
X	    RETRAN;
X	    goto drop;
X	  }
X
X	  if (dupacks > tcprexmtthresh) { 
X	    ev = 'r';				/* Event: fast recovery */
X	    goto send;
X	  }
X
X	  ev = 'd';				/* Event: misc dup ack */
X	  goto drop;
X
X	} else {
X	  if (cwnd > ssthresh) {
X	    ev = 'a';			/* Event: congestion avoidance  */
X	    cwnd += maxseg * maxseg / cwnd;
X	  } else {
X	    ev = 'e';		/* Event: exponential open (aka slow start) */
X	    cwnd += maxseg;
X	  }
X	}
X
X	/* send out to cwnd beyond mrseq */
X      send:
X	Event(ev, cwnd);
X	tseq = SHORT(mrseq + (cwnd/maxseg));
X	while (NEG(sseq - tseq)) {
X	  sendseq(&sseq);
X	  if (NEG(rttseq - mrseq)) {
X	    rttstamp = now;
X	    rttseq = sseq;
X	  }
X	  out++;
X	}
X	continue;
X      drop:
X	Event(ev, cwnd);
X      } /* loop for a second */
X
X    } /* loop for the duration */
X    Event2('A', ttl, goodfrom.sin_addr.s_addr);
X
X    /* drain outstanding packets and report final stats */
X    alarm(2);
X    while(recvseq(&rseq)) in++;
X    {
X      float rtt, bw;
X      char *name="";
X      unsigned char *fp = (unsigned char *) &goodfrom.sin_addr;
X      struct hostent *hp;
X
X      if (!numeric) {
X	hp = gethostbyaddr((char *) &goodfrom.sin_addr, 4, AF_INET);
X	if (hp) name = hp->h_name;
X      }
X
X      rtt = minrtt / 1000.0;
/*    pct = out? (in*100.0)/out: 0.0; */
X      bw = ((float) in)*maxseg*8 / duration / 1000;
X      if (verbose >= 4) {
X	printf(lfmt, ttl,
X	     in, out, rtt,
X	     bw, fp[0], fp[1], fp[2], fp[3], name); }
X      else
X	printf(sfmt, ttl,
X	       bw, fp[0], fp[1], fp[2], fp[3], name);
X    }
X  } /* ttl loop */
X
X  /* close out the event log */
X  evflush();
X  close(evfd);
}
X
void ring(signo)
{
X  MP_RESIGNAL(SIGALRM,ring);
X  alarm(2);
}
SHAR_EOF
  chmod 0640 'treno.c' ||
  echo 'restore of treno.c failed'
  shar_count="`wc -c < 'treno.c'`"
  test 20775 -eq "$shar_count" ||
    echo "treno.c: original size 20775, current size $shar_count"
fi
# ============= mplib.h ==============
if test -f 'mplib.h' && test X"$1" != X"-c"; then
  echo 'x - skipping mplib.h (File already exists)'
else
  echo 'x - extracting mplib.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'mplib.h' &&
/*
X * Multi Ping includes
X *
X * (c) Pittsburgh Supercomputing Center, Matthew B Mathis, 1992.
X * All rights reserved.   See COPYRIGHT.h for additional information.
X */
X
#ifdef CRAY
#include "craytypes.h"
#include <netinet/udp_var.h>
#include <fcntl.h>
#include <sys/cpu.h>
#include <sys/category.h>
#else /* ! CRAY */
#define STRUCTALIGN(type, ptr, offset) (type)(ptr)
#define ip32o ip
#define icmp32o icmp
#define udphdr32o udphdr
#define SIZEOFstructip sizeof(struct ip)
#endif /* CRAY */
X
#define NOERROR(val,msg) {if (((int)(val)) < 0) {perror(msg);exit(1);}}
#define NOTNULL(val,msg) {if (!(val)) {fprintf(stderr,msg);exit(1);}}
#define USEC(st,fi) (((fi)->tv_sec-(st)->tv_sec)*1000000+ \
X		((fi)->tv_usec-(st)->tv_usec))
#define NEG(val)	((val) & 0x8000)	/* Sign of an unsigned short*/
#define SHORT(val)	((val) & 0xFFFF)
X
/************** canonical signal functions ************
X * "The nice thing about standards, is that there are so many to choose from"
X * If anybody can suggest a better way to do this, I am open to suggestions.
X */
#ifdef								POSIX_SIGNALS
#ifndef SV_INTERRUPT
#define SV_INTERRUPT 0
#endif
#define MP_SIGNAL(sig, fcn) {struct sigaction sa, osa; \
X sigemptyset(&sa.sa_mask); sa.sa_handler = fcn; sa.sa_flags = SV_INTERRUPT; \
X NOERROR(sigaction(sig,&sa,&osa),"MP_SIGNAL");}
#define MP_RESIGNAL(sig, fcn) /* Noop */
#define MP_SIG_TYPE "Signals are POSIX style"
#endif /* POSIX_SIGNALS */
X
#ifdef								BSD44_SIGNALS
#define MP_SIGNAL(sig, fcn) { \
X NOERROR(signal(sig, fcn), "MP_SIGNAL"); \
X NOERROR(siginterrupt(sig,1),"SIGINTERRUPT");}
#define MP_RESIGNAL(sig, fcn) /* Noop */
#define MP_SIG_TYPE "Signals are BSD44 style"
#endif /* BSD44_SIGNALS */
X
#ifdef								SYSV_SIGNALS
#define MP_SIGNAL(sig, fcn) NOERROR(signal(sig, fcn), "MP_SIGNAL");
#define MP_RESIGNAL(sig, fcn) /* Noop */
#define MP_SIG_TYPE "Signals are SYSV style"
#endif
X
#ifndef MP_SIGNAL						/* default */
#define MP_SIGNAL(sig, fcn) NOERROR(signal(sig, fcn), "MP_SIGNAL");
#define MP_RESIGNAL(sig, fcn) MP_SIGNAL(sig, fcn)
#define MP_SIG_TYPE "Signals are old style"
#endif
X
#ifndef MP_EXTERN
#define MP_EXTERN extern
#else
char sig_type[] = MP_SIG_TYPE;		/* strings <cmd> | fgrep Signals */
#endif
X
/* globals used by crackip(buff) to indicate parts of the message */
/* In all cases zero indicates not found, in order of increasing address */
MP_EXTERN	struct ip	*cip;		/* IP header */
MP_EXTERN	u_char		*cipopt;	/* options */
MP_EXTERN	int		cipolen;	/* option length (bytes) */
MP_EXTERN	struct icmp32o	*cicmp;		/* icmp message */
MP_EXTERN	struct ip32o	*cip2;		/* IP that provoked an icmp error */
MP_EXTERN	u_char		*cipopt2;	/* options within the error */
MP_EXTERN	int		cipolen2;	/* option length */
MP_EXTERN	u_char		*cipclient;	/* client data */
X
/* global used by netprintf */
extern int numeric;				/* 1 -> suppress namelookups */
X
/* globals used by setroute() */
#define MAXOPTS 40	/* (15-5)*4 bytes */
#define MAXROUTE 20     /* But only if we can do one hop at a time */
MP_EXTERN struct sockaddr_in route[MAXROUTE];	/* The requested route */
SHAR_EOF
  chmod 0644 'mplib.h' ||
  echo 'restore of mplib.h failed'
  shar_count="`wc -c < 'mplib.h'`"
  test 3064 -eq "$shar_count" ||
    echo "mplib.h: original size 3064, current size $shar_count"
fi
# ============= mplib.c ==============
if test -f 'mplib.c' && test X"$1" != X"-c"; then
  echo 'x - skipping mplib.c (File already exists)'
else
  echo 'x - extracting mplib.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'mplib.c' &&
/*
X * Multi Ping library functions
X *
X * (c) Pittsburgh Supercomputing Center, Matthew B Mathis, 1992.
X * All rights reserved.   See COPYRIGHT.h for additional information.
X *
X * Content:
X * setthrust() - More thrust if we are on a Cray
X * setsin(sin, name) - Mash host name into a socket struct
X * setroute(...) - general set route options
X * crackip(buff) - locate all headers within a packet
X * checksum(data, len) - a simple checksum
X *
X */
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <ctype.h>
X
#include <sys/types.h>
#include <sys/socket.h>
X
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/ip_var.h>
X
#define MP_EXTERN
#include "mplib.h"
X
setthrust()
{
#ifdef CRAY
X    /* More thrust if we are on a Cray,  Claim "Real Time",
X       so we get our cycles even in the presence of gready users */
X    int cpufd;
X    static struct cpudev cdev = { 0 };
X
X    NOERROR(cpufd=open("/dev/cpu/any", O_RDONLY, 0), "Open /dev/cpu");
X    cdev.cat = C_PROC;
X    cdev.id = 0;
X    NOERROR(ioctl(cpufd, CPU_SETRT, &cdev), "ioctl set RT");
X    NOERROR(close(cpufd), "close cpufd");
X    errno=0;
X    nice(-40);
X    if(errno) NOERROR(-1, "nice");
#else /* CRAY */
X    errno=0;
X    nice(-20);
X    if(errno) NOERROR(-1, "nice");
#endif /* CRAY */
}
X
X
/*
X * setsin - mash some form of a host name into a socket struct
X * BEWARE: does not set the port number
X */
int setsin(sin, name)
X     struct sockaddr_in *sin;
X     char *name;
{
X  struct hostent *hp;
X  
X  bzero((char *)sin, sizeof(struct sockaddr_in));
X  if (isdigit(*name)) {
X    sin->sin_addr.s_addr = inet_addr(name);
X  }
X  else if (hp = gethostbyname(name)) {
X    bcopy(hp->h_addr, (char *)&sin->sin_addr, hp->h_length);
X  }
X  else {
X    printf("I Don't understand host/gateway name %s\n",name);
X    exit(2);
X  }
X  sin->sin_family = AF_INET;
X  sin->sin_port = 0;		/* must be set again elsewhere */
X  return(1);
}
X
/* general set route options */
int setroute(sock, mode, rlen, route, rr)
X     int sock;			/* the socket to set */
X     int mode;			/* 0 (none), LSRR or SSRR */
X     int rlen;			/* number of the above */
X     struct sockaddr_in *route;	/* the requested route */
X     int rr;				/* number of record route, -1 => MAX */
{
X  u_char optlist[MAXOPTS], *oix;
X  
X  bzero(optlist, MAXOPTS);		/* all end of options */
X  oix = optlist;
X  if (mode && (rlen>1)) {
X    int i;
X    
X    *oix++ = mode;			/* BUG does not enforce LSRR/SSRR */
X    *oix++ = 3+rlen*4;
X    *oix++ = 4;
X    for (i=0; i<rlen; i++) {
#ifdef CRAY
X      bcopy(((char *)&route[i].sin_addr) + 4 /* crock */, oix, 4);
#else
X      bcopy(&route[i].sin_addr, oix, 4);
#endif
X      oix +=4;
X    }
X  }
X  if (rr<0) 
X    rr = (oix-optlist-3)/4;
X  if (rr>0) {
X    *oix++ = IPOPT_RR;		/* record route */
X    *oix++ = 3 + rr*4;
X    *oix++ = 4;
X    oix += rr*4;
X  }
X  while ((oix-optlist)&3) oix++;	/* force mult of 32 bits */
X  if (optlist-oix) {
X    NOERROR(setsockopt(sock, 0, IP_OPTIONS, optlist, oix-optlist),
X	    "set sockoptions");
X  }
X  return((oix-optlist)?(oix-optlist-4):0);	/* deduct initial host */
}
X
/* #define WHYRET(str) printf(str) */
#define WHYRET(str) /* nothing */
/* crackip - a general routine for locating all of the headers within ip */
/* BUG this should be passed a structure rather than using globals but.... */
crackip(buff)
X     u_char * buff;
{
X  cip = 0; cipopt = 0; cipolen = 0; cicmp = 0;
X  cip2 = 0; cipopt2 = 0; cipolen2 =0; cipclient = 0;
X  
X  cip = (struct ip *) buff;
X  if ((cip->ip_v != 4) || (cip->ip_hl <5)) {
X    cip = 0;
WHYRET("cip->ip_v\n");
X    return;
X  }
X  buff += SIZEOFstructip;
X  
X  if (cip->ip_hl != 5) {
X    cipopt = buff;
X    buff += cipolen = (cip->ip_hl - 5) * 4;
#ifdef CRAY
X    if (cipolen%8) {
X      printf("Can not parse packets w/ options which are not full words\n");
X      return;
X    }
#endif
X  }
X
X  if (cip->ip_p != IPPROTO_ICMP) {
X    cipclient = buff;	/* immediately follows the ip header */
WHYRET("cip->ip_p != IPPROTO_ICMP\n");
X    return;
X  }
X 
X  cicmp = STRUCTALIGN(struct icmp32o *, buff, 4);
X  buff += ICMP_MINLEN;
X
X  cip2 = STRUCTALIGN(struct ip32o *, buff, 4);
X  if ((cip2->ip_v != 4) || (cip2->ip_hl <5)) {
X    cip2 = 0;
WHYRET("cip2->ip_v\n");
X    return;
X  }
X  buff += SIZEOFstructip;
X  
X  if (cip2->ip_hl != 5) {
X    cipopt2 = buff;
X    buff += cipolen2 = (cip2->ip_hl - 5) * 4;
#ifdef CRAY
X    if (cipolen2%8) {
X      printf("Can not parse packets w/ options which are not full words\n");
X      return;
X    }
#endif
X  }
X  
X  cipclient = buff;	/* immediately follows the ip header */
X  return;
}
X
#ifndef CRAY
/* a simple checksum */
u_short checksum(data, len)
X     register u_short *data;
X     register int len;
{
X  register u_long sum;
X  sum = 0;
X  
X  while(len>1) {
X    sum += *data++;
X    len -= 2;
X  }
X  
X  if (len) {
X    sum += *data & ntohs(0xFFFF0000);
X  }
X  sum = (sum>>16) + (sum&0xFFFF);
X  sum += (sum>>16);
X  return((u_short) (~sum));
}
X
#else /* CRAY */
X
/* Nothing is simple on the cray */
u_short checksum(data, len)
X     register u_long *data;
X     register int len;
{
X  register u_long sum;
X  sum = 0;
X
X  /* 8 bytes at a time */
X  while(len>7) {
X    sum += ((*data>>32) & 0xFFFFFFFF) + (*data & 0xFFFFFFFF);
X    data++;
X    len -= 8;
X  }
X
X  switch (len) {
X  case 0:
X    break;
X  case 1:
X    sum += ((*data>>32) & 0xFF000000); break;
X  case 2:
X    sum += ((*data>>32) & 0xFFFF0000); break;
X  case 3:
X    sum += ((*data>>32) & 0xFFFFFF00); break;
X  case 4:
X    sum += ((*data>>32) & 0xFFFFFFFF); break;
X  case 5:
X    sum += ((*data>>32) & 0xFFFFFFFF) + (*data & 0xFF000000); break;
X  case 6:
X    sum += ((*data>>32) & 0xFFFFFFFF) + (*data & 0xFFFF0000); break;
X  case 7:
X    sum += ((*data>>32) & 0xFFFFFFFF) + (*data & 0xFFFFFF00); break;
X  }
X  
/* Fold 32+some bits into 16 bits ones complement */
X  sum = (sum>>16) + (sum&0xFFFF);
X  sum = (sum>>16) + (sum&0xFFFF);
X
X  return((u_short) (sum^0xFFFF));
}
#endif /* CRAY */
SHAR_EOF
  chmod 0644 'mplib.c' ||
  echo 'restore of mplib.c failed'
  shar_count="`wc -c < 'mplib.c'`"
  test 5944 -eq "$shar_count" ||
    echo "mplib.c: original size 5944, current size $shar_count"
fi
# ============= netprintf.c ==============
if test -f 'netprintf.c' && test X"$1" != X"-c"; then
  echo 'x - skipping netprintf.c (File already exists)'
else
  echo 'x - extracting netprintf.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'netprintf.c' &&
/*****************************************************************
X *
X * netprintf.c - Print functions to display network headers and data.
X *
X * (c) Matthew B Mathis, Carnegie Mellon University, 1988, 1993
X * All rights reserved.   See COPYRIGHT.h for additional information.
X *
X * Guiding philosophy: every byte of the message is printed exactly once
X *	in hex is network order, interspersed with commentary driven by
X *	the content.
X *
X * The following arguments are common:
X *		The first two are updated as the packet is displayed.
X *	char **pkt;	Pointer to the 'cursor' used to print the packet.
X *	int *len;	Pointer to the remaining undisplayed length.
X *	int fdata;	Count of layers to format, -1 => all
X */
X
/* Fix Ultrix 4.3a brain damage  */
#ifdef _CFE
#undef _CFE
#endif
X
#include <stdio.h>
#if !defined(sun)
#include <stdarg.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
X
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
X
#ifdef CRAY
#include "craytypes.h"
#else /* CRAY */
#define STRUCTALIGN(type, ptr, offset) (type)(ptr)
#define icmp32o icmp
#define SIZEOFstructip sizeof(struct ip)
#endif /* CRAY */
X
int numeric=1;		/* default to 1  -> suppress namelookups */
X
#ifdef UNDEF
/*****************************************************************
X * Display an arp packet
X *
X */
fprtarp(ei,ar)
struct erroritem *ei;			/* The error under examination */
register struct addres *ar;
{
X    fprtpkt ( "ARP     :   HRD=#x #x   PRO=#x #x   HLN=#x   PLN=#x    OP=#x #x\n",
X	    (char *) ar);
X    if (ei->p_off + 8 + (2 * (ar -> ar_hln + ar -> ar_pln)) <= ERRORCOPY) {
X
X	fprtdata ( "sha     :", ar_sha (ar), ar -> ar_hln);
X	fprtdata ( "spa     :", ar_spa (ar), ar -> ar_pln);
X	fprtdata ( "tha     :", ar_tha (ar), ar -> ar_hln);
X	fprtdata ( "tpa     :", ar_tpa (ar), ar -> ar_pln);
X    }
X    else {
X	fprtdata("ARP data:",ar->ar_data, ERRORCOPY - 8 - ei->p_off);
X    }
}
#endif /* undef */
X
/*****************************************************************
X *	Display an IP packet
X *
X */
netprintip(pkt,len,fdata)
u_char **pkt;
int *len;
int fdata;
{
X    struct ip *ip = (struct ip *) (*pkt);
X    int     optlen,xolen;
X    int	    proto,onum;
X    static struct optab {int olen; char *ofmt;} otab[] = {
X	{ 1,"IPoption= #x (End of options)\n"},			/* 0 */
X	{ 1,"IPoption= #x (No op)\n"},				/* 1 */
X	{11,"IPoption= #x (Security) #x #x#x #x#x #x#x #x#x#x\n"}, /* 2 */
X	{-1,"IPoption= #x (LSRR) Len=#x Point=#x"},		/* 3 */
X	{-1,"IPoption= #x (Timestamp) Len=#x Point=#x Flgs=#x"}, /* 4 */
X	{ 1,"IPoption= #x (Unused)"},				/* 5 */
X	{ 1,"IPoption= #x (Unused)"},				/* 6 */
X	{-1,"IPoption= #x (RR) Len=#x Point=#x"},		/* 7 */
X	{ 4,"IPoption= #x (Stream ID) Len=#x Id=#x #x\n"},	/* 8 */
X	{-1,"IPoption= #x (SSRR) Len=#x Point=#x"},		/* 9 */
X	{ 1,"IPoption= #x (Unused)\n"},				/* 10 */
X	{ 1,"IPoption= #x (Unused)\n"},				/* 11 */
X	{ 1,"IPoption= #x (Unused)\n"},				/* 12 */
X	{ 1,"IPoption= #x (Unused)\n"},				/* 13 */
X	{ 1,"IPoption= #x (Unused)\n"},				/* 14 */
X	{ 1,"IPoption= #x (Unused)\n"}				/* 15 */
X    };
X
X    fprtpkt(pkt, len, "IP      : V/Hl=#x Tos=#x Len=#x #x Id=#x #x Frag=#x #x\n");
X    fprtpkt(pkt, len, "IP      : Ttl=#x Proto=#x Hcrc=#x #x\n");
X    fprtpkt(pkt, len, "IP      : Sa=#x #x #x #x (#H)\n");
X    fprtpkt(pkt, len, "IP      : Da=#x #x #x #x (#H)\n");
X
X    optlen = ip->ip_hl * 4 - SIZEOFstructip;
X    if (optlen > 0) {
X	optlen = (optlen < (*len)) ? optlen : (*len);
X	(*len) -= optlen;
X	while (optlen>0) {
X	    onum = (**pkt) & 0xF;
X	    xolen = (otab[onum].olen>0) ?0 :((*pkt)[1]-3);/* extra options */
X	    fprtpkt(pkt, &optlen, otab[onum].ofmt);
X	    if (!xolen) continue;
X	    while (xolen>0) {
X		fprtpkt(pkt, &optlen,"\n    Host: #x #x #x #x (#H)");
X		xolen -= 4;
X	    }
X	    printf("\n");
X	}
X    }
X
X    if (!--fdata) {
X      printf("IP  data: ... (%d bytes)\n", *len);
X      return;
X    }
X    proto = (int) ip->ip_p;			/* get the protocol */
X    switch (proto){
X	case IPPROTO_ICMP:
X	    netprinticmp(pkt, len, fdata);
X	    break;
X	case IPPROTO_EGP:
X	    fprtdata(pkt, len, "EGP     :");
X	    break;
X	case IPPROTO_UDP:
X	    fprtpkt(pkt, len, "UDP hdr : SPort=#x #x DPort=#x #x   Len=#x #x CkSum=#x #x\n");
X	    if (!--fdata) {
X	      printf("UDP data: ... (%d bytes)\n", *len);
X	      return;
X	    }
X	    fprtdata(pkt, len, "UDP data:");
X	    break;
X	case IPPROTO_TCP:
X	    netprinttcp(pkt, len, fdata);
X	    break;
X	default:
X	    fprtdata(pkt, len, "IP data?:");
X    }
}
/*****************************************************************
X *	Display a TCP
X *
X */
netprinttcp(pkt,len,fdata)
u_char **pkt;
int *len;
int fdata;
{
X  struct tcphdr *tcp = (struct tcphdr *) (*pkt);
X  int     optlen, onum;
X  static struct optab {int olen; char *ofmt;} otab[] = {
X    { 1, "TCPopt  = #x (End of options)\n"},		/* 0 */
X    { 1, "TCPopt  = #x (No op)\n"},			/* 1 */
X    { 4, "TCPopt  = #x (MSS) Len=#x mss=#x #x\n"},	/* 2 */
X    { 3, "TCPopt  = #x (Window Scale) Len=#x Shift=#x\n"}, /* 3 */
X    { 1, "TCPopt  = #x (unused ?)\n"},			/* 4 */
X    { 1, "TCPopt  = #x (unused ?)\n"},			/* 5 */
X    { 1, "TCPopt  = #x (unused ?)\n"},			/* 6 */
X    { 1, "TCPopt  = #x (unused ?)\n"},			/* 7 */
X    { 10, "TCPopt  = #x (Timestamps) Len=#x TSval=#x #x #x #x  TSecr=#x #x #x #x\n"},			/* 8 */
X    { 1, "TCPopt  = #x (unused ?)\n"},			/* 9 */
X    { 1, "TCPopt  = #x (unused ?)\n"},			/* 10 */
X    { 1, "TCPopt  = #x (unused ?)\n"},			/* 11 */
X    { 1, "TCPopt  = #x (unused ?)\n"},			/* 12 */
X    { 1, "TCPopt  = #x (unused ?)\n"},			/* 13 */
X    { 1, "TCPopt  = #x (unused ?)\n"},			/* 14 */
X    { 1, "TCPopt  = #x (unused ?)\n"},			/* 15 */
X  };
X
X  fprtpkt(pkt, len, "TCP     : sp=#x #x dp=#x #x   seq=#x #x #x #x   ack=#x #x #x #x\n");
X  fprtpkt(pkt, len, "TCP     : do=#x    cb=#x      win=#x #x   cks=#x #x    up=#x #x\n");
X
X  optlen = (tcp->th_off - 5) * 4;
X  if (optlen > 0) {
X    optlen = (optlen < (*len)) ? optlen : (*len);
X    (*len) -= optlen;
X    while (optlen>0) {
X      onum = (**pkt) & 0xF;
X      fprtpkt(pkt, &optlen, otab[onum].ofmt);
X    }
X  }
X
X  if (!--fdata) {
X    printf("TCP data: ... (%d bytes)\n", *len);
X    return;
X  }
X
X  fprtdata(pkt, len, "TCP rest:");
}
/*****************************************************************
X *	Display an ICMP
X *
X */
netprinticmp(pkt,len,fdata)
u_char **pkt;
int *len;
int fdata;
{
X    struct icmp32o *icmp;
X
X    int itype;
X    static char *ictypes[] = {
X		"Echo Reply","Undef","Undef","Dest Unreach","Source Quence",
X		"Redirect","Undef","Undef","Echo","Undef","Undef",
X		"Time Exceeded","Parameter Problem","Timestamp",
X		"Timestamp Reply","Info Req","Info Reply", "Mask Req",
X		"Mask Reply", "????"};
X    icmp = STRUCTALIGN(struct icmp32o *, *pkt, 4);
X
X    itype = icmp->icmp_type;
X    if (itype<0 || itype>ICMP_MASKREPLY) itype = ICMP_MASKREPLY+1;
X
X    fprtpkt(pkt, len, "ICMP    : Type=#x (%s) Code=#x Cksm=#x #x ",
X			ictypes[itype]);
X    switch (itype) {
X
X    case ICMP_UNREACH:
X    case ICMP_TIMXCEED:
X    case ICMP_SOURCEQUENCH:
X	fprtpkt(pkt, len, "Unused=#x #x #x #x\n");
X	goto err;
X    case ICMP_REDIRECT:
X	fprtpkt(pkt, len, "Gway=#x #x #x #x\n");
X	goto err;
X    case ICMP_PARAMPROB:
X	fprtpkt(pkt, len, "Point=#x Unused=#x #x #x\n");
err:	if (!--fdata) return;
X	netprintip(pkt, len, fdata);	/* note possible recursion */
X	return;
X    case ICMP_ECHOREPLY:
X    case ICMP_ECHO:
X    case ICMP_TSTAMP:
X    case ICMP_TSTAMPREPLY:
X    case ICMP_IREQ:
X    case ICMP_IREQREPLY:
X    case ICMP_MASKREQ:
X    case ICMP_MASKREPLY:
X	fprtpkt(pkt, len, "Id=#x #x Seq=#x #x\n");
X	break;
X    default:
X	printf("\n");
X    }
X
X    if (!--fdata) {
X      printf("ICMPdata: ... (%d bytes)\n", *len);
X      return;
X    }
X
X    fprtdata(pkt, len, "ICMPdata:");
}
/*****************************************************************
X *	printf a portion of a packet per a format string
X *		"%<fmt>" "printfs" args, does not count as part of the message
X *		"#<fmt> displays bytes from the message
X *	Format  "#x" becomes "%2x"
X *		"#o" becomes "%03d"
X *		"#H" recans prior 4 bytes and displays a host addr/name
X *			Uses global 'numeric' to prevent name lookups
X *	% only supports formats %s, %d 
X */
#ifdef va_start
fprtpkt(pkt, len, fmt, va_args)
u_char **pkt;
int *len;
char *fmt;                      /* format string */
{
X  va_list argl;
#else /* va_start */
fprtpkt(pkt,len,fmt,args)
u_char **pkt;
int *len;
char *fmt;			/* format string */
int *args;			/* arguments for % formats */
/* VARARGS */
{
X    int **argl = &args;
#endif /* va_start */
X    register char c;
X    struct hostent *hp;
X
#ifdef va_start
X    va_start(argl, fmt);
#endif /* va_start */
X    while (c = *fmt++) {
X	if (c == '#') {
X	    c = *fmt++;
X	    if ( ((*len) <= 0) && (c > 'a') ){
X		printf("??");
X		continue;
X	    }
X	    switch (c) {
X		case 'x':
X		    printf ( "%02x", *(*pkt)++);
X		    (*len)--;
X		    break;
X		case 'o':
X		    printf ( "%03d", *(*pkt)++);
X		    (*len)--;
X		    break;
X		case 'H':
X		    printf("%d.%d.%d.%d",
X			(*pkt)[-4],(*pkt)[-3],(*pkt)[-2],(*pkt)[-1]);
X		    if (numeric || !(hp=gethostbyaddr((*pkt)-4, 4, AF_INET)))
X			break;
X		    printf(" %s",hp->h_name);
X		    break;
X		default:
X		    printf("#%c",c);
X	    }
X	}
X	else if (c == '%'){
X	    c = *fmt++;
X	    switch (c) {
X		case 'd':
#ifdef va_start
X	            printf("%d", va_arg(argl, int));
#else
X		    printf("%d", *argl++);
#endif
X		    break;
X		case 's':
#ifdef va_start
X		    printf("%s", va_arg(argl, char *));
#else
X		    printf("%s", (char *) *argl++);
#endif
X		    break;
X		default:
X		    printf("%%%c",c);
X	    }
X	}
X	else {
X	    printf("%c",c);
X	}
X    }
#ifdef va_start
X    va_end(argl);
#endif /* va_start */
}
X
/*****************************************************************
X *	Display undeciphered bytes in a reasonable format
X *
X */
fprtdata(pkt,len,comment)
u_char **pkt;
int *len;
char *comment;				/* A name for what we are showing */
{
X    int     cnt;
X    for (cnt = 0; (*len)>0 ; cnt++,(*len)-- ) {
X	if (((cnt) % 16) == 0) {
X	    if (cnt != 0) printf ( "\n");
X	    printf ( "%s ", comment);
X	}
X	else if (((cnt) % 8) == 0)
X	    printf ( "   ");
X	printf ("%02x ", *(*pkt)++);
X    }
X    printf ("\n");
}
/* ruler        "comment : dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd */
SHAR_EOF
  chmod 0644 'netprintf.c' ||
  echo 'restore of netprintf.c failed'
  shar_count="`wc -c < 'netprintf.c'`"
  test 10255 -eq "$shar_count" ||
    echo "netprintf.c: original size 10255, current size $shar_count"
fi
# ============= version.c ==============
if test -f 'version.c' && test X"$1" != X"-c"; then
  echo 'x - skipping version.c (File already exists)'
else
  echo 'x - extracting version.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'version.c' &&
/*
X * Version.c
X *
X * (c) Pittsburgh Supercomputing Center, Matthew B Mathis, 1988, 1990, 1992.
X * All rights reserved.   See COPYRIGHT.h for additional information.
X */
X
#include "version.h"
X
printversion()
{
X  printf("%s %s\n", SVERSION, VERSION);
}
SHAR_EOF
  chmod 0644 'version.c' ||
  echo 'restore of version.c failed'
  shar_count="`wc -c < 'version.c'`"
  test 252 -eq "$shar_count" ||
    echo "version.c: original size 252, current size $shar_count"
fi
# ============= sharversion.h ==============
if test -f 'sharversion.h' && test X"$1" != X"-c"; then
  echo 'x - skipping sharversion.h (File already exists)'
else
  echo 'x - extracting sharversion.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'sharversion.h' &&
#define SVERSION "Shar:08/11/95_14:28:11"
SHAR_EOF
  chmod 0644 'sharversion.h' ||
  echo 'restore of sharversion.h failed'
  shar_count="`wc -c < 'sharversion.h'`"
  test 42 -eq "$shar_count" ||
    echo "sharversion.h: original size 42, current size $shar_count"
fi
# ============= version.h ==============
if test -f 'version.h' && test X"$1" != X"-c"; then
  echo 'x - skipping version.h (File already exists)'
else
  echo 'x - extracting version.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'version.h' &&
#define SVERSION "Shar:08/11/95_14:28:11"
#define VERSION "Edit:Never"
SHAR_EOF
  chmod 0644 'version.h' ||
  echo 'restore of version.h failed'
  shar_count="`wc -c < 'version.h'`"
  test 71 -eq "$shar_count" ||
    echo "version.h: original size 71, current size $shar_count"
fi
exit 0
