Mercurial > hg > toybox
comparison toys/pending/traceroute.c @ 1075:565eba9b549e draft
traceroute from Ashwini Sharma
author | Rob Landley <rob@landley.net> |
---|---|
date | Sun, 22 Sep 2013 11:21:06 -0500 |
parents | |
children | e11684e3bbc5 |
comparison
equal
deleted
inserted
replaced
1074:82c5eb459a91 | 1075:565eba9b549e |
---|---|
1 /* traceroute - trace the route to "host". | |
2 * | |
3 * Copyright 2012 Madhur Verma <mad.flexi@gmail.com> | |
4 * Copyright 2013 Kyungwan Han <asura321@gmail.com> | |
5 * | |
6 * No Standard | |
7 | |
8 USE_TRACEROUTE(NEWTOY(traceroute, "<1>2f#<1>255=1z#<0>86400=0g*w#<0>86400=5t#<0>255=0s:q#<1>255=3p#<1>65535=33434m#<1>255=30rvndlIUF64", TOYFLAG_USR|TOYFLAG_BIN)) | |
9 | |
10 config TRACEROUTE | |
11 bool "traceroute" | |
12 default n | |
13 help | |
14 usage: traceroute [-FUIldnvr] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES] | |
15 [-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE] [-z PAUSE_MSEC] HOST [BYTES] | |
16 | |
17 Trace the route to HOST | |
18 | |
19 -F Set the don't fragment bit | |
20 -U Use UDP datagrams instead of ICMP ECHO | |
21 -I Use ICMP ECHO instead of UDP datagrams | |
22 -l Display the TTL value of the returned packet | |
23 -f Start from the 1ST_TTL hop (instead from 1)(RANGE 1 to 255) | |
24 -d Set SO_DEBUG options to socket | |
25 -n Print numeric addresses | |
26 -v verbose | |
27 -r Bypass routing tables, send directly to HOST | |
28 -m Max time-to-live (max number of hops)(RANGE 1 to 255) | |
29 -p Base UDP port number used in probes(default 33434)(RANGE 1 to 65535) | |
30 -q Number of probes per TTL (default 3)(RANGE 1 to 255) | |
31 -s IP address to use as the source address | |
32 -t Type-of-service in probe packets (default 0)(RANGE 0 to 255) | |
33 -w Time in seconds to wait for a response (default 3)(RANGE 0 to 86400) | |
34 -g Loose source route gateway (8 max) | |
35 -z Pause Time in milisec (default 0)(RANGE 0 to 86400) | |
36 */ | |
37 #define FOR_traceroute | |
38 #include "toys.h" | |
39 #include "toynet.h" | |
40 #include <netinet/udp.h> | |
41 #include <netinet/ip_icmp.h> | |
42 | |
43 GLOBALS( | |
44 long max_ttl; | |
45 long port; | |
46 long ttl_probes; | |
47 char *src_ip; | |
48 long tos; | |
49 long wait_time; | |
50 struct arg_list *loose_source; | |
51 long pause_time; | |
52 long first_ttl; | |
53 int recv_sock; | |
54 int snd_sock; | |
55 unsigned msg_len; | |
56 struct ip *packet; | |
57 uint32_t gw_list[9]; | |
58 uint32_t ident; | |
59 ) | |
60 | |
61 | |
62 #define ICMP_HD_SIZE 8 | |
63 #define send_icmp ((struct icmp *)(TT.packet + 1)) | |
64 #define send_udp ((struct udphdr *)(TT.packet + 1)) | |
65 | |
66 struct payload_s { | |
67 unsigned char seq; | |
68 unsigned char ttl; | |
69 struct timeval tv; | |
70 }; | |
71 | |
72 static struct payload_s *send_data; | |
73 static struct sockaddr_in dest, from; | |
74 | |
75 // Computes and returns checksum SUM of buffer P of length LEN | |
76 static u_int16_t in_cksum(u_int16_t *p, u_int len) | |
77 { | |
78 u_int32_t sum = 0; | |
79 int nwords = len >> 1; | |
80 | |
81 while (nwords-- != 0) sum += *p++; | |
82 if (len & 1) { | |
83 union { | |
84 u_int16_t w; | |
85 u_int8_t c[2]; | |
86 } u; | |
87 u.c[0] = *(u_char *) p; | |
88 u.c[1] = 0; | |
89 sum += u.w; | |
90 } | |
91 // end-around-carry | |
92 sum = (sum >> 16) + (sum & 0xffff); | |
93 sum += (sum >> 16); | |
94 return (~sum); | |
95 } | |
96 | |
97 /* | |
98 * sends a single probe packet with sequence SEQ and | |
99 * time-to-live TTL | |
100 */ | |
101 static void send_probe(int seq, int ttl) | |
102 { | |
103 int res, len; | |
104 void *out; | |
105 | |
106 if (toys.optflags & FLAG_U) { | |
107 send_data->seq = seq; | |
108 send_data->ttl = ttl; | |
109 dest.sin_port = TT.port + seq; | |
110 } else { | |
111 send_icmp->icmp_seq = htons(seq); | |
112 send_icmp->icmp_cksum = 0; | |
113 send_icmp->icmp_cksum = in_cksum((uint16_t *) send_icmp, TT.msg_len - sizeof(struct ip)); | |
114 if (send_icmp->icmp_cksum == 0) send_icmp->icmp_cksum = 0xffff; | |
115 } | |
116 | |
117 res = setsockopt(TT.snd_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); | |
118 if (res < 0) perror_exit("setsockopt ttl %d", ttl); | |
119 | |
120 if (toys.optflags & FLAG_U) { | |
121 out = send_data; | |
122 len = sizeof(struct payload_s); | |
123 } else { | |
124 out = send_icmp; | |
125 len = TT.msg_len - sizeof(struct ip); | |
126 } | |
127 res = sendto(TT.snd_sock, out, len, 0, (struct sockaddr *) &dest, sizeof(dest)); | |
128 if (res != len) perror_exit(" sendto"); | |
129 } | |
130 | |
131 static void resolve_addr(char *host, int type, int proto, struct sockaddr_in *sock) | |
132 { | |
133 struct addrinfo *rp, *info, hint; | |
134 int ret; | |
135 | |
136 memset(&hint, 0, sizeof(hint)); | |
137 hint.ai_family = AF_INET; | |
138 hint.ai_socktype = type; | |
139 hint.ai_protocol = proto; | |
140 | |
141 ret = getaddrinfo(host, NULL, &hint, &info); | |
142 if (ret || !info) perror_exit("bad address: %s ", host); | |
143 | |
144 for (rp = info; rp; rp = rp->ai_next) { | |
145 if (rp->ai_addrlen == sizeof(struct sockaddr_in)) { | |
146 memcpy(sock, rp->ai_addr, rp->ai_addrlen); | |
147 break; | |
148 } | |
149 } | |
150 freeaddrinfo(info); | |
151 if (!rp) perror_exit("Resolve failed "); | |
152 } | |
153 | |
154 static void do_trace() | |
155 { | |
156 int seq, fexit, ttl, tv = TT.wait_time * 1000; | |
157 struct pollfd pfd[1]; | |
158 | |
159 pfd[0].fd = TT.recv_sock; | |
160 pfd[0].events = POLLIN; | |
161 for (ttl = TT.first_ttl; ttl <= TT.max_ttl; ++ttl) { | |
162 int probe, dest_reach = 0; | |
163 struct timeval t1, t2; | |
164 struct sockaddr_in last; | |
165 | |
166 memset(&last, 0, sizeof(last)); | |
167 fexit = 0; | |
168 printf("%2d", ttl); | |
169 | |
170 for (probe = 0, seq = 0; probe < TT.ttl_probes; ++probe) { | |
171 int res = 0, tleft; | |
172 | |
173 fflush(NULL); | |
174 if (probe && (toys.optflags & FLAG_z)) usleep(TT.pause_time * 1000); | |
175 | |
176 send_probe(++seq, ttl); | |
177 gettimeofday(&t1, NULL); | |
178 t2 = t1; | |
179 | |
180 while ((tleft = (int)(tv - ((unsigned long long)(t2.tv_sec * 1000ULL | |
181 + t2.tv_usec/1000) - (unsigned long long)(t1.tv_sec * 1000ULL | |
182 + t1.tv_usec/1000)))) >= 0) { | |
183 if (!(res = poll(pfd, 1, tleft))) { | |
184 printf(" *"); | |
185 break; | |
186 } | |
187 gettimeofday(&t2, NULL); | |
188 if (res < 0) { | |
189 if (errno != EINTR) perror_exit("poll"); | |
190 continue; | |
191 } | |
192 | |
193 if (pfd[0].revents) { | |
194 unsigned addrlen = sizeof(from); | |
195 struct ip *rcv_pkt = (struct ip*) toybuf; | |
196 int rcv_len, pmtu = 0; | |
197 | |
198 rcv_len = recvfrom(TT.recv_sock, rcv_pkt, sizeof(toybuf), | |
199 MSG_DONTWAIT, (struct sockaddr *) &from, &addrlen); | |
200 if (rcv_len <= 0) continue; | |
201 else { | |
202 struct icmp *ricmp; | |
203 int icmp_res = 0; | |
204 | |
205 ricmp = (struct icmp *) ((void*)rcv_pkt + (rcv_pkt->ip_hl << 2)); | |
206 if (ricmp->icmp_code == ICMP_UNREACH_NEEDFRAG) | |
207 pmtu = ntohs(ricmp->icmp_nextmtu); | |
208 | |
209 if ((ricmp->icmp_type == ICMP_TIMXCEED | |
210 && ricmp->icmp_code == ICMP_TIMXCEED_INTRANS) | |
211 || ricmp->icmp_type == ICMP_UNREACH | |
212 || ricmp->icmp_type == ICMP_ECHOREPLY) { | |
213 | |
214 struct ip *hip; | |
215 struct udphdr *hudp; | |
216 struct icmp *hicmp; | |
217 | |
218 hip = &ricmp->icmp_ip; | |
219 if (toys.optflags & FLAG_U) { | |
220 hudp = (struct udphdr*) (hip + (hip->ip_hl << 2)); | |
221 if ((hip->ip_hl << 2) + 12 <= (rcv_len - (rcv_pkt->ip_hl << 2)) | |
222 && hip->ip_p == IPPROTO_UDP | |
223 && hudp->dest == htons(TT.port + seq)) | |
224 icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ?-1 : ricmp->icmp_code); | |
225 } else { | |
226 hicmp = (struct icmp *) ((void*)hip + (hip->ip_hl << 2)); | |
227 if (ricmp->icmp_type == ICMP_ECHOREPLY && ricmp->icmp_id == htons(TT.ident) | |
228 && ricmp->icmp_seq == htons(seq)) | |
229 icmp_res = ICMP_UNREACH_PORT; | |
230 | |
231 if ((hip->ip_hl << 2) + ICMP_HD_SIZE <= (rcv_len - (rcv_pkt->ip_hl << 2)) | |
232 && hip->ip_p == IPPROTO_ICMP | |
233 && hicmp->icmp_id == htons(TT.ident) | |
234 && hicmp->icmp_seq == htons(seq)) | |
235 icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ? -1 : ricmp->icmp_code); | |
236 } | |
237 } | |
238 if (!icmp_res) continue; | |
239 | |
240 if (addrlen > 0) { | |
241 unsigned delta = (t2.tv_sec * 1000000ULL + t2.tv_usec) | |
242 - (t1.tv_sec * 1000000ULL + t1.tv_usec); | |
243 | |
244 if (memcmp(&last.sin_addr, &from.sin_addr, sizeof(struct in_addr))) { | |
245 if (!(toys.optflags & FLAG_n)) { | |
246 char host[NI_MAXHOST]; | |
247 if (!getnameinfo((struct sockaddr *) &from, | |
248 sizeof(from), host, NI_MAXHOST, NULL, 0, 0)) | |
249 printf(" %s (", host); | |
250 else printf(" %s (", inet_ntoa(from.sin_addr)); | |
251 } | |
252 printf(" %s", inet_ntoa(from.sin_addr)); | |
253 if (!(toys.optflags & FLAG_n) ) printf(")"); | |
254 last = from; | |
255 } | |
256 printf(" %u.%03u ms", delta / 1000, delta % 1000); | |
257 if (toys.optflags & FLAG_l) printf(" (%d)", rcv_pkt->ip_ttl); | |
258 if (toys.optflags & FLAG_v) { | |
259 printf(" %d bytes from %s : icmp type %d code %d\t", | |
260 rcv_len, inet_ntoa(from.sin_addr), | |
261 ricmp->icmp_type, ricmp->icmp_code); | |
262 } | |
263 } else printf("\t!H"); | |
264 | |
265 switch (icmp_res) { | |
266 case ICMP_UNREACH_PORT: | |
267 if (rcv_pkt->ip_ttl <= 1) printf(" !"); | |
268 dest_reach = 1; | |
269 break; | |
270 case ICMP_UNREACH_NET: | |
271 printf(" !N"); | |
272 ++fexit; | |
273 break; | |
274 case ICMP_UNREACH_HOST: | |
275 printf(" !H"); | |
276 ++fexit; | |
277 break; | |
278 case ICMP_UNREACH_PROTOCOL: | |
279 printf(" !P"); | |
280 dest_reach = 1; | |
281 break; | |
282 case ICMP_UNREACH_NEEDFRAG: | |
283 printf(" !F-%d", pmtu); | |
284 ++fexit; | |
285 break; | |
286 case ICMP_UNREACH_SRCFAIL: | |
287 printf(" !S"); | |
288 ++fexit; | |
289 break; | |
290 case ICMP_UNREACH_FILTER_PROHIB: | |
291 case ICMP_UNREACH_NET_PROHIB: | |
292 printf(" !A"); | |
293 ++fexit; | |
294 break; | |
295 case ICMP_UNREACH_HOST_PROHIB: | |
296 printf(" !C"); | |
297 ++fexit; | |
298 break; | |
299 case ICMP_UNREACH_HOST_PRECEDENCE: | |
300 printf(" !V"); | |
301 ++fexit; | |
302 break; | |
303 case ICMP_UNREACH_PRECEDENCE_CUTOFF: | |
304 printf(" !C"); | |
305 ++fexit; | |
306 break; | |
307 case ICMP_UNREACH_NET_UNKNOWN: | |
308 case ICMP_UNREACH_HOST_UNKNOWN: | |
309 printf(" !U"); | |
310 ++fexit; | |
311 break; | |
312 case ICMP_UNREACH_ISOLATED: | |
313 printf(" !I"); | |
314 ++fexit; | |
315 break; | |
316 case ICMP_UNREACH_TOSNET: | |
317 case ICMP_UNREACH_TOSHOST: | |
318 printf(" !T"); | |
319 ++fexit; | |
320 break; | |
321 } | |
322 break; | |
323 } | |
324 } | |
325 } | |
326 } | |
327 xputc('\n'); | |
328 if (!memcmp(&from.sin_addr, &dest.sin_addr, sizeof(struct in_addr)) | |
329 || dest_reach || (fexit >= TT.ttl_probes -1)) | |
330 break; | |
331 } | |
332 | |
333 } | |
334 | |
335 void traceroute_main(void) | |
336 { | |
337 unsigned opt_len = 0, pack_size, tyser = 0; | |
338 int set = 1, lsrr = 0; | |
339 | |
340 if (toys.optflags & FLAG_g) { | |
341 struct arg_list *node; | |
342 | |
343 for (node = TT.loose_source; node; node = node->next, lsrr++) { | |
344 struct sockaddr_in sin; | |
345 | |
346 memset( &sin, 0, sizeof(sin)); | |
347 if (lsrr >= 8) error_exit("no more than 8 gateways"); // NGATEWAYS | |
348 resolve_addr(node->arg, SOCK_STREAM, 0, &sin); | |
349 TT.gw_list[lsrr] = sin.sin_addr.s_addr; | |
350 } | |
351 opt_len = (lsrr + 1) * sizeof(TT.gw_list[0]); | |
352 } | |
353 | |
354 pack_size = sizeof(struct ip) + opt_len; | |
355 pack_size += (toys.optflags & FLAG_U) ? sizeof(struct udphdr) | |
356 + sizeof(struct payload_s) : ICMP_HD_SIZE; | |
357 | |
358 if (toys.optargs[1]) | |
359 TT.msg_len = get_int_value(toys.optargs[1], pack_size, 32768);//max packet size | |
360 | |
361 TT.msg_len = (TT.msg_len < pack_size) ? pack_size : TT.msg_len; | |
362 TT.recv_sock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP); | |
363 if (toys.optflags & FLAG_d | |
364 && (setsockopt(TT.recv_sock, SOL_SOCKET, SO_DEBUG, &set, sizeof(set)) < 0)) | |
365 perror_exit("SO_DEBUG failed "); | |
366 if (toys.optflags & FLAG_r | |
367 && (setsockopt(TT.recv_sock, SOL_SOCKET, SO_DONTROUTE, &set, sizeof(set)) < 0)) | |
368 perror_exit("SO_DONTROUTE failed "); | |
369 | |
370 if (toys.optflags & FLAG_U) TT.snd_sock = xsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
371 else TT.snd_sock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP); | |
372 | |
373 resolve_addr(toys.optargs[0], ((toys.optflags & FLAG_U) ? SOCK_DGRAM : SOCK_RAW), | |
374 ((toys.optflags & FLAG_U) ? IPPROTO_UDP : IPPROTO_ICMP), &dest); | |
375 | |
376 if (lsrr > 0) { | |
377 unsigned char optlist[MAX_IPOPTLEN]; | |
378 unsigned size; | |
379 | |
380 TT.gw_list[lsrr] = dest.sin_addr.s_addr; | |
381 ++lsrr; | |
382 | |
383 optlist[0] = IPOPT_NOP; | |
384 optlist[1] = IPOPT_LSRR;// loose source route option | |
385 size = lsrr * sizeof(TT.gw_list[0]); | |
386 optlist[2] = size + 3; | |
387 optlist[3] = IPOPT_MINOFF; | |
388 memcpy(optlist + 4, TT.gw_list, size); | |
389 | |
390 if (setsockopt(TT.snd_sock, IPPROTO_IP, IP_OPTIONS, | |
391 (char *)optlist, size + sizeof(TT.gw_list[0])) < 0) | |
392 perror_exit("LSRR IP_OPTIONS"); | |
393 } | |
394 | |
395 if (setsockopt(TT.snd_sock, SOL_SOCKET, SO_SNDBUF, &TT.msg_len, sizeof(TT.msg_len)) < 0) | |
396 perror_exit("SO_SNDBUF failed "); | |
397 | |
398 if ((toys.optflags & FLAG_t) && | |
399 setsockopt(TT.snd_sock, IPPROTO_IP, IP_TOS, &tyser, sizeof(tyser)) < 0) | |
400 perror_exit("IP_TOS %d failed ", TT.tos); | |
401 | |
402 #ifdef IP_DONTFRAG | |
403 if ((toys.optflags & FLAG_F) && | |
404 (setsockopt(TT.snd_sock, IPPROTO_IP, IP_DONTFRAG, &set, sizeof(set)) < 0)) | |
405 perror_exit("IP_DONTFRAG failed "); | |
406 #endif | |
407 | |
408 if ((toys.optflags & FLAG_d) | |
409 && (setsockopt(TT.snd_sock, SOL_SOCKET, SO_DEBUG, &set, sizeof(set)) < 0)) | |
410 perror_exit("SO_DEBUG failed "); | |
411 if ((toys.optflags & FLAG_r) && | |
412 (setsockopt(TT.snd_sock, SOL_SOCKET, SO_DONTROUTE, &set, sizeof(set)) < 0)) | |
413 perror_exit("SO_DONTROUTE failed "); | |
414 | |
415 | |
416 TT.packet = xmalloc(TT.msg_len); | |
417 TT.ident = getpid(); | |
418 | |
419 if (toys.optflags & FLAG_U) send_data = (struct payload_s *) (send_udp + 1); | |
420 else { | |
421 TT.ident |= 0x8000; | |
422 send_icmp->icmp_type = ICMP_ECHO; | |
423 send_icmp->icmp_id = htons(TT.ident); | |
424 } | |
425 if (toys.optflags & FLAG_s) { | |
426 struct sockaddr_in *source = xzalloc(sizeof(struct sockaddr_in)); | |
427 inet_aton(TT.src_ip, &source->sin_addr); | |
428 if (setsockopt(TT.snd_sock, IPPROTO_IP, IP_MULTICAST_IF, | |
429 (struct sockaddr*)source, sizeof(*source))) | |
430 perror_exit("can't set multicast source interface"); | |
431 if (bind(TT.snd_sock,(struct sockaddr*)source, sizeof(*source))) | |
432 perror_exit("bind"); | |
433 free(source); | |
434 } | |
435 | |
436 if(TT.first_ttl > TT.max_ttl) | |
437 error_exit("ERROR :Range for -f is 1 to %d (max ttl)", TT.max_ttl); | |
438 | |
439 printf("traceroute to %s(%s)", toys.optargs[0], inet_ntoa(dest.sin_addr)); | |
440 if (toys.optflags & FLAG_s) printf(" from %s", TT.src_ip); | |
441 printf(", %ld hops max, %u byte packets\n", TT.max_ttl, TT.msg_len); | |
442 | |
443 do_trace(); | |
444 } |