4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
15 #include <sys/socket.h>
16 #include <linux/types.h>
17 #include <linux/errqueue.h>
21 #include <netinet/in.h>
25 #include <arpa/inet.h>
27 #ifndef IP_PMTUDISC_PROBE
28 #define IP_PMTUDISC_PROBE 3
34 struct timeval sendtime;
37 struct hhistory his[64];
40 struct sockaddr_in target;
43 const int overhead = 28;
50 #define HOST_COLUMN_SIZE 52
58 void data_wait(int fd)
66 select(fd+1, &fds, NULL, NULL, &tv);
69 void print_host(const char *a, const char *b, int both)
76 plen += strlen(b) + 3;
78 if (plen >= HOST_COLUMN_SIZE)
79 plen = HOST_COLUMN_SIZE - 1;
80 printf("%*s", HOST_COLUMN_SIZE - plen, "");
83 int recverr(int fd, int ttl)
86 struct probehdr rcvbuf;
91 struct sock_extended_err *e;
92 struct sockaddr_in addr;
94 struct timeval *rettv;
102 memset(&rcvbuf, -1, sizeof(rcvbuf));
103 iov.iov_base = &rcvbuf;
104 iov.iov_len = sizeof(rcvbuf);
105 msg.msg_name = (__u8*)&addr;
106 msg.msg_namelen = sizeof(addr);
110 msg.msg_control = cbuf;
111 msg.msg_controllen = sizeof(cbuf);
113 gettimeofday(&tv, NULL);
114 res = recvmsg(fd, &msg, MSG_ERRQUEUE);
127 slot = ntohs(addr.sin_port) - base_port;
128 if (slot>=0 && slot < 63 && his[slot].hops) {
129 sndhops = his[slot].hops;
130 rettv = &his[slot].sendtime;
134 if (res == sizeof(rcvbuf)) {
135 if (rcvbuf.ttl == 0 || rcvbuf.tv.tv_sec == 0) {
138 sndhops = rcvbuf.ttl;
143 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
144 if (cmsg->cmsg_level == SOL_IP) {
145 if (cmsg->cmsg_type == IP_RECVERR) {
146 e = (struct sock_extended_err *) CMSG_DATA(cmsg);
147 } else if (cmsg->cmsg_type == IP_TTL) {
148 rethops = *(int*)CMSG_DATA(cmsg);
150 printf("cmsg:%d\n ", cmsg->cmsg_type);
158 if (e->ee_origin == SO_EE_ORIGIN_LOCAL) {
159 printf("%2d?: %*s ", ttl, -(HOST_COLUMN_SIZE - 1), "[LOCALHOST]");
160 } else if (e->ee_origin == SO_EE_ORIGIN_ICMP) {
162 struct sockaddr_in *sin = (struct sockaddr_in*)(e+1);
163 struct hostent *h = NULL;
165 inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
168 printf("%2d: ", sndhops);
170 printf("%2d?: ", ttl);
172 if (!no_resolve || show_both) {
174 h = gethostbyaddr((char *) &sin->sin_addr, sizeof(sin->sin_addr), AF_INET);
178 print_host(abuf, h ? h->h_name : abuf, show_both);
180 print_host(h ? h->h_name : abuf, abuf, show_both);
184 int diff = (tv.tv_sec-rettv->tv_sec)*1000000+(tv.tv_usec-rettv->tv_usec);
185 printf("%3d.%03dms ", diff/1000, diff%1000);
187 printf("(This broken router returned corrupted payload) ");
190 switch (e->ee_errno) {
195 printf("pmtu %d\n", e->ee_info);
201 hops_to = sndhops<0 ? ttl : sndhops;
208 if (e->ee_origin == SO_EE_ORIGIN_ICMP &&
213 rethops = 65-rethops;
214 else if (rethops<=128)
215 rethops = 129-rethops;
217 rethops = 256-rethops;
218 if (sndhops>=0 && rethops != sndhops)
219 printf("asymm %2d ", rethops);
220 else if (sndhops<0 && rethops != ttl)
221 printf("asymm %2d ", rethops);
243 int probe_ttl(int fd, int ttl)
247 struct probehdr *hdr = (struct probehdr*)sndbuf;
249 memset(sndbuf,0,mtu);
252 for (i=0; i<10; i++) {
256 target.sin_port = htons(base_port + hisptr);
257 gettimeofday(&hdr->tv, NULL);
258 his[hisptr].hops = ttl;
259 his[hisptr].sendtime = hdr->tv;
260 if (sendto(fd, sndbuf, mtu-overhead, 0, (struct sockaddr*)&target, sizeof(target)) > 0)
262 res = recverr(fd, ttl);
263 his[hisptr].hops = 0;
269 hisptr = (hisptr + 1)&63;
273 if (recv(fd, sndbuf, sizeof(sndbuf), MSG_DONTWAIT) > 0) {
274 printf("%2d?: reply received 8)\n", ttl);
277 return recverr(fd, ttl);
280 printf("%2d: send failed\n", ttl);
284 static void usage(void) __attribute((noreturn));
286 static void usage(void)
288 fprintf(stderr, "Usage: tracepath [-n] [-b] [-l <len>] <destination>[/<port>]\n");
293 main(int argc, char **argv)
302 while ((ch = getopt(argc, argv, "nbh?l:")) != EOF) {
311 if ((mtu = atoi(optarg)) <= overhead) {
312 fprintf(stderr, "Error: length must be >= %d\n", overhead);
328 fd = socket(AF_INET, SOCK_DGRAM, 0);
333 target.sin_family = AF_INET;
335 p = strchr(argv[0], '/');
338 base_port = atoi(p+1);
341 he = gethostbyname(argv[0]);
343 herror("gethostbyname");
346 memcpy(&target.sin_addr, he->h_addr, 4);
348 on = IP_PMTUDISC_PROBE;
349 if (setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on)) &&
350 (on = IP_PMTUDISC_DO,
351 setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on)))) {
352 perror("IP_MTU_DISCOVER");
356 if (setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) {
357 perror("IP_RECVERR");
360 if (setsockopt(fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) {
361 perror("IP_RECVTTL");
365 for (ttl=1; ttl<32; ttl++) {
370 if (setsockopt(fd, SOL_IP, IP_TTL, &on, sizeof(on))) {
376 for (i=0; i<3; i++) {
380 res = probe_ttl(fd, ttl);
390 printf("%2d: no reply\n", ttl);
392 printf(" Too many hops: pmtu %d\n", mtu);
394 printf(" Resume: pmtu %d ", mtu);
396 printf("hops %d ", hops_to);
398 printf("back %d ", hops_from);