Tue 3 Mar 2009 15:28:44 +0100 (CET) --- 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; isize; i++) { + int fd=his->e[i].fd; + + if(fd>=0 && fd!=lastfd) { + lastfd=fd; FD_SET(fd, &fds); + } + if(maxfd0 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 && slotsize && 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 && slotsize && 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= *mtuptr; + 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 ] [/]\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; ifd=-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(mtuh_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 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=0 && fd!=lastfd) { + if(close(fd)<0) + fprintf(stderr, "Error close(%i): %s\n", fd, strerror(errno)); + lastfd=fd; + } + } + } + return err; }