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]