| /* |
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| /* |
| * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997 |
| * The Regents of the University of California. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that: (1) source code distributions |
| * retain the above copyright notice and this paragraph in its entirety, (2) |
| * distributions including binary code include the above copyright notice and |
| * this paragraph in its entirety in the documentation or other materials |
| * provided with the distribution, and (3) all advertising materials mentioning |
| * features or use of this software display the following acknowledgement: |
| * ``This product includes software developed by the University of California, |
| * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of |
| * the University nor the names of its contributors may be used to endorse |
| * or promote products derived from this software without specific prior |
| * written permission. |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| * |
| * |
| * @(#)$Header: traceroute.c,v 1.49 97/06/13 02:30:23 leres Exp $ (LBL) |
| */ |
| |
| #include <sys/param.h> |
| #include <sys/file.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <sys/time.h> |
| #include <sys/sysmacros.h> |
| |
| #include <netinet/in_systm.h> |
| #include <netinet/in.h> |
| #include <netinet/ip.h> |
| #include <netinet/ip_var.h> |
| #include <netinet/ip_icmp.h> |
| #include <netinet/udp.h> |
| #include <netinet/udp_var.h> |
| #include <netinet/ip6.h> |
| #include <netinet/icmp6.h> |
| |
| #include <arpa/inet.h> |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <malloc.h> |
| #include <memory.h> |
| #include <netdb.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <strings.h> |
| #include <unistd.h> |
| #include <libintl.h> |
| #include <locale.h> |
| #include <signal.h> |
| #include <setjmp.h> |
| #include <limits.h> |
| #include <zone.h> |
| |
| #include <priv_utils.h> |
| |
| #include <libinetutil.h> |
| #include "traceroute.h" |
| |
| #define MAX_SEQ 65535 /* max sequence value for ICMP */ |
| #define MAX_TRAFFIC_CLASS 255 /* max traffic class for IPv6 */ |
| #define MAX_FLOW_LABEL 0xFFFFF /* max flow label for IPv6 */ |
| #define MAX_TOS 255 /* max type-of-service for IPv4 */ |
| #define STR_LEN 30 |
| |
| /* store the information about a host */ |
| struct hostinfo { |
| char *name; /* hostname */ |
| int family; /* address family of the IP addresses */ |
| int num_addr; /* number of IP addresses */ |
| union any_in_addr *addrs; /* list of IP addresses */ |
| }; |
| |
| /* used to store a bunch of protocol specific values */ |
| struct pr_set { |
| int family; /* AF_INET or AF_INET6 */ |
| char name[STR_LEN]; /* "IPv4" or "IPv6" */ |
| char icmp[STR_LEN]; /* "icmp" or "ipv6-icmp" */ |
| int icmp_minlen; |
| int addr_len; |
| int ip_hdr_len; |
| int packlen; |
| int sock_size; /* size of sockaddr_in or sockaddr_in6 */ |
| struct sockaddr *to; |
| struct sockaddr *from; |
| void *from_sin_addr; |
| union any_in_addr *gwIPlist; |
| /* pointers to v4/v6 functions */ |
| struct ip *(*set_buffers_fn) (int); |
| int (*check_reply_fn)(struct msghdr *, int, int, uchar_t *, uchar_t *); |
| boolean_t (*print_icmp_other_fn)(uchar_t, uchar_t); |
| void (*print_addr_fn)(uchar_t *, int, struct sockaddr *); |
| |
| }; |
| |
| /* |
| * LBNL bug fixed: in LBNL traceroute 'uchar_t packet[512];' |
| * Not sufficient to hold the complete packet for ECHO REPLY of a big probe. |
| * Packet size is reported incorrectly in such a case. |
| * Also this buffer needs to be 32 bit aligned. In the future the alignment |
| * requirement will be increased to 64 bit. So, let's use 64 bit alignment now. |
| */ |
| static uint64_t packet[(IP_MAXPACKET + 1)/8]; /* received packet */ |
| |
| static struct ip *outip4; /* output buffer to send as an IPv4 datagram */ |
| static struct ip *outip6; /* output buffer to send as an IPv6 datagram */ |
| |
| /* Used to store the ancillary data that comes with the received packets */ |
| static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8]; |
| |
| /* first get the gw names, later you'll resolve them based on the family */ |
| static char *gwlist[MAXMAX_GWS]; /* gateway names list */ |
| static union any_in_addr gwIPlist[MAX_GWS]; /* gateway IPv4 address list */ |
| static union any_in_addr gwIP6list[MAX_GWS6]; /* gateway IPv6 address list */ |
| |
| static int family_input = AF_UNSPEC; /* User supplied protocol family */ |
| static int rcvsock4; /* receive (icmp) socket file descriptor */ |
| static int sndsock4; /* send (udp/icmp) socket file descriptor */ |
| static int rcvsock6; /* receive (icmp6) socket file descriptor */ |
| static int sndsock6; /* send (udp6/icmp6) socket file descriptor */ |
| int gw_count = 0; /* number of gateways */ |
| static struct sockaddr_in whereto; /* Who to try to reach */ |
| static struct sockaddr_in6 whereto6; |
| static struct sockaddr_in wherefrom; /* Who we are */ |
| static struct sockaddr_in6 wherefrom6; |
| static int packlen_input = 0; /* user input for packlen */ |
| |
| char *prog; |
| static char *source_input = NULL; /* this is user arg. source, doesn't change */ |
| static char *source = NULL; /* this gets modified after name lookup */ |
| char *hostname; |
| static char *device = NULL; /* interface name */ |
| static struct pr_set *pr4; /* protocol info for IPv4 */ |
| static struct pr_set *pr6; /* protocol info for IPv6 */ |
| static struct ifaddrlist *al4; /* list of interfaces */ |
| static struct ifaddrlist *al6; /* list of interfaces */ |
| static uint_t if_index = 0; /* interface index */ |
| static int num_v4 = 0; /* count of IPv4 addresses */ |
| static int num_v6 = 0; /* count of IPv6 addresses */ |
| static int num_ifs4 = 0; /* count of local IPv4 interfaces */ |
| static int num_ifs6 = 0; /* count of local IPv6 interfaces */ |
| |
| static int nprobes = 3; /* number of probes */ |
| static int max_ttl = 30; /* max number of hops */ |
| static int first_ttl = 1; /* initial number of hops */ |
| ushort_t ident; /* used to authenticate replies */ |
| ushort_t port = 32768 + 666; /* start udp dest port # for probe packets */ |
| |
| static int options = 0; /* socket options */ |
| boolean_t verbose = _B_FALSE; /* verbose output */ |
| static int waittime = 5; /* time to wait for response (in seconds) */ |
| static struct timeval delay = {0, 0}; /* delay between consecutive probe */ |
| boolean_t nflag = _B_FALSE; /* print addresses numerically */ |
| static boolean_t showttl = _B_FALSE; /* print the ttl(hop limit) of recvd pkt */ |
| boolean_t useicmp = _B_FALSE; /* use icmp echo instead of udp packets */ |
| boolean_t docksum = _B_TRUE; /* calculate checksums */ |
| static boolean_t collect_stat = _B_FALSE; /* print statistics */ |
| boolean_t settos = _B_FALSE; /* set type-of-service field */ |
| static int max_timeout = 5; /* quit after this consecutive timeouts */ |
| static boolean_t probe_all = _B_FALSE; /* probe all the IFs of the target */ |
| static boolean_t pick_src = _B_FALSE; /* traceroute picks the src address */ |
| |
| /* |
| * flow and class are specific to IPv6, tos and off are specific to IPv4. |
| * Each protocol uses the ones that are specific to itself, and ignores |
| * others. |
| */ |
| static uint_t flow = 0; /* IPv6 flow info */ |
| static uint_t class = 0; /* IPv6 class */ |
| uchar_t tos = 0; /* IPv4 type-of-service */ |
| ushort_t off = 0; /* set DF bit */ |
| |
| static jmp_buf env; /* stack environment for longjmp() */ |
| boolean_t raw_req; /* if sndsock for IPv4 must be raw */ |
| |
| /* Forwards */ |
| static uint_t calc_packetlen(int, struct pr_set *); |
| extern int check_reply(struct msghdr *, int, int, uchar_t *, uchar_t *); |
| extern int check_reply6(struct msghdr *, int, int, uchar_t *, uchar_t *); |
| static double deltaT(struct timeval *, struct timeval *); |
| static char *device_name(struct ifaddrlist *, int, union any_in_addr *, |
| struct pr_set *); |
| extern void *find_ancillary_data(struct msghdr *, int, int); |
| static boolean_t has_addr(struct addrinfo *, union any_in_addr *); |
| static struct ifaddrlist *find_device(struct ifaddrlist *, int, char *); |
| static struct ifaddrlist *find_ifaddr(struct ifaddrlist *, int, |
| union any_in_addr *, int); |
| static void get_gwaddrs(char **, int, union any_in_addr *, |
| union any_in_addr *, int *, int *); |
| static void get_hostinfo(char *, int, struct addrinfo **); |
| char *inet_name(union any_in_addr *, int); |
| ushort_t in_cksum(ushort_t *, int); |
| extern int ip_hdr_length_v6(ip6_t *, int, uint8_t *); |
| extern char *pr_type(uchar_t); |
| extern char *pr_type6(uchar_t); |
| extern void print_addr(uchar_t *, int, struct sockaddr *); |
| extern void print_addr6(uchar_t *, int, struct sockaddr *); |
| extern boolean_t print_icmp_other(uchar_t, uchar_t); |
| extern boolean_t print_icmp_other6(uchar_t, uchar_t); |
| static void print_stats(int, int, double, double, double, double); |
| static void print_unknown_host_msg(const char *, const char *); |
| static void record_stats(double, int *, double *, double *, double *, double *); |
| static void resolve_nodes(int *, struct addrinfo **); |
| static void select_src_addr(union any_in_addr *, union any_in_addr *, int); |
| extern void send_probe(int, struct sockaddr *, struct ip *, int, int, |
| struct timeval *, int); |
| extern void send_probe6(int, struct msghdr *, struct ip *, int, int, |
| struct timeval *, int); |
| extern void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int, |
| uint_t); |
| extern struct ip *set_buffers(int); |
| extern struct ip *set_buffers6(int); |
| extern void set_IPv4opt_sourcerouting(int, union any_in_addr *, |
| union any_in_addr *); |
| static void set_sin(struct sockaddr *, union any_in_addr *, int); |
| static int set_src_addr(struct pr_set *, struct ifaddrlist **); |
| static void setup_protocol(struct pr_set *, int); |
| static void setup_socket(struct pr_set *, int); |
| static void sig_handler(int); |
| static int str2int(const char *, const char *, int, int); |
| static double str2dbl(const char *, const char *, double, double); |
| static void trace_it(struct addrinfo *); |
| static void traceroute(union any_in_addr *, struct msghdr *, struct pr_set *, |
| int, struct ifaddrlist *); |
| static void tv_sub(struct timeval *, struct timeval *); |
| static void usage(void); |
| static int wait_for_reply(int, struct msghdr *, struct timeval *); |
| static double xsqrt(double); |
| |
| /* |
| * main |
| */ |
| int |
| main(int argc, char **argv) |
| { |
| struct addrinfo *ai_dst = NULL; /* destination host */ |
| /* |
| * "probing_successful" indicates if we could successfully send probes, |
| * not necessarily received reply from the target (this behavior is from |
| * the original traceroute). It's _B_FALSE if packlen is invalid, or no |
| * interfaces found. |
| */ |
| boolean_t probing_successful = _B_FALSE; |
| int longjmp_return; /* return value from longjump */ |
| int i = 0; |
| char *cp; |
| int op; |
| char *ep; |
| char temp_buf[INET6_ADDRSTRLEN]; /* use for inet_ntop() */ |
| double pause; |
| |
| /* |
| * A raw socket will be used for IPv4 if there is sufficient |
| * privilege. |
| */ |
| raw_req = priv_ineffect(PRIV_NET_RAWACCESS); |
| |
| /* |
| * We'll need the privilege only when we open the sockets; that's |
| * when we'll fail if the program has insufficient privileges. |
| */ |
| (void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_NET_ICMPACCESS, |
| raw_req ? PRIV_NET_RAWACCESS : NULL, NULL); |
| |
| (void) setlinebuf(stdout); |
| |
| if ((cp = strrchr(argv[0], '/')) != NULL) |
| prog = cp + 1; |
| else |
| prog = argv[0]; |
| |
| opterr = 0; |
| while ((op = getopt(argc, argv, "adFIlnrSvxA:c:f:g:i:L:m:P:p:Q:q:s:" |
| "t:w:")) != EOF) { |
| switch (op) { |
| case 'A': |
| if (strcmp(optarg, "inet") == 0) { |
| family_input = AF_INET; |
| } else if (strcmp(optarg, "inet6") == 0) { |
| family_input = AF_INET6; |
| } else { |
| Fprintf(stderr, |
| "%s: unknown address family %s\n", |
| prog, optarg); |
| exit(EXIT_FAILURE); |
| } |
| break; |
| |
| case 'a': |
| probe_all = _B_TRUE; |
| break; |
| |
| case 'c': |
| class = str2int(optarg, "traffic class", 0, |
| MAX_TRAFFIC_CLASS); |
| break; |
| |
| case 'd': |
| options |= SO_DEBUG; |
| break; |
| |
| case 'f': |
| first_ttl = str2int(optarg, "first ttl", 1, MAXTTL); |
| break; |
| |
| case 'F': |
| off = IP_DF; |
| break; |
| |
| case 'g': |
| if (!raw_req) { |
| Fprintf(stderr, |
| "%s: privilege to specify a loose source " |
| "route gateway is unavailable\n", |
| prog); |
| exit(EXIT_FAILURE); |
| } |
| if (gw_count >= MAXMAX_GWS) { |
| Fprintf(stderr, |
| "%s: Too many gateways\n", prog); |
| exit(EXIT_FAILURE); |
| } |
| gwlist[gw_count] = strdup(optarg); |
| if (gwlist[gw_count] == NULL) { |
| Fprintf(stderr, "%s: strdup %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| ++gw_count; |
| break; |
| |
| case 'l': |
| showttl = _B_TRUE; |
| break; |
| |
| case 'i': |
| /* this can be IF name or IF index */ |
| if_index = (uint_t)strtol(optarg, &ep, 10); |
| |
| /* convert IF index <--> IF name */ |
| if (errno != 0 || *ep != '\0') { |
| device = optarg; |
| if_index = if_nametoindex((const char *)device); |
| |
| /* |
| * In case it fails, check to see if the problem |
| * is other than "IF not found". |
| */ |
| if (if_index == 0 && errno != ENXIO) { |
| Fprintf(stderr, "%s: if_nametoindex:" |
| "%s\n", prog, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } else { |
| device = (char *)malloc(LIFNAMSIZ + 1); |
| if (device == NULL) { |
| Fprintf(stderr, "%s: malloc: %s\n", |
| prog, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| device = if_indextoname(if_index, device); |
| if (device != NULL) { |
| device[LIFNAMSIZ] = '\0'; |
| } else if (errno != ENXIO) { |
| /* |
| * The problem was other than "index |
| * not found". |
| */ |
| Fprintf(stderr, "%s: if_indextoname:" |
| "%s\n", prog, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| if (device == NULL || if_index == 0) { |
| Fprintf(stderr, "%s: interface %s " |
| "doesn't match any actual interfaces\n", |
| prog, optarg); |
| exit(EXIT_FAILURE); |
| } |
| break; |
| |
| case 'I': |
| useicmp = _B_TRUE; |
| break; |
| |
| case 'L': |
| flow = str2int(optarg, "flow label", 0, MAX_FLOW_LABEL); |
| break; |
| |
| case 'm': |
| max_ttl = str2int(optarg, "max ttl(hop limit)", 1, |
| MAXTTL); |
| break; |
| |
| case 'n': |
| nflag = _B_TRUE; |
| break; |
| |
| case 'P': |
| pause = str2dbl(optarg, "pause", 0, INT_MAX); |
| delay.tv_sec = (time_t)pause; |
| delay.tv_usec = (suseconds_t)((pause - delay.tv_sec) * |
| 1000000); |
| break; |
| |
| case 'p': |
| port = str2int(optarg, "port", 1, MAX_PORT); |
| break; |
| |
| case 'Q': |
| max_timeout = str2int(optarg, "max timeout", 1, -1); |
| break; |
| |
| case 'q': |
| nprobes = str2int(optarg, "nprobes", 1, -1); |
| break; |
| |
| case 'r': |
| options |= SO_DONTROUTE; |
| break; |
| |
| case 'S': |
| collect_stat = _B_TRUE; |
| break; |
| |
| case 's': |
| /* |
| * set the ip source address of the outbound |
| * probe (e.g., on a multi-homed host). |
| */ |
| source_input = optarg; |
| break; |
| |
| case 't': |
| tos = (uchar_t)str2int(optarg, "tos", 0, MAX_TOS); |
| settos = _B_TRUE; |
| break; |
| |
| case 'v': |
| verbose = _B_TRUE; |
| break; |
| |
| case 'x': |
| docksum = _B_FALSE; |
| break; |
| |
| case 'w': |
| waittime = str2int(optarg, "wait time", 2, -1); |
| break; |
| |
| default: |
| usage(); |
| break; |
| } |
| } |
| |
| /* |
| * If it's probe_all, SIGQUIT makes traceroute exit(). But we set the |
| * address to jump back to in traceroute(). Until then, we'll need to |
| * temporarily specify one. |
| */ |
| if (probe_all) { |
| if ((longjmp_return = setjmp(env)) != 0) { |
| if (longjmp_return == SIGQUIT) { |
| Printf("(exiting)\n"); |
| exit(EXIT_SUCCESS); |
| } else { /* should never happen */ |
| exit(EXIT_FAILURE); |
| } |
| } |
| (void) signal(SIGQUIT, sig_handler); |
| } |
| |
| if ((gw_count > 0) && (options & SO_DONTROUTE)) { |
| Fprintf(stderr, "%s: loose source route gateways (-g)" |
| " cannot be specified when probe packets are sent" |
| " directly to a host on an attached network (-r)\n", |
| prog); |
| exit(EXIT_FAILURE); |
| } |
| |
| i = argc - optind; |
| if (i == 1 || i == 2) { |
| hostname = argv[optind]; |
| |
| if (i == 2) { |
| /* accept any length now, we'll check it later */ |
| packlen_input = str2int(argv[optind + 1], |
| "packet length", 0, -1); |
| } |
| } else { |
| usage(); |
| } |
| |
| if (first_ttl > max_ttl) { |
| Fprintf(stderr, |
| "%s: first ttl(hop limit) (%d) may not be greater" |
| " than max ttl(hop limit) (%d)\n", |
| prog, first_ttl, max_ttl); |
| exit(EXIT_FAILURE); |
| } |
| |
| /* resolve hostnames */ |
| resolve_nodes(&family_input, &ai_dst); |
| if (ai_dst == NULL) { |
| exit(EXIT_FAILURE); |
| } |
| |
| /* |
| * If it's probe_all, SIGINT makes traceroute skip to probing next IP |
| * address of the target. The new interrupt handler is assigned in |
| * traceroute() function. Until then let's ignore the signal. |
| */ |
| if (probe_all) |
| (void) signal(SIGINT, SIG_IGN); |
| |
| ident = (getpid() & 0xffff) | 0x8000; |
| |
| /* |
| * We KNOW that probe_all == TRUE if family is AF_UNSPEC, |
| * since family is set to the specific AF found unless it's |
| * probe_all. So if family == AF_UNSPEC, we need to init pr4 and pr6. |
| */ |
| switch (family_input) { |
| case AF_UNSPEC: |
| pr4 = (struct pr_set *)malloc(sizeof (struct pr_set)); |
| if (pr4 == NULL) { |
| Fprintf(stderr, |
| "%s: malloc %s\n", prog, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| pr6 = (struct pr_set *)malloc(sizeof (struct pr_set)); |
| if (pr6 == NULL) { |
| Fprintf(stderr, |
| "%s: malloc %s\n", prog, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| setup_protocol(pr6, AF_INET6); |
| setup_protocol(pr4, AF_INET); |
| outip6 = (*pr6->set_buffers_fn)(pr6->packlen); |
| setup_socket(pr6, pr6->packlen); |
| |
| outip4 = (*pr4->set_buffers_fn)(pr4->packlen); |
| setup_socket(pr4, pr4->packlen); |
| num_ifs6 = set_src_addr(pr6, &al6); |
| num_ifs4 = set_src_addr(pr4, &al4); |
| break; |
| case AF_INET6: |
| pr6 = (struct pr_set *)malloc(sizeof (struct pr_set)); |
| if (pr6 == NULL) { |
| Fprintf(stderr, |
| "%s: malloc %s\n", prog, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| setup_protocol(pr6, AF_INET6); |
| outip6 = (*pr6->set_buffers_fn)(pr6->packlen); |
| setup_socket(pr6, pr6->packlen); |
| num_ifs6 = set_src_addr(pr6, &al6); |
| break; |
| case AF_INET: |
| pr4 = (struct pr_set *)malloc(sizeof (struct pr_set)); |
| if (pr4 == NULL) { |
| Fprintf(stderr, |
| "%s: malloc %s\n", prog, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| setup_protocol(pr4, AF_INET); |
| outip4 = (*pr4->set_buffers_fn)(pr4->packlen); |
| setup_socket(pr4, pr4->packlen); |
| num_ifs4 = set_src_addr(pr4, &al4); |
| break; |
| default: |
| Fprintf(stderr, "%s: unknow address family.\n", prog); |
| exit(EXIT_FAILURE); |
| } |
| |
| if (num_v4 + num_v6 > 1 && !probe_all) { |
| if (ai_dst->ai_family == AF_INET) { |
| Fprintf(stderr, |
| "%s: Warning: %s has multiple addresses;" |
| " using %s\n", prog, hostname, |
| inet_ntop(AF_INET, |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| (void *)&((struct sockaddr_in *) |
| ai_dst->ai_addr)->sin_addr, |
| temp_buf, sizeof (temp_buf))); |
| } else { |
| Fprintf(stderr, |
| "%s: Warning: %s has multiple addresses;" |
| " using %s\n", prog, hostname, |
| inet_ntop(AF_INET6, |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| (void *)&((struct sockaddr_in6 *) |
| ai_dst->ai_addr)->sin6_addr, |
| temp_buf, sizeof (temp_buf))); |
| } |
| } |
| |
| if (num_ifs4 + num_ifs6 > 0) { |
| trace_it(ai_dst); |
| probing_successful = _B_TRUE; |
| } |
| |
| (void) close(rcvsock4); |
| (void) close(sndsock4); |
| (void) close(rcvsock6); |
| (void) close(sndsock6); |
| |
| /* |
| * if we could probe any of the IP addresses of the target, that means |
| * this was a successful operation |
| */ |
| if (probing_successful) |
| return (EXIT_SUCCESS); |
| else |
| return (EXIT_FAILURE); |
| } |
| |
| /* |
| * print "unknown host" message |
| */ |
| static void |
| print_unknown_host_msg(const char *protocol, const char *host) |
| { |
| Fprintf(stderr, "%s: unknown%s host %s\n", prog, protocol, host); |
| } |
| |
| /* |
| * resolve destination host and gateways |
| */ |
| static void |
| resolve_nodes(int *family, struct addrinfo **ai_dstp) |
| { |
| struct addrinfo *ai_dst = NULL; |
| struct addrinfo *aip = NULL; |
| int num_resolved_gw = 0; |
| int num_resolved_gw6 = 0; |
| |
| get_hostinfo(hostname, *family, &ai_dst); |
| if (ai_dst == NULL) { |
| print_unknown_host_msg("", hostname); |
| exit(EXIT_FAILURE); |
| } |
| /* Get a count of the v4 & v6 addresses */ |
| for (aip = ai_dst; aip != NULL; aip = aip->ai_next) { |
| switch (aip->ai_family) { |
| case AF_INET: |
| num_v4++; |
| break; |
| case AF_INET6: |
| num_v6++; |
| break; |
| } |
| } |
| |
| if (*family == AF_UNSPEC && !probe_all) { |
| *family = ai_dst->ai_family; |
| } |
| |
| /* resolve gateways */ |
| if (gw_count > 0) { |
| get_gwaddrs(gwlist, *family, gwIPlist, gwIP6list, |
| &num_resolved_gw, &num_resolved_gw6); |
| |
| /* we couldn't resolve a gateway as an IPv6 host */ |
| if (num_resolved_gw6 != gw_count && num_v6 != 0) { |
| if (*family == AF_INET6 || *family == AF_UNSPEC) |
| print_unknown_host_msg(" IPv6", |
| gwlist[num_resolved_gw6]); |
| num_v6 = 0; |
| } |
| |
| /* we couldn't resolve a gateway as an IPv4 host */ |
| if (num_resolved_gw != gw_count && num_v4 != 0) { |
| if (*family == AF_INET || *family == AF_UNSPEC) |
| print_unknown_host_msg(" IPv4", |
| gwlist[num_resolved_gw]); |
| num_v4 = 0; |
| } |
| } |
| |
| *ai_dstp = (num_v4 + num_v6 > 0) ? ai_dst : NULL; |
| } |
| |
| /* |
| * Given IP address or hostname, return v4 and v6 hostinfo lists. |
| * Assumes that hostinfo ** ptrs are non-null. |
| */ |
| static void |
| get_hostinfo(char *host, int family, struct addrinfo **aipp) |
| { |
| struct addrinfo hints, *ai; |
| struct in6_addr addr6; |
| struct in_addr addr; |
| char abuf[INET6_ADDRSTRLEN]; /* use for inet_ntop() */ |
| int rc; |
| |
| /* |
| * Take care of v4-mapped addresses. It should run same as v4, after |
| * chopping off the prefix, leaving the IPv4 address |
| */ |
| if ((inet_pton(AF_INET6, host, &addr6) > 0) && |
| IN6_IS_ADDR_V4MAPPED(&addr6)) { |
| /* peel off the "mapping" stuff, leaving 32 bit IPv4 address */ |
| IN6_V4MAPPED_TO_INADDR(&addr6, &addr); |
| |
| /* convert it back to a string */ |
| (void) inet_ntop(AF_INET, &addr, abuf, sizeof (abuf)); |
| |
| /* now the host is an IPv4 address */ |
| (void) strcpy(host, abuf); |
| |
| /* |
| * If it's a mapped address, we convert it into IPv4 |
| * address because traceroute will send and receive IPv4 |
| * packets for that address. Therefore, it's a failure case to |
| * ask get_hostinfo() to treat a mapped address as an IPv6 |
| * address. |
| */ |
| if (family == AF_INET6) { |
| return; |
| } |
| } |
| |
| (void) memset(&hints, 0, sizeof (hints)); |
| hints.ai_family = family; |
| hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME; |
| rc = getaddrinfo(host, NULL, &hints, &ai); |
| if (rc != 0) { |
| if (rc != EAI_NONAME) |
| Fprintf(stderr, "%s: getaddrinfo: %s\n", prog, |
| gai_strerror(rc)); |
| *aipp = NULL; |
| return; |
| } |
| *aipp = ai; |
| } |
| |
| /* |
| * Calculate the packet length to be used, and check against the valid range. |
| * Returns -1 if range check fails. |
| */ |
| static uint_t |
| calc_packetlen(int plen_input, struct pr_set *pr) |
| { |
| int minpacket; /* min ip packet size */ |
| int optlen; /* length of ip options */ |
| int plen; |
| |
| /* |
| * LBNL bug fixed: miscalculation of optlen |
| */ |
| if (gw_count > 0) { |
| /* |
| * IPv4: |
| * ---- |
| * 5 (NO OPs) + 3 (code, len, ptr) + gateways |
| * IP options field can hold up to 9 gateways. But the API |
| * allows you to specify only 8, because the last one is the |
| * destination host. When this packet is sent, on the wire |
| * you see one gateway replaced by 4 NO OPs. The other 1 NO |
| * OP is for alignment |
| * |
| * IPv6: |
| * ---- |
| * Well, formula is different, but the result is same. |
| * 8 byte fixed part for Type 0 Routing header, followed by |
| * gateway addresses |
| */ |
| optlen = 8 + gw_count * pr->addr_len; |
| } else { |
| optlen = 0; |
| } |
| |
| /* take care of the packet length calculations and checks */ |
| minpacket = pr->ip_hdr_len + sizeof (struct outdata) + optlen; |
| if (useicmp) |
| minpacket += pr->icmp_minlen; /* minimum ICMP header size */ |
| else |
| minpacket += sizeof (struct udphdr); |
| plen = plen_input; |
| if (plen == 0) { |
| plen = minpacket; /* minimum sized packet */ |
| } else if (minpacket > plen || plen > IP_MAXPACKET) { |
| Fprintf(stderr, "%s: %s packet size must be >= %d and <= %d\n", |
| prog, pr->name, minpacket, IP_MAXPACKET); |
| return (0); |
| } |
| |
| return (plen); |
| } |
| |
| /* |
| * Sets the source address by resolving -i and -s arguments, or if -i and -s |
| * don't dictate any, it sets the pick_src to make sure traceroute uses the |
| * kernel's pick of the source address. |
| * Returns number of interfaces configured on the source host, 0 on error or |
| * there's no interface which is up amd not a loopback. |
| */ |
| static int |
| set_src_addr(struct pr_set *pr, struct ifaddrlist **alp) |
| { |
| union any_in_addr *ap; |
| struct ifaddrlist *al = NULL; |
| struct ifaddrlist *tmp1_al = NULL; |
| struct ifaddrlist *tmp2_al = NULL; |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| struct sockaddr_in *sin_from = (struct sockaddr_in *)pr->from; |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| struct sockaddr_in6 *sin6_from = (struct sockaddr_in6 *)pr->from; |
| struct addrinfo *aip; |
| char errbuf[ERRBUFSIZE]; |
| char abuf[INET6_ADDRSTRLEN]; /* use for inet_ntop() */ |
| int num_ifs; /* all the interfaces */ |
| int num_src_ifs; /* exclude loopback and down */ |
| int i; |
| uint_t ifaddrflags = 0; |
| |
| source = source_input; |
| |
| if (device != NULL) |
| ifaddrflags |= LIFC_UNDER_IPMP; |
| |
| /* get the interface address list */ |
| num_ifs = ifaddrlist(&al, pr->family, ifaddrflags, errbuf); |
| if (num_ifs < 0) { |
| Fprintf(stderr, "%s: ifaddrlist: %s\n", prog, errbuf); |
| exit(EXIT_FAILURE); |
| } |
| |
| num_src_ifs = 0; |
| for (i = 0; i < num_ifs; i++) { |
| if (!(al[i].flags & IFF_LOOPBACK) && (al[i].flags & IFF_UP)) |
| num_src_ifs++; |
| } |
| |
| if (num_src_ifs == 0) { |
| Fprintf(stderr, "%s: can't find any %s network interfaces\n", |
| prog, pr->name); |
| return (0); |
| } |
| |
| /* verify the device */ |
| if (device != NULL) { |
| tmp1_al = find_device(al, num_ifs, device); |
| |
| if (tmp1_al == NULL) { |
| Fprintf(stderr, "%s: %s (index %d) is an invalid %s" |
| " interface\n", prog, device, if_index, pr->name); |
| free(al); |
| return (0); |
| } |
| } |
| |
| /* verify the source address */ |
| if (source != NULL) { |
| get_hostinfo(source, pr->family, &aip); |
| if (aip == NULL) { |
| Fprintf(stderr, |
| "%s: %s is an invalid %s source address\n", |
| prog, source, pr->name); |
| |
| free(al); |
| return (0); |
| } |
| |
| source = aip->ai_canonname; |
| |
| if (pr->family == AF_INET) |
| ap = (union any_in_addr *) |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| &((struct sockaddr_in *)aip->ai_addr)->sin_addr; |
| else |
| ap = (union any_in_addr *) |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| &((struct sockaddr_in6 *)aip->ai_addr)->sin6_addr; |
| |
| /* |
| * LBNL bug fixed: used to accept any src address |
| */ |
| tmp2_al = find_ifaddr(al, num_ifs, ap, pr->family); |
| if (tmp2_al == NULL) { |
| (void) inet_ntop(pr->family, ap, abuf, sizeof (abuf)); |
| Fprintf(stderr, "%s: %s is not a local %s address\n", |
| prog, abuf, pr->name); |
| free(al); |
| freeaddrinfo(aip); |
| return (0); |
| } |
| } |
| |
| pick_src = _B_FALSE; |
| |
| if (source == NULL) { /* no -s used */ |
| if (device == NULL) { /* no -i used, no -s used */ |
| pick_src = _B_TRUE; |
| } else { /* -i used, no -s used */ |
| /* |
| * -i used, but not -s, and it's IPv4: set the source |
| * address to whatever the interface has configured on |
| * it. |
| */ |
| if (pr->family == AF_INET) |
| set_sin(pr->from, &(tmp1_al->addr), pr->family); |
| else |
| pick_src = _B_TRUE; |
| } |
| } else { /* -s used */ |
| if (device == NULL) { /* no -i used, -s used */ |
| set_sin(pr->from, ap, pr->family); |
| |
| if (aip->ai_next != NULL) { |
| (void) inet_ntop(pr->family, pr->from_sin_addr, |
| abuf, sizeof (abuf)); |
| Fprintf(stderr, "%s: Warning: %s has multiple " |
| "addresses; using %s\n", prog, source, |
| abuf); |
| } |
| } else { /* -i and -s used */ |
| /* |
| * Make sure the source specified matches the |
| * interface address. You only care about this for IPv4 |
| * IPv6 can handle IF not matching src address |
| */ |
| if (pr->family == AF_INET) { |
| if (!has_addr(aip, &tmp1_al->addr)) { |
| Fprintf(stderr, |
| "%s: %s is not on interface %s\n", |
| prog, source, device); |
| exit(EXIT_FAILURE); |
| } |
| /* |
| * make sure we use the one matching the |
| * interface's address |
| */ |
| *ap = tmp1_al->addr; |
| } |
| |
| set_sin(pr->from, ap, pr->family); |
| } |
| } |
| |
| /* |
| * Binding at this point will set the source address to be used |
| * for both IPv4 (when raw IP datagrams are not required) and |
| * IPv6. If the address being bound to is zero, then the kernel |
| * will end up choosing the source address when the datagram is |
| * sent. |
| * |
| * For raw IPv4 datagrams, the source address is initialized |
| * within traceroute() along with the outbound destination |
| * address. |
| */ |
| if (pr->family == AF_INET && !raw_req) { |
| sin_from->sin_family = AF_INET; |
| sin_from->sin_port = htons(ident); |
| if (bind(sndsock4, (struct sockaddr *)pr->from, |
| sizeof (struct sockaddr_in)) < 0) { |
| Fprintf(stderr, "%s: bind: %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } else if (pr->family == AF_INET6) { |
| sin6_from->sin6_family = AF_INET6; |
| sin6_from->sin6_port = htons(ident); |
| if (bind(sndsock6, (struct sockaddr *)pr->from, |
| sizeof (struct sockaddr_in6)) < 0) { |
| Fprintf(stderr, "%s: bind: %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| whereto6.sin6_flowinfo = htonl((class << 20) | flow); |
| } |
| *alp = al; |
| return (num_ifs); |
| } |
| |
| /* |
| * Returns the complete ifaddrlist structure matching the desired interface |
| * address. Ignores interfaces which are either down or loopback. |
| */ |
| static struct ifaddrlist * |
| find_ifaddr(struct ifaddrlist *al, int len, union any_in_addr *addr, |
| int family) |
| { |
| struct ifaddrlist *tmp_al = al; |
| int i; |
| size_t addr_len = (family == AF_INET) ? sizeof (struct in_addr) : |
| sizeof (struct in6_addr); |
| |
| for (i = 0; i < len; i++, tmp_al++) { |
| if ((!(tmp_al->flags & IFF_LOOPBACK) && |
| (tmp_al->flags & IFF_UP)) && |
| (memcmp(&tmp_al->addr, addr, addr_len) == 0)) |
| break; |
| } |
| |
| if (i < len) { |
| return (tmp_al); |
| } else { |
| return (NULL); |
| } |
| } |
| |
| /* |
| * Returns the complete ifaddrlist structure matching the desired interface name |
| * Ignores interfaces which are either down or loopback. |
| */ |
| static struct ifaddrlist * |
| find_device(struct ifaddrlist *al, int len, char *device) |
| { |
| struct ifaddrlist *tmp_al = al; |
| int i; |
| |
| for (i = 0; i < len; i++, tmp_al++) { |
| if ((!(tmp_al->flags & IFF_LOOPBACK) && |
| (tmp_al->flags & IFF_UP)) && |
| (strcmp(tmp_al->device, device) == 0)) |
| break; |
| } |
| |
| if (i < len) { |
| return (tmp_al); |
| } else { |
| return (NULL); |
| } |
| } |
| |
| /* |
| * returns _B_TRUE if given hostinfo contains the given address |
| */ |
| static boolean_t |
| has_addr(struct addrinfo *ai, union any_in_addr *addr) |
| { |
| struct addrinfo *ai_tmp = NULL; |
| union any_in_addr *ap; |
| |
| for (ai_tmp = ai; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) { |
| if (ai_tmp->ai_family == AF_INET6) |
| continue; |
| ap = (union any_in_addr *) |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| &((struct sockaddr_in *)ai_tmp->ai_addr)->sin_addr; |
| if (memcmp(ap, addr, sizeof (struct in_addr)) == 0) |
| break; |
| } |
| |
| if (ai_tmp != NULL) { |
| return (_B_TRUE); |
| } else { |
| return (_B_FALSE); |
| } |
| } |
| |
| /* |
| * Resolve the gateway names, splitting results into v4 and v6 lists. |
| * Gateway addresses are added to the appropriate passed-in array; the |
| * number of resolved gateways for each af is returned in resolved[6]. |
| * Assumes that passed-in arrays are large enough for MAX_GWS[6] addrs |
| * and resolved[6] ptrs are non-null; ignores array and counter if the |
| * address family param makes them irrelevant. |
| */ |
| static void |
| get_gwaddrs(char **gwlist, int family, union any_in_addr *gwIPlist, |
| union any_in_addr *gwIPlist6, int *resolved, int *resolved6) |
| { |
| int i; |
| boolean_t check_v4 = _B_TRUE, check_v6 = _B_TRUE; |
| struct addrinfo *ai = NULL; |
| struct addrinfo *aip = NULL; |
| |
| *resolved = *resolved6 = 0; |
| switch (family) { |
| case AF_UNSPEC: |
| break; |
| case AF_INET: |
| check_v6 = _B_FALSE; |
| break; |
| case AF_INET6: |
| check_v4 = _B_FALSE; |
| break; |
| default: |
| return; |
| } |
| |
| if (check_v4 && gw_count >= MAX_GWS) { |
| check_v4 = _B_FALSE; |
| Fprintf(stderr, "%s: too many IPv4 gateways\n", prog); |
| num_v4 = 0; |
| } |
| if (check_v6 && gw_count >= MAX_GWS6) { |
| check_v6 = _B_FALSE; |
| Fprintf(stderr, "%s: too many IPv6 gateways\n", prog); |
| num_v6 = 0; |
| } |
| |
| for (i = 0; i < gw_count; i++) { |
| if (!check_v4 && !check_v6) |
| return; |
| get_hostinfo(gwlist[i], family, &ai); |
| if (ai == NULL) |
| return; |
| if (check_v4 && num_v4 != 0) { |
| check_v4 = _B_FALSE; |
| for (aip = ai; aip != NULL; aip = aip->ai_next) { |
| if (aip->ai_family == AF_INET) { |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| bcopy(&((struct sockaddr_in *) |
| aip->ai_addr)->sin_addr, |
| &gwIPlist[i].addr, |
| aip->ai_addrlen); |
| (*resolved)++; |
| check_v4 = _B_TRUE; |
| break; |
| } |
| } |
| } else if (check_v4) { |
| check_v4 = _B_FALSE; |
| } |
| if (check_v6 && num_v6 != 0) { |
| check_v6 = _B_FALSE; |
| for (aip = ai; aip != NULL; aip = aip->ai_next) { |
| if (aip->ai_family == AF_INET6) { |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| bcopy(&((struct sockaddr_in6 *) |
| aip->ai_addr)->sin6_addr, |
| &gwIPlist6[i].addr6, |
| aip->ai_addrlen); |
| (*resolved6)++; |
| check_v6 = _B_TRUE; |
| break; |
| } |
| } |
| } else if (check_v6) { |
| check_v6 = _B_FALSE; |
| } |
| } |
| freeaddrinfo(ai); |
| } |
| |
| /* |
| * set protocol specific values here |
| */ |
| static void |
| setup_protocol(struct pr_set *pr, int family) |
| { |
| /* |
| * Set the global variables for each AF. This is going to save us lots |
| * of "if (family == AF_INET)... else .." |
| */ |
| pr->family = family; |
| |
| if (family == AF_INET) { |
| if (!docksum) { |
| Fprintf(stderr, |
| "%s: Warning: checksums disabled\n", prog); |
| } |
| (void) strcpy(pr->name, "IPv4"); |
| (void) strcpy(pr->icmp, "icmp"); |
| pr->icmp_minlen = ICMP_MINLEN; |
| pr->addr_len = sizeof (struct in_addr); |
| pr->ip_hdr_len = sizeof (struct ip); |
| pr->sock_size = sizeof (struct sockaddr_in); |
| pr->to = (struct sockaddr *)&whereto; |
| pr->from = (struct sockaddr *)&wherefrom; |
| pr->from_sin_addr = (void *)&wherefrom.sin_addr; |
| pr->gwIPlist = gwIPlist; |
| pr->set_buffers_fn = set_buffers; |
| pr->check_reply_fn = check_reply; |
| pr->print_icmp_other_fn = print_icmp_other; |
| pr->print_addr_fn = print_addr; |
| pr->packlen = calc_packetlen(packlen_input, pr); |
| } else { |
| (void) strcpy(pr->name, "IPv6"); |
| (void) strcpy(pr->icmp, "ipv6-icmp"); |
| pr->icmp_minlen = ICMP6_MINLEN; |
| pr->addr_len = sizeof (struct in6_addr); |
| pr->ip_hdr_len = sizeof (struct ip6_hdr); |
| pr->sock_size = sizeof (struct sockaddr_in6); |
| pr->to = (struct sockaddr *)&whereto6; |
| pr->from = (struct sockaddr *)&wherefrom6; |
| pr->from_sin_addr = (void *)&wherefrom6.sin6_addr; |
| pr->gwIPlist = gwIP6list; |
| pr->set_buffers_fn = set_buffers6; |
| pr->check_reply_fn = check_reply6; |
| pr->print_icmp_other_fn = print_icmp_other6; |
| pr->print_addr_fn = print_addr6; |
| pr->packlen = calc_packetlen(packlen_input, pr); |
| } |
| if (pr->packlen == 0) |
| exit(EXIT_FAILURE); |
| } |
| |
| /* |
| * setup the sockets for the given protocol's address family |
| */ |
| static void |
| setup_socket(struct pr_set *pr, int packet_len) |
| { |
| int on = 1; |
| struct protoent *pe; |
| int type; |
| int proto; |
| int int_op; |
| int rsock; |
| int ssock; |
| |
| if ((pe = getprotobyname(pr->icmp)) == NULL) { |
| Fprintf(stderr, "%s: unknown protocol %s\n", prog, pr->icmp); |
| exit(EXIT_FAILURE); |
| } |
| |
| /* privilege bracketing */ |
| (void) __priv_bracket(PRIV_ON); |
| |
| if ((rsock = socket(pr->family, SOCK_RAW, pe->p_proto)) < 0) { |
| Fprintf(stderr, "%s: icmp socket: %s\n", prog, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| if (options & SO_DEBUG) { |
| if (setsockopt(rsock, SOL_SOCKET, SO_DEBUG, (char *)&on, |
| sizeof (on)) < 0) { |
| Fprintf(stderr, "%s: SO_DEBUG: %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } |
| if (options & SO_DONTROUTE) { |
| if (setsockopt(rsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on, |
| sizeof (on)) < 0) { |
| Fprintf(stderr, "%s: SO_DONTROUTE: %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| if (pr->family == AF_INET6) { |
| /* Enable receipt of destination address info */ |
| if (setsockopt(rsock, IPPROTO_IPV6, IPV6_RECVPKTINFO, |
| (char *)&on, sizeof (on)) < 0) { |
| Fprintf(stderr, "%s: IPV6_RECVPKTINFO: %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| /* Enable receipt of hoplimit info */ |
| if (setsockopt(rsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, |
| (char *)&on, sizeof (on)) < 0) { |
| Fprintf(stderr, "%s: IPV6_RECVHOPLIMIT: %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| } |
| |
| /* |
| * Initialize the socket type and protocol based on the address |
| * family, whether or not a raw IP socket is required (for IPv4) |
| * or whether ICMP will be used instead of UDP. |
| * |
| * For historical reasons, the datagrams sent out by |
| * traceroute(1M) do not have the "don't fragment" flag set. For |
| * this reason as well as the ability to set the Loose Source and |
| * Record Route (LSRR) option, a raw IP socket will be used for |
| * IPv4 when run in the global zone. Otherwise, the actual |
| * datagram that will be sent will be a regular UDP or ICMP echo |
| * request packet. However for convenience and for future options |
| * when other IP header information may be specified using |
| * traceroute, the buffer including the raw IP and UDP or ICMP |
| * header is always filled in. When the probe is actually sent, |
| * the size of the request and the start of the packet is set |
| * according to the type of datagram to send. |
| */ |
| if (pr->family == AF_INET && raw_req) { |
| type = SOCK_RAW; |
| proto = IPPROTO_RAW; |
| } else if (useicmp) { |
| type = SOCK_RAW; |
| if (pr->family == AF_INET) |
| proto = IPPROTO_ICMP; |
| else |
| proto = IPPROTO_ICMPV6; |
| } else { |
| type = SOCK_DGRAM; |
| proto = IPPROTO_UDP; |
| } |
| ssock = socket(pr->family, type, proto); |
| |
| if (ssock < 0) { |
| if (proto == IPPROTO_RAW) { |
| Fprintf(stderr, "%s: raw socket: %s\n", prog, |
| strerror(errno)); |
| } else if (proto == IPPROTO_UDP) { |
| Fprintf(stderr, "%s: udp socket: %s\n", prog, |
| strerror(errno)); |
| } else { |
| Fprintf(stderr, "%s: icmp socket: %s\n", prog, |
| strerror(errno)); |
| } |
| exit(EXIT_FAILURE); |
| } |
| |
| if (setsockopt(ssock, SOL_SOCKET, SO_SNDBUF, (char *)&packet_len, |
| sizeof (packet_len)) < 0) { |
| Fprintf(stderr, "%s: SO_SNDBUF: %s\n", prog, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| if (pr->family == AF_INET && raw_req) { |
| if (setsockopt(ssock, IPPROTO_IP, IP_HDRINCL, (char *)&on, |
| sizeof (on)) < 0) { |
| Fprintf(stderr, "%s: IP_HDRINCL: %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| if (options & SO_DEBUG) { |
| if (setsockopt(ssock, SOL_SOCKET, SO_DEBUG, (char *)&on, |
| sizeof (on)) < 0) { |
| Fprintf(stderr, "%s: SO_DEBUG: %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } |
| if (options & SO_DONTROUTE) { |
| if (setsockopt(ssock, SOL_SOCKET, SO_DONTROUTE, |
| (char *)&on, sizeof (on)) < 0) { |
| Fprintf(stderr, "%s: SO_DONTROUTE: %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| /* |
| * If a raw IPv4 packet is going to be sent, the Type of Service |
| * field in the packet will be initialized in set_buffers(). |
| * Otherwise, it is initialized here using the IPPROTO_IP level |
| * socket option. |
| */ |
| if (settos && !raw_req) { |
| int_op = tos; |
| if (setsockopt(ssock, IPPROTO_IP, IP_TOS, (char *)&int_op, |
| sizeof (int_op)) < 0) { |
| Fprintf(stderr, "%s: IP_TOS: %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| } |
| if (pr->family == AF_INET) { |
| rcvsock4 = rsock; |
| sndsock4 = ssock; |
| } else { |
| rcvsock6 = rsock; |
| sndsock6 = ssock; |
| } |
| /* Revert to non-privileged user after configuring sockets */ |
| (void) __priv_bracket(PRIV_OFF); |
| } |
| |
| /* |
| * If we are "probing all", this function calls traceroute() for each IP address |
| * of the target, otherwise calls only once. Returns _B_FALSE if traceroute() |
| * fails. |
| */ |
| static void |
| trace_it(struct addrinfo *ai_dst) |
| { |
| struct msghdr msg6; |
| int num_dst_IPaddrs; |
| struct addrinfo *aip; |
| int i; |
| |
| if (!probe_all) |
| num_dst_IPaddrs = 1; |
| else |
| num_dst_IPaddrs = num_v4 + num_v6; |
| |
| /* |
| * Initialize the msg6 structure using the hoplimit for the first |
| * probe packet, gateway addresses and the outgoing interface index. |
| */ |
| if (ai_dst->ai_family == AF_INET6 || (probe_all && num_v6)) { |
| msg6.msg_control = NULL; |
| msg6.msg_controllen = 0; |
| set_ancillary_data(&msg6, first_ttl, pr6->gwIPlist, gw_count, |
| if_index); |
| } |
| |
| /* run traceroute for all the IP addresses of the multihomed dest */ |
| for (aip = ai_dst, i = 0; i < num_dst_IPaddrs && aip != NULL; i++) { |
| union any_in_addr *addrp; |
| if (aip->ai_family == AF_INET) { |
| addrp = (union any_in_addr *) |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| &((struct sockaddr_in *) |
| aip->ai_addr)->sin_addr; |
| set_sin((struct sockaddr *)pr4->to, addrp, |
| aip->ai_family); |
| traceroute(addrp, &msg6, pr4, num_ifs4, al4); |
| } else { |
| addrp = (union any_in_addr *) |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| &((struct sockaddr_in6 *) |
| aip->ai_addr)->sin6_addr; |
| set_sin((struct sockaddr *)pr6->to, addrp, |
| aip->ai_family); |
| traceroute(addrp, &msg6, pr6, num_ifs6, al6); |
| } |
| aip = aip->ai_next; |
| if (i < (num_dst_IPaddrs - 1)) |
| (void) putchar('\n'); |
| } |
| } |
| |
| /* |
| * set the IP address in a sockaddr struct |
| */ |
| static void |
| set_sin(struct sockaddr *sock, union any_in_addr *addr, int family) |
| { |
| sock->sa_family = family; |
| |
| if (family == AF_INET) |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| ((struct sockaddr_in *)sock)->sin_addr = addr->addr; |
| else |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| ((struct sockaddr_in6 *)sock)->sin6_addr = addr->addr6; |
| } |
| |
| /* |
| * returns the IF name on which the given IP address is configured |
| */ |
| static char * |
| device_name(struct ifaddrlist *al, int len, union any_in_addr *ip_addr, |
| struct pr_set *pr) |
| { |
| int i; |
| struct ifaddrlist *tmp_al; |
| |
| tmp_al = al; |
| |
| for (i = 0; i < len; i++, tmp_al++) { |
| if (memcmp(&tmp_al->addr, ip_addr, pr->addr_len) == 0) { |
| return (tmp_al->device); |
| } |
| } |
| |
| return (NULL); |
| } |
| |
| /* |
| * Trace the route to the host with given IP address. |
| */ |
| static void |
| traceroute(union any_in_addr *ip_addr, struct msghdr *msg6, struct pr_set *pr, |
| int num_ifs, struct ifaddrlist *al) |
| { |
| int ttl; |
| int probe; |
| uchar_t type; /* icmp type */ |
| uchar_t code; /* icmp code */ |
| int reply; |
| int seq = 0; |
| char abuf[INET6_ADDRSTRLEN]; /* use for inet_ntop() */ |
| int longjmp_return; /* return value from longjump */ |
| struct ip *ip = (struct ip *)packet; |
| boolean_t got_there = _B_FALSE; /* we hit the destination */ |
| static boolean_t first_pkt = _B_TRUE; |
| int hoplimit; /* hoplimit for IPv6 packets */ |
| struct in6_addr addr6; |
| int num_src_ifs; /* excludes down and loopback */ |
| struct msghdr in_msg; |
| struct iovec iov; |
| int *intp; |
| int sndsock; |
| int rcvsock; |
| |
| msg6->msg_name = pr->to; |
| msg6->msg_namelen = sizeof (struct sockaddr_in6); |
| sndsock = (pr->family == AF_INET) ? sndsock4 : sndsock6; |
| rcvsock = (pr->family == AF_INET) ? rcvsock4 : rcvsock6; |
| |
| /* carry out the source address selection */ |
| if (pick_src) { |
| union any_in_addr src_addr; |
| char *dev_name; |
| int i; |
| |
| /* |
| * If there's a gateway, a routing header as a consequence, our |
| * kernel picks the source address based on the first hop |
| * address, rather than final destination address. |
| */ |
| if (gw_count > 0) { |
| (void) select_src_addr(pr->gwIPlist, &src_addr, |
| pr->family); |
| } else { |
| (void) select_src_addr(ip_addr, &src_addr, pr->family); |
| } |
| set_sin(pr->from, &src_addr, pr->family); |
| |
| /* filter out down and loopback interfaces */ |
| num_src_ifs = 0; |
| for (i = 0; i < num_ifs; i++) { |
| if (!(al[i].flags & IFF_LOOPBACK) && |
| (al[i].flags & IFF_UP)) |
| num_src_ifs++; |
| } |
| |
| if (num_src_ifs > 1) { |
| dev_name = device_name(al, num_ifs, &src_addr, pr); |
| if (dev_name == NULL) |
| dev_name = "?"; |
| |
| (void) inet_ntop(pr->family, pr->from_sin_addr, abuf, |
| sizeof (abuf)); |
| Fprintf(stderr, |
| "%s: Warning: Multiple interfaces found;" |
| " using %s @ %s\n", prog, abuf, dev_name); |
| } |
| } |
| |
| if (pr->family == AF_INET) { |
| outip4->ip_src = *(struct in_addr *)pr->from_sin_addr; |
| outip4->ip_dst = ip_addr->addr; |
| } |
| |
| /* |
| * If the hostname is an IPv6 literal address, let's not print it twice. |
| */ |
| if (pr->family == AF_INET6 && |
| inet_pton(AF_INET6, hostname, &addr6) > 0) { |
| Fprintf(stderr, "%s to %s", prog, hostname); |
| } else { |
| Fprintf(stderr, "%s to %s (%s)", prog, hostname, |
| inet_ntop(pr->family, ip_addr, abuf, sizeof (abuf))); |
| } |
| |
| if (source) |
| Fprintf(stderr, " from %s", source); |
| Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, |
| pr->packlen); |
| (void) fflush(stderr); |
| |
| /* |
| * Setup the source routing for IPv4. For IPv6, we did the required |
| * setup in the caller function, trace_it(), because it's independent |
| * from the IP address of target. |
| */ |
| if (pr->family == AF_INET && gw_count > 0) |
| set_IPv4opt_sourcerouting(sndsock, ip_addr, pr->gwIPlist); |
| |
| if (probe_all) { |
| /* interrupt handler sig_handler() jumps back to here */ |
| if ((longjmp_return = setjmp(env)) != 0) { |
| switch (longjmp_return) { |
| case SIGINT: |
| Printf("(skipping)\n"); |
| return; |
| case SIGQUIT: |
| Printf("(exiting)\n"); |
| exit(EXIT_SUCCESS); |
| default: /* should never happen */ |
| exit(EXIT_FAILURE); |
| } |
| } |
| (void) signal(SIGINT, sig_handler); |
| } |
| |
| for (ttl = first_ttl; ttl <= max_ttl; ++ttl) { |
| union any_in_addr lastaddr; |
| int timeouts = 0; |
| double rtt; /* for statistics */ |
| int nreceived = 0; |
| double rttmin, rttmax; |
| double rttsum, rttssq; |
| int unreachable; |
| |
| got_there = _B_FALSE; |
| unreachable = 0; |
| |
| /* |
| * The following line clears both IPv4 and IPv6 address stored |
| * in the union. |
| */ |
| lastaddr.addr6 = in6addr_any; |
| |
| if ((ttl == (first_ttl + 1)) && (options & SO_DONTROUTE)) { |
| Fprintf(stderr, |
| "%s: host %s is not on a directly-attached" |
| " network\n", prog, hostname); |
| break; |
| } |
| |
| Printf("%2d ", ttl); |
| (void) fflush(stdout); |
| |
| for (probe = 0; (probe < nprobes) && (timeouts < max_timeout); |
| ++probe) { |
| int cc; |
| struct timeval t1, t2; |
| |
| /* |
| * Put a delay before sending this probe packet. Don't |
| * delay it if it's the very first packet. |
| */ |
| if (!first_pkt) { |
| if (delay.tv_sec > 0) |
| (void) sleep((uint_t)delay.tv_sec); |
| if (delay.tv_usec > 0) |
| (void) usleep(delay.tv_usec); |
| } else { |
| first_pkt = _B_FALSE; |
| } |
| |
| (void) gettimeofday(&t1, NULL); |
| |
| if (pr->family == AF_INET) { |
| send_probe(sndsock, pr->to, outip4, seq, ttl, |
| &t1, pr->packlen); |
| } else { |
| send_probe6(sndsock, msg6, outip6, seq, ttl, |
| &t1, pr->packlen); |
| } |
| |
| /* prepare msghdr for recvmsg() */ |
| in_msg.msg_name = pr->from; |
| in_msg.msg_namelen = pr->sock_size; |
| |
| iov.iov_base = (char *)packet; |
| iov.iov_len = sizeof (packet); |
| |
| in_msg.msg_iov = &iov; |
| in_msg.msg_iovlen = 1; |
| |
| in_msg.msg_control = ancillary_data; |
| in_msg.msg_controllen = sizeof (ancillary_data); |
| |
| while ((cc = wait_for_reply(rcvsock, &in_msg, |
| &t1)) != 0) { |
| (void) gettimeofday(&t2, NULL); |
| |
| reply = (*pr->check_reply_fn) (&in_msg, cc, seq, |
| &type, &code); |
| |
| in_msg.msg_controllen = |
| sizeof (ancillary_data); |
| /* Skip short packet */ |
| if (reply == REPLY_SHORT_PKT) { |
| continue; |
| } |
| |
| timeouts = 0; |
| |
| /* |
| * if reply comes from a different host, print |
| * the hostname |
| */ |
| if (memcmp(pr->from_sin_addr, &lastaddr, |
| pr->addr_len) != 0) { |
| (*pr->print_addr_fn) ((uchar_t *)packet, |
| cc, pr->from); |
| /* store the address response */ |
| (void) memcpy(&lastaddr, |
| pr->from_sin_addr, pr->addr_len); |
| } |
| |
| rtt = deltaT(&t1, &t2); |
| if (collect_stat) { |
| record_stats(rtt, &nreceived, &rttmin, |
| &rttmax, &rttsum, &rttssq); |
| } else { |
| Printf(" %.3f ms", rtt); |
| } |
| |
| if (pr->family == AF_INET6) { |
| intp = find_ancillary_data(&in_msg, |
| IPPROTO_IPV6, IPV6_HOPLIMIT); |
| if (intp == NULL) { |
| Fprintf(stderr, |
| "%s: can't find " |
| "IPV6_HOPLIMIT ancillary " |
| "data\n", prog); |
| exit(EXIT_FAILURE); |
| } |
| hoplimit = *intp; |
| } |
| |
| if (reply == REPLY_GOT_TARGET) { |
| got_there = _B_TRUE; |
| |
| if (((pr->family == AF_INET) && |
| (ip->ip_ttl <= 1)) || |
| ((pr->family == AF_INET6) && |
| (hoplimit <= 1))) |
| Printf(" !"); |
| } |
| |
| if (!collect_stat && showttl) { |
| if (pr->family == AF_INET) { |
| Printf(" (ttl=%d)", |
| (int)ip->ip_ttl); |
| } else if (hoplimit != -1) { |
| Printf(" (hop limit=%d)", |
| hoplimit); |
| } |
| } |
| |
| if (reply == REPLY_GOT_OTHER) { |
| if ((*pr->print_icmp_other_fn) |
| (type, code)) { |
| unreachable++; |
| } |
| } |
| |
| /* special case */ |
| if (pr->family == AF_INET && |
| type == ICMP_UNREACH && |
| code == ICMP_UNREACH_PROTOCOL) |
| got_there = _B_TRUE; |
| |
| break; |
| } |
| |
| seq = (seq + 1) % (MAX_SEQ + 1); |
| |
| if (cc == 0) { |
| Printf(" *"); |
| timeouts++; |
| } |
| |
| (void) fflush(stdout); |
| } |
| |
| if (collect_stat) { |
| print_stats(probe, nreceived, rttmin, rttmax, rttsum, |
| rttssq); |
| } |
| |
| (void) putchar('\n'); |
| |
| /* either we hit the target or received too many unreachables */ |
| if (got_there || |
| (unreachable > 0 && unreachable >= nprobes - 1)) |
| break; |
| } |
| |
| /* Ignore the SIGINT between traceroute() runs */ |
| if (probe_all) |
| (void) signal(SIGINT, SIG_IGN); |
| } |
| |
| /* |
| * for a given destination address and address family, it finds out what |
| * source address kernel is going to pick |
| */ |
| static void |
| select_src_addr(union any_in_addr *dst_addr, union any_in_addr *src_addr, |
| int family) |
| { |
| int tmp_fd; |
| struct sockaddr *sock; |
| struct sockaddr_in *sin; |
| struct sockaddr_in6 *sin6; |
| size_t sock_len; |
| |
| sock = (struct sockaddr *)malloc(sizeof (struct sockaddr_in6)); |
| if (sock == NULL) { |
| Fprintf(stderr, "%s: malloc %s\n", prog, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| (void) bzero(sock, sizeof (struct sockaddr_in6)); |
| |
| if (family == AF_INET) { |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| sin = (struct sockaddr_in *)sock; |
| sin->sin_family = AF_INET; |
| sin->sin_addr = dst_addr->addr; |
| sin->sin_port = IPPORT_ECHO; /* port shouldn't be 0 */ |
| sock_len = sizeof (struct sockaddr_in); |
| } else { |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| sin6 = (struct sockaddr_in6 *)sock; |
| sin6->sin6_family = AF_INET6; |
| sin6->sin6_addr = dst_addr->addr6; |
| sin6->sin6_port = IPPORT_ECHO; /* port shouldn't be 0 */ |
| sock_len = sizeof (struct sockaddr_in6); |
| } |
| |
| /* open a UDP socket */ |
| if ((tmp_fd = socket(family, SOCK_DGRAM, 0)) < 0) { |
| Fprintf(stderr, "%s: udp socket: %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| /* connect it */ |
| if (connect(tmp_fd, sock, sock_len) < 0) { |
| /* |
| * If there's no route to the destination, this connect() call |
| * fails. We just return all-zero (wildcard) as the source |
| * address, so that user can get to see "no route to dest" |
| * message, as it'll try to send the probe packet out and will |
| * receive ICMP unreachable. |
| */ |
| if (family == AF_INET) |
| src_addr->addr.s_addr = INADDR_ANY; |
| else |
| src_addr->addr6 = in6addr_any; |
| free(sock); |
| return; |
| } |
| |
| /* get the local sock info */ |
| if (getsockname(tmp_fd, sock, &sock_len) < 0) { |
| Fprintf(stderr, "%s: getsockname: %s\n", prog, |
| strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| if (family == AF_INET) { |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| sin = (struct sockaddr_in *)sock; |
| src_addr->addr = sin->sin_addr; |
| } else { |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| sin6 = (struct sockaddr_in6 *)sock; |
| src_addr->addr6 = sin6->sin6_addr; |
| } |
| |
| free(sock); |
| (void) close(tmp_fd); |
| } |
| |
| /* |
| * Checksum routine for Internet Protocol family headers (C Version) |
| */ |
| ushort_t |
| in_cksum(ushort_t *addr, int len) |
| { |
| int nleft = len; |
| ushort_t *w = addr; |
| ushort_t answer; |
| int sum = 0; |
| |
| /* |
| * Our algorithm is simple, using a 32 bit accumulator (sum), |
| * we add sequential 16 bit words to it, and at the end, fold |
| * back all the carry bits from the top 16 bits into the lower |
| * 16 bits. |
| */ |
| while (nleft > 1) { |
| sum += *w++; |
| nleft -= 2; |
| } |
| |
| /* mop up an odd byte, if necessary */ |
| if (nleft == 1) |
| sum += *(uchar_t *)w; |
| |
| /* add back carry outs from top 16 bits to low 16 bits */ |
| sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ |
| sum += (sum >> 16); /* add carry */ |
| answer = ~sum; /* truncate to 16 bits */ |
| return (answer); |
| } |
| |
| /* |
| * Wait until a reply arrives or timeout occurs. If packet arrived, read it |
| * return the size of the packet read. |
| */ |
| static int |
| wait_for_reply(int sock, struct msghdr *msg, struct timeval *tp) |
| { |
| fd_set fds; |
| struct timeval now, wait; |
| int cc = 0; |
| int result; |
| |
| (void) FD_ZERO(&fds); |
| FD_SET(sock, &fds); |
| |
| wait.tv_sec = tp->tv_sec + waittime; |
| wait.tv_usec = tp->tv_usec; |
| (void) gettimeofday(&now, NULL); |
| tv_sub(&wait, &now); |
| |
| if (wait.tv_sec < 0 || wait.tv_usec < 0) |
| return (0); |
| |
| result = select(sock + 1, &fds, (fd_set *)NULL, (fd_set *)NULL, &wait); |
| |
| if (result == -1) { |
| if (errno != EINTR) { |
| Fprintf(stderr, "%s: select: %s\n", prog, |
| strerror(errno)); |
| } |
| } else if (result > 0) |
| cc = recvmsg(sock, msg, 0); |
| |
| return (cc); |
| } |
| |
| /* |
| * Construct an Internet address representation. If the nflag has been supplied, |
| * give numeric value, otherwise try for symbolic name. |
| */ |
| char * |
| inet_name(union any_in_addr *in, int family) |
| { |
| char *cp; |
| static boolean_t first = _B_TRUE; |
| static char domain[NI_MAXHOST + 1]; |
| static char line[NI_MAXHOST + 1]; /* assuming */ |
| /* (NI_MAXHOST + 1) >= INET6_ADDRSTRLEN */ |
| char hbuf[NI_MAXHOST]; |
| socklen_t slen; |
| struct sockaddr_in sin; |
| struct sockaddr_in6 sin6; |
| struct sockaddr *sa; |
| int flags; |
| |
| switch (family) { |
| case AF_INET: |
| slen = sizeof (struct sockaddr_in); |
| sin.sin_addr = in->addr; |
| sin.sin_port = 0; |
| sa = (struct sockaddr *)&sin; |
| break; |
| case AF_INET6: |
| slen = sizeof (struct sockaddr_in6); |
| sin6.sin6_addr = in->addr6; |
| sin6.sin6_port = 0; |
| sin6.sin6_scope_id = 0; |
| sa = (struct sockaddr *)&sin6; |
| break; |
| default: |
| (void) snprintf(line, sizeof (line), |
| "<invalid address family>"); |
| return (line); |
| } |
| sa->sa_family = family; |
| |
| if (first && !nflag) { |
| /* find out the domain name */ |
| first = _B_FALSE; |
| if (gethostname(domain, MAXHOSTNAMELEN) == 0 && |
| (cp = strchr(domain, '.')) != NULL) { |
| (void) strncpy(domain, cp + 1, sizeof (domain) - 1); |
| domain[sizeof (domain) - 1] = '\0'; |
| } else { |
| domain[0] = '\0'; |
| } |
| } |
| |
| flags = (nflag) ? NI_NUMERICHOST : NI_NAMEREQD; |
| if (getnameinfo(sa, slen, hbuf, sizeof (hbuf), NULL, 0, flags) != 0) { |
| if (inet_ntop(family, (const void *)&in->addr6, |
| hbuf, sizeof (hbuf)) == NULL) |
| hbuf[0] = 0; |
| } else if (!nflag && (cp = strchr(hbuf, '.')) != NULL && |
| strcmp(cp + 1, domain) == 0) { |
| *cp = '\0'; |
| } |
| (void) strlcpy(line, hbuf, sizeof (line)); |
| |
| return (line); |
| } |
| |
| /* |
| * return the difference (in msec) between two time values |
| */ |
| static double |
| deltaT(struct timeval *t1p, struct timeval *t2p) |
| { |
| double dt; |
| |
| dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 + |
| (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0; |
| return (dt); |
| } |
| |
| /* |
| * Subtract 2 timeval structs: out = out - in. |
| * Out is assumed to be >= in. |
| */ |
| static void |
| tv_sub(struct timeval *out, struct timeval *in) |
| { |
| if ((out->tv_usec -= in->tv_usec) < 0) { |
| --out->tv_sec; |
| out->tv_usec += 1000000; |
| } |
| out->tv_sec -= in->tv_sec; |
| } |
| |
| /* |
| * record statistics |
| */ |
| static void |
| record_stats(double rtt, int *nreceived, double *rttmin, double *rttmax, |
| double *rttsum, double *rttssq) |
| { |
| if (*nreceived == 0) { |
| *rttmin = rtt; |
| *rttmax = rtt; |
| *rttsum = rtt; |
| *rttssq = rtt * rtt; |
| } else { |
| if (rtt < *rttmin) |
| *rttmin = rtt; |
| |
| if (rtt > *rttmax) |
| *rttmax = rtt; |
| |
| *rttsum += rtt; |
| *rttssq += rtt * rtt; |
| } |
| |
| (*nreceived)++; |
| } |
| |
| /* |
| * display statistics |
| */ |
| static void |
| print_stats(int ntransmitted, int nreceived, double rttmin, double rttmax, |
| double rttsum, double rttssq) |
| { |
| double rttavg; /* average round-trip time */ |
| double rttstd; /* rtt standard deviation */ |
| |
| if (ntransmitted > 0 && ntransmitted >= nreceived) { |
| int missed = ntransmitted - nreceived; |
| double loss = 100 * (double)missed / (double)ntransmitted; |
| |
| if (nreceived > 0) { |
| rttavg = rttsum / nreceived; |
| rttstd = rttssq - (rttavg * rttsum); |
| rttstd = xsqrt(rttstd / nreceived); |
| |
| Printf(" %.3f", rttmin); |
| Printf("/%.3f", rttavg); |
| Printf("/%.3f", rttmax); |
| |
| Printf(" (%.3f) ms ", rttstd); |
| } |
| |
| Printf(" %d/%d pkts", nreceived, ntransmitted); |
| |
| if (nreceived == 0) |
| Printf(" (100%% loss)"); |
| else |
| Printf(" (%.2g%% loss)", loss); |
| } |
| } |
| |
| /* |
| * square root function |
| */ |
| double |
| xsqrt(double y) |
| { |
| double t, x; |
| |
| if (y <= 0) { |
| return (0.0); |
| } |
| |
| x = (y < 1.0) ? 1.0 : y; |
| do { |
| t = x; |
| x = (t + (y/t))/2.0; |
| } while (0 < x && x < t); |
| |
| return (x); |
| } |
| |
| /* |
| * String to double with optional min and max. |
| */ |
| static double |
| str2dbl(const char *str, const char *what, double mi, double ma) |
| { |
| double val; |
| char *ep; |
| |
| errno = 0; |
| |
| val = strtod(str, &ep); |
| if (errno != 0 || *ep != '\0') { |
| Fprintf(stderr, "%s: \"%s\" bad value for %s \n", |
| prog, str, what); |
| exit(EXIT_FAILURE); |
| } |
| if (val < mi && mi >= 0) { |
| Fprintf(stderr, "%s: %s must be >= %f\n", prog, what, mi); |
| exit(EXIT_FAILURE); |
| } |
| if (val > ma && ma >= 0) { |
| Fprintf(stderr, "%s: %s must be <= %f\n", prog, what, ma); |
| exit(EXIT_FAILURE); |
| } |
| return (val); |
| } |
| |
| /* |
| * String to int with optional min and max. Handles decimal and hex. |
| */ |
| static int |
| str2int(const char *str, const char *what, int mi, int ma) |
| { |
| const char *cp; |
| int val; |
| char *ep; |
| |
| errno = 0; |
| |
| if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { |
| cp = str + 2; |
| val = (int)strtol(cp, &ep, 16); |
| } else { |
| val = (int)strtol(str, &ep, 10); |
| } |
| if (errno != 0 || *ep != '\0') { |
| Fprintf(stderr, "%s: \"%s\" bad value for %s \n", |
| prog, str, what); |
| exit(EXIT_FAILURE); |
| } |
| if (val < mi && mi >= 0) { |
| if (mi == 0) { |
| Fprintf(stderr, "%s: %s must be >= %d\n", |
| prog, what, mi); |
| } else { |
| Fprintf(stderr, "%s: %s must be > %d\n", |
| prog, what, mi - 1); |
| } |
| exit(EXIT_FAILURE); |
| } |
| if (val > ma && ma >= 0) { |
| Fprintf(stderr, "%s: %s must be <= %d\n", prog, what, ma); |
| exit(EXIT_FAILURE); |
| } |
| return (val); |
| } |
| |
| /* |
| * This is the interrupt handler for SIGINT and SIGQUIT. It's completely handled |
| * where it jumps to. |
| */ |
| static void |
| sig_handler(int sig) |
| { |
| longjmp(env, sig); |
| } |
| |
| /* |
| * display the usage of traceroute |
| */ |
| static void |
| usage(void) |
| { |
| Fprintf(stderr, "Usage: %s [-adFIlnSvx] [-A address_family] " |
| "[-c traffic_class]\n" |
| "\t[-f first_hop] [-g gateway [-g gateway ...]| -r] [-i iface]\n" |
| "\t[-L flow_label] [-m max_hop] [-P pause_sec] [-p port] " |
| "[-Q max_timeout]\n" |
| "\t[-q nqueries] [-s src_addr] [-t tos] [-w wait_time] host " |
| "[packetlen]\n", prog); |
| exit(EXIT_FAILURE); |
| } |