Package: iputils-tracepath
Version: 3:20071127-1
Severity: wishlist
Tags: patch

The patch below extends tracepath for using a fixed destination port.  

Normally, tracepath employs a UDP destination port sweep to match 
received ICMP packets to the sent probe.  Alas, this breaks the trace 
when a port filtering firewall is encountered.  

The fixed destination port match is implemented by creating multiple 
sockets.  Each probe is then sent from a different socket so using a 
different source port.  Thus, the probe TTL follows from the socket 
on which the ICMP packet is received.  

Both matching methods use a sequential history table consisting of the 
4-tuple entries ( socket-fd, dest-port, (probe-ttl, timeval) ).  The 
first two fields are initialized once, in main(), and the next two upon 
probe transmission.

The extended argument syntax is:

tracepath 195.162.203.238 [base]
  source port: one random free port
  target port: incrementing: base, base+1, ...

tracepath [-b saddr] 195.162.203.238/53 
  source address: bind()'ed to saddr
  source port: multiple random free ports using 64 file descriptors
  target port: fixed, here at 53

tracepath -b saddr/sport 195.162.203.238[/base]
  source: bind()'ed to saddr and sport
  target port: incrementing: base, base+1, ...

The default destination base port still is 44444 and all changes should 
be backward compatible.  If some code seems questionable then I am 
willing to clarify the proposed patch.  
------------------------------------------------------------------------------
Linux stargate 2.4.23 #27 Mon Mar 22 23:09:25 CET 2004 i686
# fixed destination port 2.4 kernel
stargate:~$ tracepath 134.58.127.1/53
 1?: [LOCALHOST] (0.0.0.0)                                pmtu 1500
 1:  10.89.239.252 (10.89.239.252)                          9.365ms (0) 
 2:  be-bru-rr-01-fe-1-0-0.chello.be (195.162.196.182)      7.964ms (0) 
 3:  GW-kuleuven.customer.tvd.be (195.162.203.238)         16.407ms (0) 
 4:  bones1.kuleuven.net (134.58.253.58)                   10.922ms 
 5:  cisco-kulnet.kuleuven.ac.be (134.58.254.62)           10.069ms (0) 
 6:  lrswitch-ludit-srv-1.kuleuven.ac.be (134.58.255.13)    9.840ms (0) 
 7:  reply received from 134.58.127.1/53                   10.021ms (12)
     Resume: pmtu 1500 

Linux reddwarf 2.6.16.1 #2 Wed Mar 29 03:38:43 CEST 2006 i686
# fixed destination port 2.6 kernel
reddwarf:~$ tracepath 134.58.127.1/53
 1:  reddwarf.picaros.org (172.24.105.1)                    0.142ms pmtu 1500
 1:  stargate.picaros.org (172.24.105.14)                   1.058ms 
 2:  10.89.239.252 (10.89.239.252)                          9.468ms (0) 
 3:  be-bru-rr-01-fe-1-0-0.chello.be (195.162.196.182)      9.302ms (0) 
 4:  GW-kuleuven.customer.tvd.be (195.162.203.238)          9.889ms (0) 
 5:  bones1.kuleuven.net (134.58.253.58)                    9.132ms 
 6:  cisco-kulnet.kuleuven.ac.be (134.58.254.62)            9.371ms (0) 
 7:  lrswitch-ludit-srv-1.kuleuven.ac.be (134.58.255.13)   12.850ms (0) 
 8:  reply received from 134.58.127.1/53                   10.261ms (12)
     Resume: pmtu 1500 

Linux reddwarf 2.6.25.1 #2 Sun May 25 17:46:30 CEST 2008 i686
# u16 roll over
reddwarf:~$ tracepath google.be 65533
 1:  d51530884.access.telenet.be (81.83.8.132)              0.159ms pmtu 1500
 1:  d51530881.access.telenet.be (81.83.8.129)             32.039ms (0) 
 2:  dD5E0FAE1.access.telenet.be (213.224.250.225)         11.214ms 
 3:  dD5E0FD41.access.telenet.be (213.224.253.65)           9.812ms 
 4:  send failed dest 72.14.221.104/0
     Resume: pmtu 1500

# arp timeout
reddwarf:~$ tracepath orion                    
 1:  reddwarf.picaros.org (172.24.105.1)                    0.178ms pmtu 1500
 1:  reddwarf.picaros.org (172.24.105.1)                  3001.764ms !H
 1:  reddwarf.picaros.org (172.24.105.1)                  1992.199ms !H
 1:  reddwarf.picaros.org (172.24.105.1)                  980.235ms !H
     Resume: pmtu 1500
------------------------------------------------------------------------------
--- iputils-s20071127/tracepath-dist.c  2007-11-27 01:57:27.000000000 +0100
+++ iputils-s20071127/tracepath.c       2009-03-03 15:26:35.000000000 +0100
@@ -28,44 +28,99 @@
 #define IP_PMTUDISC_PROBE      3
 #endif
 
-struct hhistory
-{
-       int     hops;
-       struct timeval sendtime;
+struct probehdr {
+       __u32 ttl;
+       struct timeval tv;
 };
 
-struct hhistory his[64];
-int hisptr;
-
-struct sockaddr_in target;
-__u16 base_port;
+struct helement {
+  struct probehdr hdr;
+  int fd;
+  unsigned port;
+};
 
-const int overhead = 28;
-int mtu = 65535;
-int hops_to = -1;
-int hops_from = -1;
-int no_resolve = 0;
+struct hhistory {
+  int ptr, size;
+  struct helement *e;
+};
 
-struct probehdr
-{
-       __u32 ttl;
-       struct timeval tv;
+struct hopsinfo {
+  int to, from;
 };
 
-void data_wait(int fd)
-{
+const static int overhead=28, maxttl=255;
+
+/* in:  hptr    the slot just used to send a probe packet
+ *      toport  destination port of the packet returned as ICMP payload
+ *
+ * out: slot number in history list
+ */
+int his_slot(int hptr, unsigned toport, struct hhistory *his) {
+  int ret=-1;
+
+  if(his->e[hptr].port==toport) {
+    ret=hptr;
+    /* printf("** hit hptr=%i toport=%u\n", hptr, toport); */
+  } else {
+    int i;
+    int hfd=his->e[hptr].fd;
+
+    i=hptr+1;
+    if(i>=his->size) i=0;
+
+    while(i!=hptr) {
+      struct helement *ele=&his->e[i];
+      if(ele->fd==hfd && ele->port==toport) {
+       ret=i;
+       break;
+      }
+      i++;
+      if(i>=his->size) i=0;
+    }
+    /* printf("** nor hptr=%i toport=%u ret=%i\n", hptr, toport, ret); */
+  }
+
+  return ret;
+}
+
+int data_wait(struct hhistory *his) {
+  int err=0;
        fd_set fds;
        struct timeval tv;
+  int i, maxfd=-1, lastfd=-2;
+
        FD_ZERO(&fds);
+
+  for(i=0; i<his->size; i++) {
+    int fd=his->e[i].fd;
+
+    if(fd>=0 && fd!=lastfd) {
+      lastfd=fd;
        FD_SET(fd, &fds);
+    }
+    if(maxfd<fd) maxfd=fd;
+  }
        tv.tv_sec = 1;
-       tv.tv_usec = 0;
-       select(fd+1, &fds, NULL, NULL, &tv);
+  tv.tv_usec = 10000;     /* ARP timeout is 3s */
+  err=select(maxfd+1, &fds, NULL, NULL, &tv);
+
+  return err;
 }
 
-int recverr(int fd, int ttl)
+/* in:  hptr  the slot just used to send a packet
+ * return: <0 no error data received, =0 reached, >0 mtu change or timeout
+ */
+int recv_err(int hptr, int no_resolve, int *mtu, struct hhistory *his, struct 
hopsinfo *hops)
 {
-       int res;
+  int progress = -1;
+  int cmsgcnt = 16;  /* maximum control messages */
+  int fd, ttl;
+
+  ttl=his->e[hptr].hdr.ttl;
+  fd=his->e[hptr].fd;
+
+  /* Uncomment below to not see repeat packets, e.g. ARP timeout */
+  while(cmsgcnt-- /* && progress */) {
        struct probehdr rcvbuf;
        char cbuf[512];
        struct iovec  iov;
@@ -74,15 +129,10 @@ int recverr(int fd, int ttl)
        struct sock_extended_err *e;
        struct sockaddr_in addr;
        struct timeval tv;
-       struct timeval *rettv;
-       int slot;
-       int rethops;
-       int sndhops;
-       int progress = -1;
-       int broken_router;
+       struct timeval *sndtv;
+       int res, broken_router, slot, sndhops, recvttl, rethops;
 
-restart:
-       memset(&rcvbuf, -1, sizeof(rcvbuf));
+       memset(&rcvbuf, 0, sizeof(rcvbuf));
        iov.iov_base = &rcvbuf;
        iov.iov_len = sizeof(rcvbuf);
        msg.msg_name = (__u8*)&addr;
@@ -96,39 +146,44 @@ restart:
        gettimeofday(&tv, NULL);
        res = recvmsg(fd, &msg, MSG_ERRQUEUE);
        if (res < 0) {
-               if (errno == EAGAIN)
+               if (errno == EAGAIN || errno == EBADF || errno == ENOTSOCK 
+                   || errno == EINVAL || errno == ENOMEM)
                        return progress;
-               goto restart;
+               continue;
        }
 
-       progress = mtu;
+       progress = *mtu;
 
-       rethops = -1;
        sndhops = -1;
+       recvttl = -1;
+       rethops = -1;
+       sndtv = NULL;
        e = NULL;
-       rettv = NULL;
-       slot = ntohs(addr.sin_port) - base_port;
-       if (slot>=0 && slot < 63 && his[slot].hops) {
-               sndhops = his[slot].hops;
-               rettv = &his[slot].sendtime;
-               his[slot].hops = 0;
+       slot = his_slot(hptr, ntohs(addr.sin_port), his);
+       /* slot = ntohs(addr.sin_port) - base_port; */
+       if (slot>=0 && slot<his->size && his->e[slot].hdr.ttl) {
+               sndhops = his->e[slot].hdr.ttl;
+               sndtv = &his->e[slot].hdr.tv;
+               his->e[slot].hdr.ttl = 0;
        }
        broken_router = 0;
        if (res == sizeof(rcvbuf)) {
-               if (rcvbuf.ttl == 0 || rcvbuf.tv.tv_sec == 0) {
+               if (rcvbuf.ttl==0 || rcvbuf.ttl>maxttl 
+                   || rcvbuf.tv.tv_sec==0) {
                        broken_router = 1;
                } else {
                        sndhops = rcvbuf.ttl;
-                       rettv = &rcvbuf.tv;
-               }
+                       sndtv = &rcvbuf.tv;
        }
+       } else
+         broken_router = -1;
 
        for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
                if (cmsg->cmsg_level == SOL_IP) {
                        if (cmsg->cmsg_type == IP_RECVERR) {
                                e = (struct sock_extended_err *) 
CMSG_DATA(cmsg);
                        } else if (cmsg->cmsg_type == IP_TTL) {
-                               rethops = *(int*)CMSG_DATA(cmsg);
+                               recvttl = *(int*)CMSG_DATA(cmsg);
                        } else {
                                printf("cmsg:%d\n ", cmsg->cmsg_type);
                        }
@@ -139,35 +194,74 @@ restart:
                return 0;
        }
        if (e->ee_origin == SO_EE_ORIGIN_LOCAL) {
-               printf("%2d?: %-15s ", ttl, "[LOCALHOST]");
+         char lobuf[]="[LOCALHOST]";
+         if(no_resolve) {
+           printf("%2d?: %-15s", ttl, lobuf);
+         } else {
+           char abuf[128]="", fabuf[256];
+           struct sockaddr_in loin;
+           int lolen=sizeof(loin);
+
+           if(getsockname(fd, &loin, &lolen)<0) /* returns bind()'ed addr */
+             snprintf(abuf, sizeof(abuf), "getsockname: %s", strerror(errno));
+           else 
+             inet_ntop(loin.sin_family,&loin.sin_addr, abuf, sizeof(abuf));
+
+           snprintf(fabuf, sizeof(fabuf), "%s (%s)", lobuf, abuf);
+           printf("%2d?: %-52s", ttl, fabuf);
+         }
        } else if (e->ee_origin == SO_EE_ORIGIN_ICMP) {
-               char abuf[128];
+               char abuf[128]="";
                struct sockaddr_in *sin = (struct sockaddr_in*)(e+1);
 
-               inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
+               inet_ntop(sin->sin_family,&sin->sin_addr, abuf, sizeof(abuf));
 
                if (sndhops>0)
                        printf("%2d:  ", sndhops);
                else
-                       printf("%2d?: ", ttl);
+                       printf("%2d?:", ttl);
 
-               if(!no_resolve) {
+               if(no_resolve) {
+                       printf(" %-15s", abuf);
+               } else {
                        char fabuf[256];
                        struct hostent *h;
                        fflush(stdout);
-                       h = gethostbyaddr((char *) &sin->sin_addr, 
sizeof(sin->sin_addr), AF_INET);
+                       h = gethostbyaddr((char *) &sin->sin_addr, 
sizeof(sin->sin_addr), sin->sin_family);
                        snprintf(fabuf, sizeof(fabuf), "%s (%s)", h ? h->h_name 
: abuf, abuf);
-                       printf("%-52s ", fabuf);
-               } else {
-                       printf("%-15s ", abuf);
+                       printf(" %-52s", fabuf);
                }
        }
 
-       if (rettv) {
-               int diff = 
(tv.tv_sec-rettv->tv_sec)*1000000+(tv.tv_usec-rettv->tv_usec);
-               printf("%3d.%03dms ", diff/1000, diff%1000);
-               if (broken_router)
-                       printf("(This broken router returned corrupted payload) 
");
+       if (sndtv) {
+         int diff;
+         div_t diffms;
+
+         diff=(tv.tv_sec-sndtv->tv_sec)*1000000+(tv.tv_usec-sndtv->tv_usec);
+         diffms=div(diff, 1000);
+
+         printf(" %3d.%03dms", diffms.quot, diffms.rem);
+       }
+
+       if (recvttl>=0) {
+         /* direct: recvttl= 64 | 128 | 255 */
+         int i, ref[]={64, 128, 255, 0};
+         int snd;
+
+         for(i=0; ref[i] && recvttl>ref[i]; i++);
+         
+         rethops= ref[i] ? ref[i]+1-recvttl : -recvttl;
+         
+         snd= sndhops<0 ? ttl : sndhops;
+         if (snd != rethops)
+           printf(" asym %2d", rethops);
+       }
+
+       if(sndtv) {
+         if (broken_router>0)
+           printf(" (This broken router returned corrupted payload)");
+         else if(broken_router<0)
+           printf(" (%i)", res);
        }
 
        switch (e->ee_errno) {
@@ -175,192 +269,491 @@ restart:
                printf("\n");
                break;
        case EMSGSIZE:
-               printf("pmtu %d\n", e->ee_info);
-               mtu = e->ee_info;
-               progress = mtu;
+               printf(" pmtu %d\n", e->ee_info);
+               progress = *mtu = e->ee_info;
                break;
        case ECONNREFUSED:
-               printf("reached\n");
-               hops_to = sndhops<0 ? ttl : sndhops;
-               hops_from = rethops;
-               return 0;
+               printf(" reached\n");
+               hops->to = sndhops<0 ? ttl : sndhops;
+               hops->from = rethops;
+               progress=0;
+               break;
        case EPROTO:
-               printf("!P\n");
-               return 0;
+               printf(" !P\n");
+               progress=0;
+               break;
        case EHOSTUNREACH:
                if (e->ee_origin == SO_EE_ORIGIN_ICMP &&
-                   e->ee_type == 11 &&
-                   e->ee_code == 0) {
-                       if (rethops>=0) {
-                               if (rethops<=64)
-                                       rethops = 65-rethops;
-                               else if (rethops<=128)
-                                       rethops = 129-rethops;
-                               else
-                                       rethops = 256-rethops;
-                               if (sndhops>=0 && rethops != sndhops)
-                                       printf("asymm %2d ", rethops);
-                               else if (sndhops<0 && rethops != ttl)
-                                       printf("asymm %2d ", rethops);
-                       }
+                   e->ee_type == 11 &&  /* Time exceeded */
+                   e->ee_code == 0) {   /* TTL count exceeded  */
                        printf("\n");
-                       break;
+               } else {
+                 printf(" !H\n");
+                 progress=0;
                }
-               printf("!H\n");
-               return 0;
+               break;
        case ENETUNREACH:
-               printf("!N\n");
-               return 0;
+               printf(" !N\n");
+               progress=0;
+               break;
        case EACCES:
-               printf("!A\n");
-               return 0;
+               printf(" !A\n");
+               progress=0;
+               break;
        default:
                printf("\n");
                errno = e->ee_errno;
                perror("NET ERROR");
                return 0;
        }
-       goto restart;
+  }
+  return progress;
 }
 
-int probe_ttl(int fd, int ttl)
+/* Check for a replied probe.  The reported time is only correct if the target 
+ * host replied using the probed port e.g. dns at port 53. 
+ */
+int recv_reply(int hptr, int no_resolve, int mtu, struct hhistory *his) { 
+  int al= (mtu<1500 ? 1500 : mtu)-overhead;
+  char buf[al];
+  struct timeval tv;
+  struct sockaddr_in from;
+  int flen=sizeof(from), ttl, fd, cnt;
+
+  ttl=his->e[hptr].hdr.ttl;
+  fd=his->e[hptr].fd;
+
+  gettimeofday(&tv, NULL);
+  memset(&from, 0, sizeof(from));
+
+  cnt=recvfrom(fd, buf, sizeof(buf), MSG_DONTWAIT, &from, &flen);
+  if(cnt >= 0) {
+    char abuf[128]="", tbuf[16], xbuf[256];
+    unsigned port;
+    struct timeval *sndtv=NULL;
+    int slot, sndhops=-1;
+
+    port=(unsigned )ntohs(from.sin_port);
+    slot=his_slot(hptr, port, his);
+
+    if(slot>=0 && slot<his->size && his->e[slot].hdr.ttl) {
+      sndhops = his->e[slot].hdr.ttl;
+      sndtv = &his->e[slot].hdr.tv;
+      /* his->e[slot].hdr.ttl = 0; */
+      snprintf(tbuf,sizeof(tbuf), "%2d: ", sndhops);
+    } else {
+      snprintf(tbuf,sizeof(tbuf), "%2d?:", ttl);
+    }
+
+    inet_ntop(from.sin_family, &from.sin_addr, abuf, sizeof(abuf));
+    snprintf(xbuf,sizeof(xbuf), "reply received from %s/%u", abuf,port);
+
+    if(no_resolve)
+      printf("%s %-15s", tbuf, xbuf);
+    else 
+      printf("%s %-52s", tbuf, xbuf);
+
+    if (sndtv) {
+      int diff;
+      div_t diffms;
+      
+      diff=(tv.tv_sec-sndtv->tv_sec)*1000000+(tv.tv_usec-sndtv->tv_usec);
+      diffms=div(diff, 1000);
+      
+      printf(" %3d.%03dms", diffms.quot, diffms.rem);
+    }
+    
+    printf(" (%i)\n", cnt);
+  }
+
+  return cnt;
+}
+
+/* send a probe */
+int probe_ttl(struct sockaddr_in *target, int ttl, int no_resolve, int *mtu, 
struct hhistory *his, struct hopsinfo *hops)
 {
-       int i;
-       char sndbuf[mtu];
-       struct probehdr *hdr = (struct probehdr*)sndbuf;
+       int err=-1;
+       int nal= *mtu<sizeof(struct probehdr) ? sizeof(struct probehdr):*mtu;
+       char sndbuf[nal];
+       struct probehdr *hdr = (struct probehdr *)sndbuf;
+       struct helement *hele;
+       int hptr, hfd;
+       int i, j;
 
-       memset(sndbuf,0,mtu);
+       hptr=his->ptr;
+       hele=&his->e[hptr];
+       hfd=hele->fd;
+       target->sin_port= htons((__u16)hele->port);
+       memset(sndbuf, 0, nal);
+       hdr->ttl= ttl;
 
-restart:
-       for (i=0; i<10; i++) {
-               int res;
+       for (i=0; i<256; i++) {  /* max 256 local mtu changes supported */
+         int len= *mtu>overhead ? *mtu-overhead : 0;
+         int r;
 
-               hdr->ttl = ttl;
-               target.sin_port = htons(base_port + hisptr);
                gettimeofday(&hdr->tv, NULL);
-               his[hisptr].hops = ttl;
-               his[hisptr].sendtime = hdr->tv;
-               if (sendto(fd, sndbuf, mtu-overhead, 0, (struct 
sockaddr*)&target, sizeof(target)) > 0)
+         hele->hdr= *hdr;
+         if (sendto(hfd, sndbuf, len, 0, 
+                    (struct sockaddr*)target, sizeof(*target)) >= 0)
                        break;
-               res = recverr(fd, ttl);
-               his[hisptr].hops = 0;
-               if (res==0)
+
+         r = recv_err(hptr, no_resolve, mtu, his, hops);
+         hele->hdr.ttl = 0;
+         if (r==0)     /* all done */
                        return 0;
-               if (res > 0)
-                       goto restart;
+         if (r<0)      /* no error info in queue */
+           i+=25;
        }
-       hisptr = (hisptr + 1)&63;
-
-       if (i<10) {
-               data_wait(fd);
-               if (recv(fd, sndbuf, sizeof(sndbuf), MSG_DONTWAIT) > 0) {
-                       printf("%2d?: reply received 8)\n", ttl);
+       if(i>=256) { 
+         char abuf[128]="";
+         inet_ntop(target->sin_family, &target->sin_addr, abuf, sizeof(abuf));
+         printf("%2d:  send failed dest %s/%u\n", ttl, abuf, 
+                (unsigned )ntohs(target->sin_port));
                        return 0;
                }
-               return recverr(fd, ttl);
+
+       /* packet is sent */
+       his->ptr++;
+       if(his->ptr>=his->size) his->ptr=0;
+
+       data_wait(his);
+
+       /* perhaps cache gettimeofday() ? */
+       /* First check other sockets for late packets, then check hfd. */
+       i=his->ptr;
+       for(j=his->size; j; j--) {
+         int fd=his->e[i].fd;
+
+         if(fd!=hfd || i==hptr) {
+           int m, r;
+           m=*mtu;
+
+           r=recv_reply(i, no_resolve, m, his);
+           if(r>=0) 
+             err=0;
+
+           r=recv_err(i, no_resolve, mtu, his, hops);
+           if(r>0 && *mtu!=m) /* remote mtu change */
+             err=-2;            /* <0 retry */
+           else if(r>=0)
+             err=r;             /* =0 all done, >0 proceed */
        }
 
-       printf("%2d:  send failed\n", ttl);
-       return 0;
-}
+         i++;
+         if(i>=his->size) i=0;
+       }
 
-static void usage(void) __attribute((noreturn));
+       /* printf("**3 err=%i\n", err); */
+       return err;
+}
 
 static void usage(void)
 {
-       fprintf(stderr, "Usage: tracepath [-n] [-l <len>] 
<destination>[/<port>]\n");
-       exit(-1);
+       fprintf(stderr, "Use: tracepath [-I dev] [-Q tos] [-b src[/port]]"
+                       " [-m mtu] [-n] [-t ttli[/f]] dst[/port]\n");
+       return;
 }
 
-int
-main(int argc, char **argv)
-{
-       struct hostent *he;
-       int fd;
-       int on;
-       int ttl;
+int div_string(char *argin, long *valout) {
+  int err=0;
        char *p;
-       int ch;
 
-       while ((ch = getopt(argc, argv, "nh?l:")) != EOF) {
+  p=strrchr(argin, '/');
+  if(!p) p=strrchr(argin, ':');
+  if(!p) p=strrchr(argin, '#');
+
+  if(!p) {
+    err=1;  /* na */
+    goto end_error;
+  }
+  *p='\0';
+
+  if(p[1] == '\0') {
+    err=-1; /* eof */
+  } else {
+    char *pend;
+
+    *valout=strtol(&p[1], &pend, 0);
+    if(*pend != '\0')
+      err=-2; /* bad */
+  }
+
+end_error:
+  return err;
+}
+
+int main(int argc, char **argv)
+{
+       struct hopsinfo hops={ -1, -1};
+#define HISTSIZE 64
+       struct helement ebuf[HISTSIZE];
+       struct hhistory his={ 
+         .ptr=0, .size=HISTSIZE, .e=ebuf 
+       };
+#undef HISTSIZE
+       struct sockaddr_in source, target;
+       unsigned base_port=44444;
+       int base_fix=0;
+       char *bind_ini=NULL, *dev_ini=NULL;
+       char *mtu_ini=NULL, *ttl_ini=NULL, *tos_ini=NULL;
+       char dstname[262];
+       int mtu=65535;
+       int ttli=1, ttlf=31, ttl, tos=0;
+       int no_resolve=0;
+       int ch, res=0, err=0;
+
+       extern char *optarg;   
+       extern int optind;     /*, opterr, optopt; */
+
+       /* init history buffer */
+       { int i;
+         for(i=0; i<his.size; i++) {
+           struct helement *ele=&his.e[i];
+           ele->fd=-1;            /* fd used to send probe */
+           ele->port=0;           /* probe target port */
+           ele->hdr.ttl=0;
+         }
+       }
+
+       /** getopt **/
+       optarg=NULL;           /* init getopt */
+       optind=0;
+       while ((ch = getopt(argc, argv, "I:Q:b:l:m:ns:t:h")) != EOF) {
                switch(ch) {
-               case 'n':
-                       no_resolve = 1;
+               case 'I':
+                 dev_ini=optarg;
+                 break;
+               case 'Q':
+                 tos_ini=optarg;
+                 break;
+               case 'b':
+               case 's':
+                 bind_ini=optarg;
                        break;
                case 'l':
-                       if ((mtu = atoi(optarg)) <= overhead) {
-                               fprintf(stderr, "Error: length must be >= 
%d\n", overhead);
-                               exit(1);
-                       }
+               case 'm':
+                 mtu_ini=optarg;
+                 break;
+               case 'n':       
+                 no_resolve ^= 1;
+                 break;
+               case 't':
+                 ttl_ini=optarg;
                        break;
                default:
+                 err=254;
+               case 'h':  
                        usage();
+                 goto end_error;
                }
        }
 
        argc -= optind;
        argv += optind;
 
-       if (argc != 1)
+       if (argc != 1 && argc != 2) {
                usage();
+         err=255; goto end_error;
+       }
 
+       /** destination address/port **/
+       memset(&target, 0, sizeof(target));
+       target.sin_family = AF_INET;
+
+       { unsigned len=strlen(argv[0]);
+         if(len >= sizeof(dstname)) {
+           fprintf(stderr, "Error: bad destination host name"
+                   " length=%u>size=%u\n", len,sizeof(dstname));
+           err=1; goto end_error;
+         }
+         strcpy(dstname, argv[0]);
+       }
+
+       if(argc>1) {      /* old parameter format: address port */
+         char *pend;
+         long val;
+
+         val=strtol(argv[1], &pend, 0);
+         if(*pend != '\0') {
+           fprintf(stderr, "Error: bad destination port %li\n", val);
+           err=2; goto end_error;
+         }
+         base_port=(unsigned )val;
+       } else {          /* new format, switch to multiple source ports */
+         int ret;
+         long val;
+
+         ret=div_string(dstname, &val);
+         if(ret < 0) {
+           fprintf(stderr, "Error: bad destination port\n");
+           err=3; goto end_error;
+         } else if(ret == 0) {
+           base_port=(unsigned )val;
+           base_fix=1; /* fixed target port */
+         }
+       }
+
+       { struct hostent *he;
+         he = gethostbyname2(dstname, target.sin_family);
+         if(he == NULL) {
+           herror("gethostbyname2");
+           err=3; goto end_error;
+         }
+         memcpy(&target.sin_addr, he->h_addr, 4);
+       }
+
+       /** source address/port **/
+       memset(&source, 0, sizeof(source));
+       source.sin_family = AF_INET;
+       if(bind_ini) {
+         int ret, blen=strlen(bind_ini)+1;
+         char buf[blen];
+         long val;
+         __u16 sport=0;
+         struct hostent *she;
+
+         strcpy(buf, bind_ini);
+
+         ret=div_string(buf, &val);
+         if(ret < 0) {
+           fprintf(stderr, "Error: bad source port\n");
+           err=5; goto end_error;
+         } else if(ret == 0) {
+           sport = (unsigned )val;
+           base_fix=0;   /* must sweep target port: address already in use */
+         }
+         source.sin_port = htons(sport);
+
+         she = gethostbyname2(buf, source.sin_family);
+         if(she == NULL) {
+           herror("gethostbyname2");
+           err=6; goto end_error;
+         }
+         memcpy(&source.sin_addr, she->h_addr, 4);
+       }
+
+       /* initial mtu */
+       if(mtu_ini) {
+         char *pend;
+         long val;
+
+         val= strtol(mtu_ini, &pend, 0);
+         if(*pend != '\0') {
+           fprintf(stderr, "Error: bad mtu number %li\n", val);
+           err=7; goto end_error;
+         }
+         mtu=(int )val;
+         if(mtu<overhead) {
+           fprintf(stderr, "Error: bad mtu=%i<%i\n", mtu, overhead);
+           err=8; goto end_error;
+         }
+       }
+
+       /* ttl range */
+       if(ttl_ini) {
+         int ret, blen=strlen(ttl_ini)+1;
+         char buf[blen];
+         char *pend;
+         long val;
+
+         strcpy(buf, ttl_ini);
+
+         ret=div_string(buf, &val);
+         if(ret < 0) {
+           fprintf(stderr, "Error: bad ttlf\n");
+           err=9; goto end_error;
+         } else if(ret == 0)
+           ttlf=(int )val;
+
+         val=strtol(buf, &pend, 0);
+         if(*pend != '\0') {
+           fprintf(stderr, "Error: bad ttl numbers");
+           err=10; goto end_error;
+         }
+         ttli=(int )val;
+       }
+
+       /* tos */
+       if(tos_ini) {
+         char *pend;
+         long val;
+
+         val= strtol(tos_ini, &pend, 0);
+         if(*pend != '\0') {
+           fprintf(stderr, "Error: bad tos number");
+           err=11; goto end_error;
+         }
+         tos=(int )val;
+       }
+
+       /** create tot socket descriptors and set all target ports **/
+       { int i, tot= base_fix ? his.size : 1;
+         for(i=0; i<tot; i++) {
+           int fd, on;
 
        fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (fd < 0) {
                perror("socket");
-               exit(1);
+             err=20; goto end_error;
        }
-       target.sin_family = AF_INET;
-
-       p = strchr(argv[0], '/');
-       if (p) {
-               *p = 0;
-               base_port = atoi(p+1);
-       } else
-               base_port = 44444;
-       he = gethostbyname(argv[0]);
-       if (he == NULL) {
-               herror("gethostbyname");
-               exit(1);
+           if(dev_ini && setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, 
+                                    dev_ini, strlen(dev_ini)+1)) {
+             perror("SO_BINDTODEVICE");
+             err=21; goto end_error;
+           }
+           if(bind_ini 
+              && bind(fd, (struct sockaddr*)&source, sizeof(source))<0) {
+             perror("bind");
+             err=22; goto end_error;
        }
-       memcpy(&target.sin_addr, he->h_addr, 4);
 
        on = IP_PMTUDISC_PROBE;
        if (setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on)) &&
            (on = IP_PMTUDISC_DO,
             setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on)))) {
                perror("IP_MTU_DISCOVER");
-               exit(1);
+             err=23; goto end_error;
        }
+
        on = 1;
        if (setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) {
                perror("IP_RECVERR");
-               exit(1);
+             err=24; goto end_error;
        }
        if (setsockopt(fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) {
                perror("IP_RECVTTL");
-               exit(1);
+             err=25; goto end_error;
+           }
+           if (tos_ini && setsockopt(fd, SOL_IP, IP_TOS, &tos, sizeof(tos))) {
+             perror("IP_TOS");
+             err=26; goto end_error;
        }
 
-       for (ttl=1; ttl<32; ttl++) {
-               int res;
-               int i;
+           his.e[i].fd=fd;
+           his.e[i].port=base_port;
+         } /* for i<tot */
+
+         for(; i<his.size; i++) {
+           struct helement *ele=&his.e[i];
+           ele[0].fd=ele[-1].fd;
+           ele[0].port=ele[-1].port+1;
+         }
+       }
+
+       /** trace **/
+       for (ttl=ttli; ttl<=ttlf; ttl++) {
+               int on, i;
+               int fd=his.e[his.ptr].fd;
 
                on = ttl;
                if (setsockopt(fd, SOL_IP, IP_TTL, &on, sizeof(on))) {
                        perror("IP_TTL");
-                       exit(1);
+                       err=28; goto end_error;
                }
 
-restart:
                for (i=0; i<3; i++) {
-                       int old_mtu;
-
-                       old_mtu = mtu;
-                       res = probe_ttl(fd, ttl);
-                       if (mtu != old_mtu)
-                               goto restart;
-                       if (res == 0)
+                 res = probe_ttl(&target, ttl, no_resolve, &mtu, &his, &hops);
+                 if (res == 0) /* reached */
                                goto done;
                        if (res > 0)
                                break;
@@ -370,12 +763,25 @@ restart:
                        printf("%2d:  no reply\n", ttl);
        }
        printf("     Too many hops: pmtu %d\n", mtu);
+       err= res<0 ? 40-res : 39;
 done:
        printf("     Resume: pmtu %d ", mtu);
-       if (hops_to>=0)
-               printf("hops %d ", hops_to);
-       if (hops_from>=0)
-               printf("back %d ", hops_from);
+       if (hops.to>=0)
+               printf("hops %d ", hops.to);
+       if (hops.from>=0)
+               printf("back %d ", hops.from);
        printf("\n");
-       exit(0);
+end_error:
+       { int i, lastfd=-2;
+
+         for(i=0; i<his.size; i++) {
+           int fd=his.e[i].fd;
+           if(fd>=0 && fd!=lastfd) {
+             if(close(fd)<0)
+               fprintf(stderr, "Error close(%i): %s\n", fd, strerror(errno));
+             lastfd=fd;
+           }
+         }
+       }
+       return err;
 }



-- 
To UNSUBSCRIBE, email to [email protected]
with a subject of "unsubscribe". Trouble? Contact [email protected]

Reply via email to