1/* 2 * tracepath.c 3 * 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. 8 * 9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 10 */ 11 12#include <stdio.h> 13#include <stdlib.h> 14#include <unistd.h> 15#include <sys/socket.h> 16#include <linux/errqueue.h> 17#include <errno.h> 18#include <string.h> 19#include <netdb.h> 20#include <netinet/in.h> 21#include <resolv.h> 22#include <sys/time.h> 23#include <sys/uio.h> 24#include <arpa/inet.h> 25 26struct hhistory 27{ 28 int hops; 29 struct timeval sendtime; 30}; 31 32struct hhistory his[64]; 33int hisptr; 34 35struct sockaddr_in target; 36__u16 base_port; 37 38const int overhead = 28; 39int mtu = 65535; 40int hops_to = -1; 41int hops_from = -1; 42int no_resolve = 0; 43 44struct probehdr 45{ 46 __u32 ttl; 47 struct timeval tv; 48}; 49 50void data_wait(int fd) 51{ 52 fd_set fds; 53 struct timeval tv; 54 FD_ZERO(&fds); 55 FD_SET(fd, &fds); 56 tv.tv_sec = 1; 57 tv.tv_usec = 0; 58 select(fd+1, &fds, NULL, NULL, &tv); 59} 60 61int recverr(int fd, int ttl) 62{ 63 int res; 64 struct probehdr rcvbuf; 65 char cbuf[512]; 66 struct iovec iov; 67 struct msghdr msg; 68 struct cmsghdr *cmsg; 69 struct sock_extended_err *e; 70 struct sockaddr_in addr; 71 struct timeval tv; 72 struct timeval *rettv; 73 int slot; 74 int rethops; 75 int sndhops; 76 int progress = -1; 77 int broken_router; 78 79restart: 80 memset(&rcvbuf, -1, sizeof(rcvbuf)); 81 iov.iov_base = &rcvbuf; 82 iov.iov_len = sizeof(rcvbuf); 83 msg.msg_name = (__u8*)&addr; 84 msg.msg_namelen = sizeof(addr); 85 msg.msg_iov = &iov; 86 msg.msg_iovlen = 1; 87 msg.msg_flags = 0; 88 msg.msg_control = cbuf; 89 msg.msg_controllen = sizeof(cbuf); 90 91 gettimeofday(&tv, NULL); 92 res = recvmsg(fd, &msg, MSG_ERRQUEUE); 93 if (res < 0) { 94 if (errno == EAGAIN) 95 return progress; 96 goto restart; 97 } 98 99 progress = mtu; 100 101 rethops = -1; 102 sndhops = -1; 103 e = NULL; 104 rettv = NULL; 105 slot = ntohs(addr.sin_port) - base_port; 106 if (slot>=0 && slot < 63 && his[slot].hops) { 107 sndhops = his[slot].hops; 108 rettv = &his[slot].sendtime; 109 his[slot].hops = 0; 110 } 111 broken_router = 0; 112 if (res == sizeof(rcvbuf)) { 113 if (rcvbuf.ttl == 0 || rcvbuf.tv.tv_sec == 0) { 114 broken_router = 1; 115 } else { 116 sndhops = rcvbuf.ttl; 117 rettv = &rcvbuf.tv; 118 } 119 } 120 121 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 122 if (cmsg->cmsg_level == SOL_IP) { 123 if (cmsg->cmsg_type == IP_RECVERR) { 124 e = (struct sock_extended_err *) CMSG_DATA(cmsg); 125 } else if (cmsg->cmsg_type == IP_TTL) { 126 rethops = *(int*)CMSG_DATA(cmsg); 127 } else { 128 printf("cmsg:%d\n ", cmsg->cmsg_type); 129 } 130 } 131 } 132 if (e == NULL) { 133 printf("no info\n"); 134 return 0; 135 } 136 if (e->ee_origin == SO_EE_ORIGIN_LOCAL) { 137 printf("%2d?: %-15s ", ttl, "[LOCALHOST]"); 138 } else if (e->ee_origin == SO_EE_ORIGIN_ICMP) { 139 char abuf[128]; 140 struct sockaddr_in *sin = (struct sockaddr_in*)(e+1); 141 142 inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf)); 143 144 if (sndhops>0) 145 printf("%2d: ", sndhops); 146 else 147 printf("%2d?: ", ttl); 148 149 if(!no_resolve) { 150 char fabuf[256]; 151 struct hostent *h; 152 fflush(stdout); 153 h = gethostbyaddr((char *) &sin->sin_addr, sizeof(sin->sin_addr), AF_INET); 154 snprintf(fabuf, sizeof(fabuf), "%s (%s)", h ? h->h_name : abuf, abuf); 155 printf("%-52s ", fabuf); 156 } else { 157 printf("%-15s ", abuf); 158 } 159 } 160 161 if (rethops>=0) { 162 if (rethops<=64) 163 rethops = 65-rethops; 164 else if (rethops<=128) 165 rethops = 129-rethops; 166 else 167 rethops = 256-rethops; 168 if (sndhops>=0 && rethops != sndhops) 169 printf("asymm %2d ", rethops); 170 else if (sndhops<0 && rethops != ttl) 171 printf("asymm %2d ", rethops); 172 } 173 174 if (rettv) { 175 int diff = (tv.tv_sec-rettv->tv_sec)*1000000+(tv.tv_usec-rettv->tv_usec); 176 printf("%3d.%03dms ", diff/1000, diff%1000); 177 if (broken_router) 178 printf("(This broken router returned corrupted payload) "); 179 } 180 181 switch (e->ee_errno) { 182 case ETIMEDOUT: 183 printf("\n"); 184 break; 185 case EMSGSIZE: 186 printf("pmtu %d\n", e->ee_info); 187 mtu = e->ee_info; 188 progress = mtu; 189 break; 190 case ECONNREFUSED: 191 printf("reached\n"); 192 hops_to = sndhops<0 ? ttl : sndhops; 193 hops_from = rethops; 194 return 0; 195 case EPROTO: 196 printf("!P\n"); 197 return 0; 198 case EHOSTUNREACH: 199 if (e->ee_origin == SO_EE_ORIGIN_ICMP && 200 e->ee_type == 11 && 201 e->ee_code == 0) { 202 printf("\n"); 203 break; 204 } 205 printf("!H\n"); 206 return 0; 207 case ENETUNREACH: 208 printf("!N\n"); 209 return 0; 210 case EACCES: 211 printf("!A\n"); 212 return 0; 213 default: 214 printf("\n"); 215 errno = e->ee_errno; 216 perror("NET ERROR"); 217 return 0; 218 } 219 goto restart; 220} 221 222int probe_ttl(int fd, int ttl) 223{ 224 int i; 225 char sndbuf[mtu]; 226 struct probehdr *hdr = (struct probehdr*)sndbuf; 227 228 memset(sndbuf,0,mtu); 229 230restart: 231 for (i=0; i<10; i++) { 232 int res; 233 234 hdr->ttl = ttl; 235 target.sin_port = htons(base_port + hisptr); 236 gettimeofday(&hdr->tv, NULL); 237 his[hisptr].hops = ttl; 238 his[hisptr].sendtime = hdr->tv; 239 if (sendto(fd, sndbuf, mtu-overhead, 0, (struct sockaddr*)&target, sizeof(target)) > 0) 240 break; 241 res = recverr(fd, ttl); 242 his[hisptr].hops = 0; 243 if (res==0) 244 return 0; 245 if (res > 0) 246 goto restart; 247 } 248 hisptr = (hisptr + 1)&63; 249 250 if (i<10) { 251 data_wait(fd); 252 if (recv(fd, sndbuf, sizeof(sndbuf), MSG_DONTWAIT) > 0) { 253 printf("%2d?: reply received 8)\n", ttl); 254 return 0; 255 } 256 return recverr(fd, ttl); 257 } 258 259 printf("%2d: send failed\n", ttl); 260 return 0; 261} 262 263static void usage(void) __attribute((noreturn)); 264 265static void usage(void) 266{ 267 fprintf(stderr, "Usage: tracepath [-n] <destination>[/<port>]\n"); 268 exit(-1); 269} 270 271int 272main(int argc, char **argv) 273{ 274 struct hostent *he; 275 int fd; 276 int on; 277 int ttl; 278 char *p; 279 int ch; 280 281 while ((ch = getopt(argc, argv, "nh?")) != EOF) { 282 switch(ch) { 283 case 'n': 284 no_resolve = 1; 285 break; 286 default: ; 287 } 288 } 289 290 argc -= optind; 291 argv += optind; 292 293 if (argc != 1) 294 usage(); 295 296 297 fd = socket(AF_INET, SOCK_DGRAM, 0); 298 if (fd < 0) { 299 perror("socket"); 300 exit(1); 301 } 302 target.sin_family = AF_INET; 303 304 p = strchr(argv[0], '/'); 305 if (p) { 306 *p = 0; 307 base_port = atoi(p+1); 308 } else 309 base_port = 44444; 310 he = gethostbyname(argv[0]); 311 if (he == NULL) { 312 herror("gethostbyname"); 313 exit(1); 314 } 315 memcpy(&target.sin_addr, he->h_addr, 4); 316 317 on = IP_PMTUDISC_DO; 318 if (setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on))) { 319 perror("IP_MTU_DISCOVER"); 320 exit(1); 321 } 322 on = 1; 323 if (setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) { 324 perror("IP_RECVERR"); 325 exit(1); 326 } 327 if (setsockopt(fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) { 328 perror("IP_RECVTTL"); 329 exit(1); 330 } 331 332 for (ttl=1; ttl<32; ttl++) { 333 int res; 334 int i; 335 336 on = ttl; 337 if (setsockopt(fd, SOL_IP, IP_TTL, &on, sizeof(on))) { 338 perror("IP_TTL"); 339 exit(1); 340 } 341 342 for (i=0; i<3; i++) { 343 res = probe_ttl(fd, ttl); 344 if (res == 0) 345 goto done; 346 if (res > 0) 347 break; 348 } 349 350 if (res < 0) 351 printf("%2d: no reply\n", ttl); 352 } 353 printf(" Too many hops: pmtu %d\n", mtu); 354done: 355 printf(" Resume: pmtu %d ", mtu); 356 if (hops_to>=0) 357 printf("hops %d ", hops_to); 358 if (hops_from>=0) 359 printf("back %d ", hops_from); 360 printf("\n"); 361 exit(0); 362} 363