diff options
-rw-r--r-- | toys/pending/traceroute.c | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/toys/pending/traceroute.c b/toys/pending/traceroute.c new file mode 100644 index 00000000..e52a7880 --- /dev/null +++ b/toys/pending/traceroute.c @@ -0,0 +1,444 @@ +/* traceroute - trace the route to "host". + * + * Copyright 2012 Madhur Verma <mad.flexi@gmail.com> + * Copyright 2013 Kyungwan Han <asura321@gmail.com> + * + * No Standard + +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)) + +config TRACEROUTE + bool "traceroute" + default n + help + usage: traceroute [-FUIldnvr] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES] + [-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE] [-z PAUSE_MSEC] HOST [BYTES] + + Trace the route to HOST + + -F Set the don't fragment bit + -U Use UDP datagrams instead of ICMP ECHO + -I Use ICMP ECHO instead of UDP datagrams + -l Display the TTL value of the returned packet + -f Start from the 1ST_TTL hop (instead from 1)(RANGE 1 to 255) + -d Set SO_DEBUG options to socket + -n Print numeric addresses + -v verbose + -r Bypass routing tables, send directly to HOST + -m Max time-to-live (max number of hops)(RANGE 1 to 255) + -p Base UDP port number used in probes(default 33434)(RANGE 1 to 65535) + -q Number of probes per TTL (default 3)(RANGE 1 to 255) + -s IP address to use as the source address + -t Type-of-service in probe packets (default 0)(RANGE 0 to 255) + -w Time in seconds to wait for a response (default 3)(RANGE 0 to 86400) + -g Loose source route gateway (8 max) + -z Pause Time in milisec (default 0)(RANGE 0 to 86400) +*/ +#define FOR_traceroute +#include "toys.h" +#include "toynet.h" +#include <netinet/udp.h> +#include <netinet/ip_icmp.h> + +GLOBALS( + long max_ttl; + long port; + long ttl_probes; + char *src_ip; + long tos; + long wait_time; + struct arg_list *loose_source; + long pause_time; + long first_ttl; + int recv_sock; + int snd_sock; + unsigned msg_len; + struct ip *packet; + uint32_t gw_list[9]; + uint32_t ident; +) + + +#define ICMP_HD_SIZE 8 +#define send_icmp ((struct icmp *)(TT.packet + 1)) +#define send_udp ((struct udphdr *)(TT.packet + 1)) + +struct payload_s { + unsigned char seq; + unsigned char ttl; + struct timeval tv; +}; + +static struct payload_s *send_data; +static struct sockaddr_in dest, from; + +// Computes and returns checksum SUM of buffer P of length LEN +static u_int16_t in_cksum(u_int16_t *p, u_int len) +{ + u_int32_t sum = 0; + int nwords = len >> 1; + + while (nwords-- != 0) sum += *p++; + if (len & 1) { + union { + u_int16_t w; + u_int8_t c[2]; + } u; + u.c[0] = *(u_char *) p; + u.c[1] = 0; + sum += u.w; + } + // end-around-carry + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return (~sum); +} + +/* + * sends a single probe packet with sequence SEQ and + * time-to-live TTL + */ +static void send_probe(int seq, int ttl) +{ + int res, len; + void *out; + + if (toys.optflags & FLAG_U) { + send_data->seq = seq; + send_data->ttl = ttl; + dest.sin_port = TT.port + seq; + } else { + send_icmp->icmp_seq = htons(seq); + send_icmp->icmp_cksum = 0; + send_icmp->icmp_cksum = in_cksum((uint16_t *) send_icmp, TT.msg_len - sizeof(struct ip)); + if (send_icmp->icmp_cksum == 0) send_icmp->icmp_cksum = 0xffff; + } + + res = setsockopt(TT.snd_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); + if (res < 0) perror_exit("setsockopt ttl %d", ttl); + + if (toys.optflags & FLAG_U) { + out = send_data; + len = sizeof(struct payload_s); + } else { + out = send_icmp; + len = TT.msg_len - sizeof(struct ip); + } + res = sendto(TT.snd_sock, out, len, 0, (struct sockaddr *) &dest, sizeof(dest)); + if (res != len) perror_exit(" sendto"); +} + +static void resolve_addr(char *host, int type, int proto, struct sockaddr_in *sock) +{ + struct addrinfo *rp, *info, hint; + int ret; + + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_INET; + hint.ai_socktype = type; + hint.ai_protocol = proto; + + ret = getaddrinfo(host, NULL, &hint, &info); + if (ret || !info) perror_exit("bad address: %s ", host); + + for (rp = info; rp; rp = rp->ai_next) { + if (rp->ai_addrlen == sizeof(struct sockaddr_in)) { + memcpy(sock, rp->ai_addr, rp->ai_addrlen); + break; + } + } + freeaddrinfo(info); + if (!rp) perror_exit("Resolve failed "); +} + +static void do_trace() +{ + int seq, fexit, ttl, tv = TT.wait_time * 1000; + struct pollfd pfd[1]; + + pfd[0].fd = TT.recv_sock; + pfd[0].events = POLLIN; + for (ttl = TT.first_ttl; ttl <= TT.max_ttl; ++ttl) { + int probe, dest_reach = 0; + struct timeval t1, t2; + struct sockaddr_in last; + + memset(&last, 0, sizeof(last)); + fexit = 0; + printf("%2d", ttl); + + for (probe = 0, seq = 0; probe < TT.ttl_probes; ++probe) { + int res = 0, tleft; + + fflush(NULL); + if (probe && (toys.optflags & FLAG_z)) usleep(TT.pause_time * 1000); + + send_probe(++seq, ttl); + gettimeofday(&t1, NULL); + t2 = t1; + + while ((tleft = (int)(tv - ((unsigned long long)(t2.tv_sec * 1000ULL + + t2.tv_usec/1000) - (unsigned long long)(t1.tv_sec * 1000ULL + + t1.tv_usec/1000)))) >= 0) { + if (!(res = poll(pfd, 1, tleft))) { + printf(" *"); + break; + } + gettimeofday(&t2, NULL); + if (res < 0) { + if (errno != EINTR) perror_exit("poll"); + continue; + } + + if (pfd[0].revents) { + unsigned addrlen = sizeof(from); + struct ip *rcv_pkt = (struct ip*) toybuf; + int rcv_len, pmtu = 0; + + rcv_len = recvfrom(TT.recv_sock, rcv_pkt, sizeof(toybuf), + MSG_DONTWAIT, (struct sockaddr *) &from, &addrlen); + if (rcv_len <= 0) continue; + else { + struct icmp *ricmp; + int icmp_res = 0; + + ricmp = (struct icmp *) ((void*)rcv_pkt + (rcv_pkt->ip_hl << 2)); + if (ricmp->icmp_code == ICMP_UNREACH_NEEDFRAG) + pmtu = ntohs(ricmp->icmp_nextmtu); + + if ((ricmp->icmp_type == ICMP_TIMXCEED + && ricmp->icmp_code == ICMP_TIMXCEED_INTRANS) + || ricmp->icmp_type == ICMP_UNREACH + || ricmp->icmp_type == ICMP_ECHOREPLY) { + + struct ip *hip; + struct udphdr *hudp; + struct icmp *hicmp; + + hip = &ricmp->icmp_ip; + if (toys.optflags & FLAG_U) { + hudp = (struct udphdr*) (hip + (hip->ip_hl << 2)); + if ((hip->ip_hl << 2) + 12 <= (rcv_len - (rcv_pkt->ip_hl << 2)) + && hip->ip_p == IPPROTO_UDP + && hudp->dest == htons(TT.port + seq)) + icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ?-1 : ricmp->icmp_code); + } else { + hicmp = (struct icmp *) ((void*)hip + (hip->ip_hl << 2)); + if (ricmp->icmp_type == ICMP_ECHOREPLY && ricmp->icmp_id == htons(TT.ident) + && ricmp->icmp_seq == htons(seq)) + icmp_res = ICMP_UNREACH_PORT; + + if ((hip->ip_hl << 2) + ICMP_HD_SIZE <= (rcv_len - (rcv_pkt->ip_hl << 2)) + && hip->ip_p == IPPROTO_ICMP + && hicmp->icmp_id == htons(TT.ident) + && hicmp->icmp_seq == htons(seq)) + icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ? -1 : ricmp->icmp_code); + } + } + if (!icmp_res) continue; + + if (addrlen > 0) { + unsigned delta = (t2.tv_sec * 1000000ULL + t2.tv_usec) + - (t1.tv_sec * 1000000ULL + t1.tv_usec); + + if (memcmp(&last.sin_addr, &from.sin_addr, sizeof(struct in_addr))) { + if (!(toys.optflags & FLAG_n)) { + char host[NI_MAXHOST]; + if (!getnameinfo((struct sockaddr *) &from, + sizeof(from), host, NI_MAXHOST, NULL, 0, 0)) + printf(" %s (", host); + else printf(" %s (", inet_ntoa(from.sin_addr)); + } + printf(" %s", inet_ntoa(from.sin_addr)); + if (!(toys.optflags & FLAG_n) ) printf(")"); + last = from; + } + printf(" %u.%03u ms", delta / 1000, delta % 1000); + if (toys.optflags & FLAG_l) printf(" (%d)", rcv_pkt->ip_ttl); + if (toys.optflags & FLAG_v) { + printf(" %d bytes from %s : icmp type %d code %d\t", + rcv_len, inet_ntoa(from.sin_addr), + ricmp->icmp_type, ricmp->icmp_code); + } + } else printf("\t!H"); + + switch (icmp_res) { + case ICMP_UNREACH_PORT: + if (rcv_pkt->ip_ttl <= 1) printf(" !"); + dest_reach = 1; + break; + case ICMP_UNREACH_NET: + printf(" !N"); + ++fexit; + break; + case ICMP_UNREACH_HOST: + printf(" !H"); + ++fexit; + break; + case ICMP_UNREACH_PROTOCOL: + printf(" !P"); + dest_reach = 1; + break; + case ICMP_UNREACH_NEEDFRAG: + printf(" !F-%d", pmtu); + ++fexit; + break; + case ICMP_UNREACH_SRCFAIL: + printf(" !S"); + ++fexit; + break; + case ICMP_UNREACH_FILTER_PROHIB: + case ICMP_UNREACH_NET_PROHIB: + printf(" !A"); + ++fexit; + break; + case ICMP_UNREACH_HOST_PROHIB: + printf(" !C"); + ++fexit; + break; + case ICMP_UNREACH_HOST_PRECEDENCE: + printf(" !V"); + ++fexit; + break; + case ICMP_UNREACH_PRECEDENCE_CUTOFF: + printf(" !C"); + ++fexit; + break; + case ICMP_UNREACH_NET_UNKNOWN: + case ICMP_UNREACH_HOST_UNKNOWN: + printf(" !U"); + ++fexit; + break; + case ICMP_UNREACH_ISOLATED: + printf(" !I"); + ++fexit; + break; + case ICMP_UNREACH_TOSNET: + case ICMP_UNREACH_TOSHOST: + printf(" !T"); + ++fexit; + break; + } + break; + } + } + } + } + xputc('\n'); + if (!memcmp(&from.sin_addr, &dest.sin_addr, sizeof(struct in_addr)) + || dest_reach || (fexit >= TT.ttl_probes -1)) + break; + } + +} + +void traceroute_main(void) +{ + unsigned opt_len = 0, pack_size, tyser = 0; + int set = 1, lsrr = 0; + + if (toys.optflags & FLAG_g) { + struct arg_list *node; + + for (node = TT.loose_source; node; node = node->next, lsrr++) { + struct sockaddr_in sin; + + memset( &sin, 0, sizeof(sin)); + if (lsrr >= 8) error_exit("no more than 8 gateways"); // NGATEWAYS + resolve_addr(node->arg, SOCK_STREAM, 0, &sin); + TT.gw_list[lsrr] = sin.sin_addr.s_addr; + } + opt_len = (lsrr + 1) * sizeof(TT.gw_list[0]); + } + + pack_size = sizeof(struct ip) + opt_len; + pack_size += (toys.optflags & FLAG_U) ? sizeof(struct udphdr) + + sizeof(struct payload_s) : ICMP_HD_SIZE; + + if (toys.optargs[1]) + TT.msg_len = get_int_value(toys.optargs[1], pack_size, 32768);//max packet size + + TT.msg_len = (TT.msg_len < pack_size) ? pack_size : TT.msg_len; + TT.recv_sock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (toys.optflags & FLAG_d + && (setsockopt(TT.recv_sock, SOL_SOCKET, SO_DEBUG, &set, sizeof(set)) < 0)) + perror_exit("SO_DEBUG failed "); + if (toys.optflags & FLAG_r + && (setsockopt(TT.recv_sock, SOL_SOCKET, SO_DONTROUTE, &set, sizeof(set)) < 0)) + perror_exit("SO_DONTROUTE failed "); + + if (toys.optflags & FLAG_U) TT.snd_sock = xsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + else TT.snd_sock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + + resolve_addr(toys.optargs[0], ((toys.optflags & FLAG_U) ? SOCK_DGRAM : SOCK_RAW), + ((toys.optflags & FLAG_U) ? IPPROTO_UDP : IPPROTO_ICMP), &dest); + + if (lsrr > 0) { + unsigned char optlist[MAX_IPOPTLEN]; + unsigned size; + + TT.gw_list[lsrr] = dest.sin_addr.s_addr; + ++lsrr; + + optlist[0] = IPOPT_NOP; + optlist[1] = IPOPT_LSRR;// loose source route option + size = lsrr * sizeof(TT.gw_list[0]); + optlist[2] = size + 3; + optlist[3] = IPOPT_MINOFF; + memcpy(optlist + 4, TT.gw_list, size); + + if (setsockopt(TT.snd_sock, IPPROTO_IP, IP_OPTIONS, + (char *)optlist, size + sizeof(TT.gw_list[0])) < 0) + perror_exit("LSRR IP_OPTIONS"); + } + + if (setsockopt(TT.snd_sock, SOL_SOCKET, SO_SNDBUF, &TT.msg_len, sizeof(TT.msg_len)) < 0) + perror_exit("SO_SNDBUF failed "); + + if ((toys.optflags & FLAG_t) && + setsockopt(TT.snd_sock, IPPROTO_IP, IP_TOS, &tyser, sizeof(tyser)) < 0) + perror_exit("IP_TOS %d failed ", TT.tos); + +#ifdef IP_DONTFRAG + if ((toys.optflags & FLAG_F) && + (setsockopt(TT.snd_sock, IPPROTO_IP, IP_DONTFRAG, &set, sizeof(set)) < 0)) + perror_exit("IP_DONTFRAG failed "); +#endif + + if ((toys.optflags & FLAG_d) + && (setsockopt(TT.snd_sock, SOL_SOCKET, SO_DEBUG, &set, sizeof(set)) < 0)) + perror_exit("SO_DEBUG failed "); + if ((toys.optflags & FLAG_r) && + (setsockopt(TT.snd_sock, SOL_SOCKET, SO_DONTROUTE, &set, sizeof(set)) < 0)) + perror_exit("SO_DONTROUTE failed "); + + + TT.packet = xmalloc(TT.msg_len); + TT.ident = getpid(); + + if (toys.optflags & FLAG_U) send_data = (struct payload_s *) (send_udp + 1); + else { + TT.ident |= 0x8000; + send_icmp->icmp_type = ICMP_ECHO; + send_icmp->icmp_id = htons(TT.ident); + } + if (toys.optflags & FLAG_s) { + struct sockaddr_in *source = xzalloc(sizeof(struct sockaddr_in)); + inet_aton(TT.src_ip, &source->sin_addr); + if (setsockopt(TT.snd_sock, IPPROTO_IP, IP_MULTICAST_IF, + (struct sockaddr*)source, sizeof(*source))) + perror_exit("can't set multicast source interface"); + if (bind(TT.snd_sock,(struct sockaddr*)source, sizeof(*source))) + perror_exit("bind"); + free(source); + } + + if(TT.first_ttl > TT.max_ttl) + error_exit("ERROR :Range for -f is 1 to %d (max ttl)", TT.max_ttl); + + printf("traceroute to %s(%s)", toys.optargs[0], inet_ntoa(dest.sin_addr)); + if (toys.optflags & FLAG_s) printf(" from %s", TT.src_ip); + printf(", %ld hops max, %u byte packets\n", TT.max_ttl, TT.msg_len); + + do_trace(); +} |