From cbc33ec0197ec547668b7908ea554ef1d9a8e55a Mon Sep 17 00:00:00 2001 From: Aleksandr Lysikov <lysikov@inbox.ru> Date: Tue, 18 Dec 2018 15:50:30 +0500 Subject: [PATCH] traceroute --- CMakeLists.txt | 4 +- iputils/iputils.h | 1 + iputils/traceroute/as_lookups.c | 128 +++ iputils/traceroute/clif.c | 1259 ++++++++++++++++++++++ iputils/traceroute/clif.h | 121 +++ iputils/traceroute/csum.c | 34 + iputils/traceroute/extension.c | 132 +++ iputils/traceroute/flowlabel.h | 40 + iputils/traceroute/mod-dccp.c | 294 +++++ iputils/traceroute/mod-icmp.c | 253 +++++ iputils/traceroute/mod-raw.c | 168 +++ iputils/traceroute/mod-tcp.c | 512 +++++++++ iputils/traceroute/mod-tcpconn.c | 233 ++++ iputils/traceroute/mod-udp.c | 239 +++++ iputils/traceroute/module.c | 51 + iputils/traceroute/poll.c | 93 ++ iputils/traceroute/random.c | 28 + iputils/traceroute/time.c | 27 + iputils/traceroute/traceroute.c | 1731 ++++++++++++++++++++++++++++++ iputils/traceroute/traceroute.h | 107 ++ iputils/traceroute6.c | 981 +++++++++++++++++ 21 files changed, 6434 insertions(+), 2 deletions(-) create mode 100755 iputils/traceroute/as_lookups.c create mode 100755 iputils/traceroute/clif.c create mode 100755 iputils/traceroute/clif.h create mode 100755 iputils/traceroute/csum.c create mode 100755 iputils/traceroute/extension.c create mode 100755 iputils/traceroute/flowlabel.h create mode 100755 iputils/traceroute/mod-dccp.c create mode 100755 iputils/traceroute/mod-icmp.c create mode 100755 iputils/traceroute/mod-raw.c create mode 100755 iputils/traceroute/mod-tcp.c create mode 100755 iputils/traceroute/mod-tcpconn.c create mode 100755 iputils/traceroute/mod-udp.c create mode 100755 iputils/traceroute/module.c create mode 100755 iputils/traceroute/poll.c create mode 100755 iputils/traceroute/random.c create mode 100755 iputils/traceroute/time.c create mode 100755 iputils/traceroute/traceroute.c create mode 100755 iputils/traceroute/traceroute.h create mode 100755 iputils/traceroute6.c diff --git a/CMakeLists.txt b/CMakeLists.txt index c6d152e315..35b070543c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,8 +13,8 @@ set(DAP_CHAIN_NET_HEADERS dap_chain_node_ctl.h ) -file(GLOB IPUTILS_SRCS iputils/*.c) -file(GLOB IPUTILS_HEADERS iputils/*.h) +file(GLOB IPUTILS_SRCS iputils/*.c iputils/traceroute/*.c) +file(GLOB IPUTILS_HEADERS iputils/*.h iputils/traceroute/*.h) add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_NET_SRCS} ${DAP_CHAIN_NET_HEADERS} ${IPUTILS_SRCS} ${IPUTILS_HEADERS}) diff --git a/iputils/iputils.h b/iputils/iputils.h index 614979c1a5..3929172c01 100644 --- a/iputils/iputils.h +++ b/iputils/iputils.h @@ -35,6 +35,7 @@ int ping_util6(const char *addr, int count); * */ int tracepath_util(const char *addr, int *hops, int *time_usec); +int traceroute_util(const char *addr, int *hops, int *time_usec); void iputils_set_verbose(void); void iputils_reset_verbose(void); diff --git a/iputils/traceroute/as_lookups.c b/iputils/traceroute/as_lookups.c new file mode 100755 index 0000000000..aedf7e1f2d --- /dev/null +++ b/iputils/traceroute/as_lookups.c @@ -0,0 +1,128 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#include "traceroute.h" + + +#define DEF_RADB_SERVER "whois.radb.net" +#define DEF_RADB_SERVICE "nicname" + + +static sockaddr_any ra_addr = {{ 0, }, }; +static char ra_buf[512] = { 0, }; + + +const char *get_as_path (const char *query) { + int sk, n; + FILE *fp; + char buf[1024]; + int prefix = 0, best_prefix = 0; + char *rb, *re = &ra_buf[sizeof (ra_buf) / sizeof (*ra_buf) - 1]; + + + if (!ra_addr.sa.sa_family) { + const char *server, *service; + struct addrinfo *res; + int ret; + + server = getenv ("RA_SERVER"); + if (!server) server = DEF_RADB_SERVER; + + service = getenv ("RA_SERVICE"); + if (!service) service = DEF_RADB_SERVICE; + + + ret = getaddrinfo (server, service, NULL, &res); + if (ret) { + fprintf (stderr, "%s/%s: %s\n", server, service, + gai_strerror(ret)); + exit (2); + } + + memcpy (&ra_addr, res->ai_addr, res->ai_addrlen); + + freeaddrinfo (res); + } + + + sk = socket (ra_addr.sa.sa_family, SOCK_STREAM, 0); + if (sk < 0) error ("socket"); + + if (connect (sk, &ra_addr.sa, sizeof (ra_addr)) < 0) + goto err_sk; + + n = snprintf (buf, sizeof (buf), "%s\r\n", query); + if (n >= sizeof (buf)) goto err_sk; + + if (write (sk, buf, n) < n) + goto err_sk; + + fp = fdopen (sk, "r"); + if (!fp) goto err_sk; + + + strcpy (ra_buf, "*"); + rb = ra_buf; + + while (fgets (buf, sizeof (buf), fp) != NULL) { + + if (!strncmp (buf, "route:", sizeof ("route:") - 1) || + !strncmp (buf, "route6:", sizeof ("route6:") - 1) + ) { + char *p = strchr (buf, '/'); + + if (p) prefix = strtoul (++p, NULL, 10); + else prefix = 0; /* Hmmm... */ + + } + else if (!strncmp (buf, "origin:", sizeof ("origin:") -1)) { + char *p, *as; + + p = buf + (sizeof ("origin:") - 1); + + while (isspace (*p)) p++; + as = p; + while (*p && !isspace (*p)) p++; + *p = '\0'; + + if (prefix > best_prefix) { + best_prefix = prefix; + + rb = ra_buf; + while (rb < re && (*rb++ = *as++)) ; + } + else if (prefix == best_prefix) { + char *q = strstr (ra_buf, as); + + if (!q || (*(q += strlen (as)) != '\0' && *q != '/')) { + if (rb > ra_buf) rb[-1] = '/'; + while (rb < re && (*rb++ = *as++)) ; + } + } + /* else just ignore it */ + } + } + + fclose (fp); + + return ra_buf; + + +err_sk: + close (sk); + return "!!"; +} diff --git a/iputils/traceroute/clif.c b/iputils/traceroute/clif.c new file mode 100755 index 0000000000..4ef20e454c --- /dev/null +++ b/iputils/traceroute/clif.c @@ -0,0 +1,1259 @@ +/* + Copyright (c) 2000, 2003 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: LGPL v2.1 or any later + + See COPYING.LIB for the status of this software. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> + +#include "clif.h" + + +#if 1 /* Bad idea, anyway... */ +#define MAX_ARGC_NUMBER 256 +typedef unsigned char _CLIF_index; +#else +#define MAX_ARGC_NUMBER (4096 / 5 + 1) /* POSIX ARG_MAX >= 4096 ... */ +typedef unsigned short _CLIF_index; +#endif + + +/* This is needed for some print info functions. + This is ugly for thread-safe (is it really actual on program invoking?), + and for several CLIF_parse_cmdline invoking... But foo on this. Yeah... +*/ +static struct { + int argc; + char **argv; + CLIF_option *option_list; + CLIF_argument *argument_list; + unsigned int parse_flags; +} curr = { 0, }; + + +static void err_report (const char *format, ...) { + va_list ap; + + if (curr.parse_flags & CLIF_SILENT) + return; + + va_start (ap, format); + + vfprintf (stderr, format, ap); + + va_end (ap); + + fprintf (stderr, "\n"); + + return; +} + + +/* info generation stuff... */ + +#define SHORT_PLUS_MINUS "+/-" +#define LONG_PLUS_MINUS "++/--" +#define EXCL_DLM " | " + +static char *show_short (const CLIF_option *optn) { + static char buf[80]; + char *p = buf; + unsigned int flags = optn->flags | curr.parse_flags; + + if (optn->function_plus) { + if (!optn->function) *p++ = '+'; + else { + strcpy (p, SHORT_PLUS_MINUS); + p += sizeof (SHORT_PLUS_MINUS) - 1; + } + } else + *p++ = '-'; + + *p++ = optn->short_opt[0]; + + if (optn->arg_name) { + char *endp = buf + sizeof (buf) - sizeof (",...]"); + const char *s; + + if (!(flags & _CLIF_STRICT_JOIN_ARG)) *p++ = ' '; + if (flags & CLIF_OPTARG) *p++ = '['; + + s = optn->arg_name; + while (*s && p < endp) *p++ = *s++; + + if (flags & CLIF_SEVERAL) { + strcpy (p, ",..."); + p += sizeof (",...") - 1; /* last '\0' ... */ + } + + if (flags & CLIF_OPTARG) *p++ = ']'; + } + + *p = '\0'; + + return buf; +} + +static char *show_long (const CLIF_option *optn) { + static char buf[80]; + char *p = buf; + char *endp; + const char *s; + unsigned int flags = optn->flags | curr.parse_flags; + + + if (!(flags & _CLIF_STRICT_KEYWORD)) { + + if (!(flags & _CLIF_STRICT_ONEDASH)) { + if (optn->function_plus) { + if (!optn->function) { *p++ = '+'; *p++ = '+'; } + else { + strcpy (p, LONG_PLUS_MINUS); + p += sizeof (LONG_PLUS_MINUS) - 1; + } + } else { *p++ = '-'; *p++ = '-'; } + + } else { + if (optn->function_plus) { + if (!optn->function) *p++ = '+'; + else { + strcpy (p, SHORT_PLUS_MINUS); + p += sizeof (SHORT_PLUS_MINUS) - 1; + } + } else *p++ = '-'; + } + } + + s = optn->long_opt; + endp = buf + sizeof (buf) - sizeof (" ["); + while (*s && p < endp) *p++ = *s++; + + if (optn->arg_name) { + + if (flags & _CLIF_STRICT_NOEQUAL) { + *p++ = ' '; + if (flags & CLIF_OPTARG) *p++ = '['; + } else { + if (flags & CLIF_OPTARG) *p++ = '['; + *p++ = '='; + } + + s = optn->arg_name; + endp = buf + sizeof (buf) - sizeof (",...]"); + while (*s && p < endp) *p++ = *s++; + + if (flags & CLIF_SEVERAL) { + strcpy (p, ",..."); + p += sizeof (",...") - 1; /* last '\0' ... */ + } + + if (flags & CLIF_OPTARG) *p++ = ']'; + } + + *p = '\0'; + + return buf; +} + +static char *show_excl (const CLIF_option *option_list, int *cnt_p) { + static char buf[256]; + const CLIF_option *optn; + char *p = buf; + char *endp = buf + sizeof (buf) - sizeof (EXCL_DLM); + int excl_cnt = 0; + + *p = '\0'; + if (cnt_p) *cnt_p = 0; + if (!option_list) return buf; + + for (optn = option_list; optn->short_opt || optn->long_opt; optn++) { + char *s; + + if (!(optn->flags & CLIF_EXCL)) continue; + + if (optn->short_opt) s = show_short (optn); + else s = show_long (optn); + + if (excl_cnt > 0) { /* i.e., second etc... */ + strcpy (p, EXCL_DLM); + p += sizeof (EXCL_DLM) - 1; + } + + while (*s && p < endp) *p++ = *s++; + + excl_cnt++; + } + + *p = '\0'; + + if (cnt_p) *cnt_p = excl_cnt; + + return buf; +} + + +static int is_keyword (const CLIF_option *optn) { + unsigned int flags = optn->flags | curr.parse_flags; + + return (flags & _CLIF_STRICT_KEYWORD) != 0; +} + + +static void err_bad_opt (const char *arg, char c, int n) { + char sym = (*arg == '+') ? '+' : '-'; + + if (c) err_report ("Bad option `%c%c' (argc %d)", sym, c, n); + else { + char *p = strchr (arg, '='); + const char *type = (*arg == sym) ? "option" : "keyword"; + + if (p) + err_report ("Bad %s `%s' (with arg `%s') (argc %d)", + type, arg, p + 1, n); + else + err_report ("Bad %s `%s' (argc %d)", type, arg, n); + } +} + +static void err_bad_arg (const CLIF_option *optn, char c, int n) { + CLIF_option tmp = *optn; + char ss[80]; + char *s; + + tmp.arg_name = NULL; + + if (c) { + s = show_short (&tmp); /* always without arg... */ + strncpy (ss, s, sizeof (ss)); + s = show_short (optn); + } else { + s = show_long (&tmp); /* always without arg... */ + strncpy (ss, s, sizeof (ss)); + s = show_long (optn); + } + + err_report ("%s `%s' (argc %d) requires an argument: `%s'", + (c || !is_keyword (optn)) ? "Option" : "Keyword", ss, n, s); +} + +static void err_bad_res (const CLIF_option *optn, char c, + const char *opt_arg, int n) { + CLIF_option tmp = *optn; + char *ss; + const char *type; + + tmp.arg_name = NULL; + + if (c) { + ss = show_short (&tmp); + type = "option"; + } else { + ss = show_long (&tmp); + type = is_keyword (optn) ? "keyword" : "option"; + } + + if (optn->arg_name) + err_report ("Cannot handle `%s' %s with arg `%s' (argc %d)", + ss, type, opt_arg, n); + else + err_report ("Cannot handle `%s' %s (argc %d)", ss, type, n); +} + +static void err_bad_excl (const CLIF_option *optn, char c, int n) { + CLIF_option tmp = *optn; + char *ss; + char *excl = show_excl (curr.option_list, 0); + /* Note: show_(short|long)() nested!!! */ + + tmp.arg_name = NULL; + + if (c) ss = show_short (&tmp); + else ss = show_long (&tmp); + + err_report ("%s `%s' (argc %d): Only one of:\n %s\n" + "may be specified.", + (c || !is_keyword (optn)) ? "Option" : "Keyword", + ss, n, excl); +} + + +static CLIF_option *find_long (char *arg, char **arg_p, + unsigned int match, unsigned int nomatch) { + CLIF_option *optn; + CLIF_option *abbrev = NULL; + char *abbrev_arg = NULL; + int abbrev_found = 0; + + + for (optn = curr.option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + char *a; + const char *o; + unsigned int flags; + + if (!optn->long_opt) continue; + + flags = curr.parse_flags | optn->flags; + if (flags & nomatch) continue; + if (match && !(flags & match)) continue; /* XXX: optimize it */ + + + for (a = arg, o = optn->long_opt; *o && *a == *o; a++, o++) ; + + if (*a == '\0' || + (*a == '=' && optn->arg_name && !(flags & _CLIF_STRICT_NOEQUAL)) + ) { /* looks like end of option... */ + + if (!*o) { /* explicit match found */ + if (*a == '=' && arg_p) *arg_p = a + 1; + return optn; + } + + if ((flags & CLIF_ABBREV) && + (a - arg >= CLIF_MIN_ABBREV) + ) { + if (!abbrev_found) { + abbrev_found = 1; + abbrev = optn; + if (*a == '=') abbrev_arg = a + 1; + } else /* several possibility case... */ + abbrev = NULL; + } + } + } + + if (abbrev) { /* implicit match found */ + if (abbrev_arg && arg_p) *arg_p = abbrev_arg; + return abbrev; + } else /* no match found */ + return NULL; +} + +static int check_sym (const CLIF_option *optn, char sym) { + + if (sym == '+') { + if (!optn->function_plus) return -1; + } + else if (sym == '-') { + if (!optn->function && optn->function_plus) + return -1; + } + + return 0; +} + +static int call_function (CLIF_option *optn, char *opt_arg, char sym) { + int (*function) (CLIF_option *, char *); + + function = (sym == '+') ? optn->function_plus : optn->function; + + if (!function) return 0; + + if (opt_arg && ((optn->flags | curr.parse_flags) & CLIF_SEVERAL)) { + char tmp[80]; + char *t; + char *endt = tmp + sizeof (tmp); + + while (*opt_arg) { + + t = tmp; + while (t < endt && *opt_arg && + *opt_arg != ' ' && *opt_arg != '\t' && *opt_arg != ',' + ) *t++ = *opt_arg++; + + if (t >= endt) return -1; + + *t = '\0'; + + if (function (optn, tmp) < 0) return -1; + + while (*opt_arg == ' ' || *opt_arg == '\t' || *opt_arg == ',') + opt_arg++; + } + + return 0; + } + + return function (optn, opt_arg); +} + + +int CLIF_parse_cmdline (int argc, char *argv[], + CLIF_option *option_list, + CLIF_argument *argument_list, + unsigned int parse_flags) { + int i, j; + CLIF_option *optn; + CLIF_argument *argm; + int num_args = 0; + int num_argm = 0, strict_beg = 0, strict_end = 0; + _CLIF_index arg_n[MAX_ARGC_NUMBER]; + unsigned int dirty_flags = 0; + int dirty_plus = 0; + int exclusive_cnt = 0; + int posix = getenv ("POSIXLY_CORRECT") != NULL || + (parse_flags & CLIF_POSIX); + + curr.argc = argc; + curr.argv = argv; + curr.option_list = option_list; + curr.argument_list = argument_list; + curr.parse_flags = parse_flags; + + if (argc <= 1 && (parse_flags & CLIF_HELP_EMPTY)) { + CLIF_current_help (); + exit (0); + } + + /* Scan argument_list for check and some info. */ + + if (argument_list) { + enum stages { STRICT_BEG, OPTIONAL, STRICT_END }; + int stage = STRICT_BEG; + + for (argm = argument_list; argm->name; argm++) { + + if (argm->flags & CLIF_STRICT) { + + if (stage == STRICT_BEG) strict_beg++; + else if (stage == OPTIONAL) { + stage = STRICT_END; + strict_end++; + } + else if (stage == STRICT_END) + strict_end++; + } else { + if (stage == STRICT_BEG) stage = OPTIONAL; + else if (stage == STRICT_END) { + err_report ("Incorrect argument list set in program " + "source: more than one optional area."); + return -1; + } + } + + num_argm++; + } + } + + /* Scan option_list for some info. */ + if (option_list) { + + dirty_flags = parse_flags; + + for (optn = option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + dirty_flags |= optn->flags; + if (optn->function_plus) dirty_plus = 1; + } + } + + if (dirty_flags & CLIF_EXCL) + exclusive_cnt = 1; /* only one is allowed... */ + + + /* Go ! Store arguments, parse options. */ + + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + char *opt_arg = NULL; + char sym = '-'; + + if (!option_list) + goto handle_arg; + + if (*arg == '+' && dirty_plus) + sym = '+'; + + if (*arg != sym) { /* argument or keyword */ + + if (dirty_flags & CLIF_MAY_KEYWORD) { + optn = find_long (arg, &opt_arg, CLIF_MAY_KEYWORD, 0); + if (optn) goto long_found; + } + + if (num_args == 0 && (parse_flags & CLIF_FIRST_GROUP)) { + /* ugly... */ + parse_flags &= ~CLIF_FIRST_GROUP; + dirty_flags &= ~CLIF_FIRST_GROUP; /* to be correct */ + + goto handle_short; + } + + /* else it is an argument */ + goto handle_arg; + + } + else if (*++arg == sym) { /* `--' - long option */ + arg++; + + if (*arg == sym || /* `---' - let it be not option... */ + (parse_flags & (_CLIF_STRICT_KEYWORD|_CLIF_STRICT_ONEDASH)) + ) { + arg -= 2; + goto handle_arg; /* not option anyway */ + } + + optn = find_long (arg, &opt_arg, 0, + _CLIF_STRICT_KEYWORD | _CLIF_STRICT_ONEDASH); + if (optn) goto long_found; + + /* XXX: May be allow only for `--', not `++' too... */ + if (!*arg && sym == '-') { /* `--' and no empty longoption */ + option_list = NULL; /* POSIX way... */ + continue; + } + + /* XXX: or treat as an argument sometimes??? */ + err_bad_opt (argv[i], 0, i); + return -1; + } + else { /* short option, or several short options... */ + + if (dirty_flags & CLIF_MAY_ONEDASH) { + optn = find_long (arg, &opt_arg, CLIF_MAY_ONEDASH, 0); + if (optn) goto long_found; + } + + if (!*arg) { /* POSIX say: only "stdout specification"... */ + arg--; + goto handle_arg; + } + + goto handle_short; + } + + + long_found: + if (check_sym (optn, sym) < 0) { /* Oops... */ + err_bad_opt (argv[i], 0, i); + return -1; + } + + if (optn->flags & CLIF_EXCL) { + if (!exclusive_cnt) { + err_bad_excl (optn, 0, i); + return -1; + } + exclusive_cnt--; + } + + if (optn->arg_name && !opt_arg) { + unsigned int flags = optn->flags | parse_flags; + + if (++i >= argc || + !(flags & CLIF_MAY_NOEQUAL) + ) { /* missing opt arg */ + i--; + + if (!(flags & CLIF_OPTARG)) { + err_bad_arg (optn, 0, i); + return -1; + } + + opt_arg = NULL; + } else + opt_arg = argv[i]; + + } + + + if (call_function (optn, opt_arg, sym) < 0) { + err_bad_res (optn, 0, opt_arg, i); + return -1; + } + + if (optn->flags & CLIF_EXIT) + exit (0); + + continue; + + + handle_arg: + if (argument_list) { + if (i < MAX_ARGC_NUMBER) /* XXX: ugly, better report */ + arg_n[num_args++] = i; + } else { + err_report ("`%s' (argc %d): arguments are not allowed", + argv[i], i); + return -1; + } + + /* POSIX say: No more options after args... */ + if (posix) option_list = NULL; /* geniously... */ + + continue; + + + handle_short: + + opt_arg = NULL; + + do { + + for (optn = option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + if (optn->short_opt && optn->short_opt[0] == *arg) + break; + } + if (!optn->short_opt || + check_sym (optn, sym) < 0 + ) { + err_bad_opt (argv[i], *arg, i); + return -1; + } + + if (optn->flags & CLIF_EXCL) { + if (!exclusive_cnt) { + err_bad_excl (optn, *arg, i); + return -1; + } + exclusive_cnt--; + } + + + if (optn->arg_name) { + unsigned int flags = parse_flags | optn->flags; + + if (arg[1] == '\0') { /* a last one */ + + /* POSIX say: an option with arg cannot be grouped. */ + if (posix && arg != argv[i] && arg[-1] != sym) { + err_bad_arg (optn, *arg, i); /* good way? */ + return -1; + } + + if (++i >= argc || + (flags & _CLIF_STRICT_JOIN_ARG) + ) { + i--; + + if (!(flags & CLIF_OPTARG)) { + err_bad_arg (optn, *arg, i); + return -1; + } + + opt_arg = NULL; + } else + opt_arg = argv[i]; + } + else if ((arg == argv[i] || arg[-1] == sym) && + (flags & CLIF_MAY_JOIN_ARG) + ) { + opt_arg = ++arg; + } + else { /* inside a group... */ + if (!(flags & CLIF_OPTARG) || + (flags & CLIF_MAY_JOIN_ARG) + ) { + err_bad_arg (optn, *arg, i); + return -1; + } + + opt_arg = NULL; + } + } + + if (call_function (optn, opt_arg, sym) < 0) { + err_bad_res (optn, optn->short_opt[0], opt_arg, i); + return -1; + } + + if (optn->flags & CLIF_EXIT) + exit (0); + + } while (!opt_arg && *++arg); + + } /* for ( ... ) */ + + + if ((parse_flags & CLIF_STRICT_EXCL) && exclusive_cnt != 0) { + err_report ("One of these must be specified:\n %s\n", + show_excl (option_list, 0)); + return -1; + } + + + /* Now, after *ALL* options, handle arguments, if any. */ + + if (num_args < strict_beg + strict_end) { + /* Missing some needed arguments. */ + + if (num_args < strict_beg) argm = argument_list + num_args; + else + argm = argument_list + + ((num_args - strict_beg) + (num_argm - strict_end)); + + if (num_args == strict_beg + strict_end - 1) + err_report ("Specify \"%s\" missing argument.", argm->name); + else + err_report ("Specify \"%s\" and other missing arguments.", + argm->name); + return -1; + } + + if (num_args > 0) { + _CLIF_index argm_index[MAX_ARGC_NUMBER]; + + /* assing argm (by index) for each arg... */ + + for (i = 0, j = 0; i < strict_beg; i++, j++) + argm_index[i] = j; + for (i = num_args - strict_end, j = num_argm - strict_end; + i < num_args; i++, j++ + ) argm_index[i] = j; + for (i = strict_beg, j = strict_beg; + i < num_args - strict_end && j < num_argm - strict_end; + i++ + ) { + argm_index[i] = j; + if (!(argument_list[j].flags & CLIF_MORE)) + j++; + } + + if (i < num_args - strict_end) { /* there are extra args... */ + err_report ("Extra arg `%s' (position %d, argc %d)", + argv[arg_n[i]], i + 1, arg_n[i]); + return -1; + } + + if (j < num_argm - strict_end && + !(argument_list[j].flags & CLIF_MORE) && + /* ...i.e, there are some missing optional args... */ + (argument_list[j].flags & CLIF_ACC_PREV) + ) { + if (j == 0) + err_report ("Incorrect argument list set: first arg " + "cannot be `accompanied with previous'."); + else + err_report ("Arg \"%s\" must be specified because " + "\"%s\" `%s' is used.", argument_list[j].name, + argument_list[j - 1].name, argv[arg_n[i - 1]]); + return -1; + } + + if (argm_index[--i] == j && + /* above is true only after OPTIONAL area scan + and when `j' is stopped on CLIF_MORE */ + ++j < num_argm - strict_end + /* i.e: there is a *last* one (after CLIF_MORE) + in the OPTIONAL area */ + ) argm_index[i] = j; /* *last* is better than *more* */ + + + /* ...and work now */ + + for (i = 0; i < num_args; i++) { + argm = argument_list + argm_index[i]; + + if (argm->function && + argm->function (argm, argv[arg_n[i]], i) < 0 + ) { + err_report ("Cannot handle \"%s\" cmdline arg `%s' " + "on position %d (argc %d)", + argm->name, argv[arg_n[i]], i + 1, arg_n[i]); + return -1; + } + } + + /* That`s all. */ + } + + return 0; +} + + +static void box_output (int start, int left, int width, const char *str, + const char *arg_name) { + char *p, *endp, *s; + int l; + char buf[1024]; + char spacer[128]; /* assume it is enough */ + + if (left > sizeof (spacer) - 2) left = sizeof (spacer) - 2; + if (width > sizeof (buf) - 1) width = sizeof (buf) - 1; + + spacer[0] = '\n'; + memset (spacer + 1, ' ', left); + spacer[left + 1] = '\0'; + + + l = left - start; + if (l > 0) { + memset (buf, ' ', l); + buf[l] = '\0'; + fprintf (stderr, "%s", buf); + } else + fprintf (stderr, "%s", spacer); + + + endp = buf + width; + + p = buf; + + while (*str) { + + while (*str && p < endp) { + + if (*str == '%' && arg_name) { + if (str[1] == '%') { + *p++ = '%'; + str += 2; + continue; + } + else if (str[1] == 's') { + const char *a = arg_name; + + while (*a && p < endp) *p++ = *a++; + str += 2; + continue; + } + } + + *p++ = *str++; + } + + *p = '\0'; + + if (p < endp) break; + + + while (p > buf && *p != ' ' && *p != '\t') p--; + if (p <= buf) return; /* foo on you */ + + *p = '\0'; + fprintf (stderr, "%s", buf); + fprintf (stderr, "%s", spacer); + + p++; + for (s = buf; *p; *s++ = *p++) ; + *s = '\0'; + p = s; + } + + + fprintf (stderr, "%s", buf); + + return; +} + + +#define SHORT_LONG_DLM " " +#define OPT_START_DLM " " +#define OPT_FIELD_WIDTH 30 + +#define ARG_MARK_STRICT "+ " +#define ARG_MARK_GROUP0 " . " +#define ARG_MARK_GROUP " ' " +#define ARG_MARK_OPT " " +#define ARG_FIELD_WIDTH 20 + +#define SCREEN_WIDTH 80 + + +void CLIF_print_options (const char *header, + const CLIF_option *option_list) { + const CLIF_option *optn; + char *excl; + int excl_cnt = 0; + + /* Print a header string, if present... */ + if (header) fprintf (stderr, "%s\n", header); + + if (!option_list) return; + + + for (optn = option_list; optn->short_opt || optn->long_opt; optn++) { + int len; + + /* generate and print an option usage */ + + if (optn->short_opt) { + if (optn->long_opt) + len = fprintf (stderr, OPT_START_DLM "%s" + SHORT_LONG_DLM "%s", + show_short (optn), show_long (optn)); + else + len = fprintf (stderr, OPT_START_DLM "%s", + show_short (optn)); + } else + len = fprintf (stderr, OPT_START_DLM "%s", show_long (optn)); + + + /* print a help string, if present */ + + if (optn->help_string) + box_output (len, OPT_FIELD_WIDTH, + SCREEN_WIDTH - OPT_FIELD_WIDTH, + optn->help_string, optn->arg_name); + + fprintf (stderr, "\n"); /* a last one */ + } + + excl = show_excl (option_list, &excl_cnt); + if (excl_cnt > 0) { + + if (excl_cnt == 1) { + if ((curr.parse_flags & CLIF_STRICT_EXCL) && + curr.option_list == option_list + ) fprintf (stderr, "Anyway `%s' must be specified.\n", excl); + else /* simple ordinary option, because excl_cnt == 1 ... */; + } else + fprintf (stderr, "Only one of these may be specified:\n" + " %s\n", excl); + } + + return; +} + + +void CLIF_print_arguments (const char *header, + const CLIF_argument *argument_list) { + const CLIF_argument *argm; + + + if (!argument_list) return; + + /* Print a header string, if present... */ + if (header) fprintf (stderr, "%s\n", header); + + + for (argm = argument_list; argm->name; argm++) { + int len; + + if (argm->flags & CLIF_STRICT) + len = fprintf (stderr, ARG_MARK_STRICT "%s", argm->name); + else if (argm->flags & CLIF_MORE) + len = fprintf (stderr, ARG_MARK_OPT "%s ...", argm->name); + else if (argm->flags & CLIF_ACC_PREV) + len = fprintf (stderr, ARG_MARK_GROUP "%s", argm->name); + else if ((argm + 1)->name && ((argm + 1)->flags & CLIF_ACC_PREV)) + len = fprintf (stderr, ARG_MARK_GROUP0 "%s", argm->name); + else + len = fprintf (stderr, ARG_MARK_OPT "%s", argm->name); + + if (argm->help_string) + box_output (len, ARG_FIELD_WIDTH, + SCREEN_WIDTH - ARG_FIELD_WIDTH, + argm->help_string, argm->name); + + fprintf (stderr, "\n"); + } + + return; +} + + +void CLIF_print_usage (const char *header, const char *progname, + const CLIF_option *option_list, + const CLIF_argument *argument_list) { + + if (!progname && curr.argv) + progname = curr.argv[0]; + + if (!header) { + if (progname) + fprintf (stderr, "Usage: %s", progname); + else + fprintf (stderr, "Command line options:"); + } else { + if (progname) + fprintf (stderr, "%s\n" OPT_START_DLM "%s", header, progname); + else + fprintf (stderr, "%s", header); + } + + + if (option_list) { + const CLIF_option *optn; + char m_buf[256], p_buf[256], mp_buf[256]; + char *m = m_buf, *p = p_buf, *mp = mp_buf; + char *end_m = m_buf + sizeof (m_buf) - 1; + char *end_p = p_buf + sizeof (p_buf) - 1; + char *end_mp = mp_buf + sizeof (mp_buf) - 1; + char *excl; + int excl_cnt = 0; + + + /* first, show exclusive option list, if any... */ + + excl = show_excl (option_list, &excl_cnt); + if (excl_cnt > 0) { + if ((curr.parse_flags & CLIF_STRICT_EXCL) && + curr.option_list == option_list + ) { + if (excl_cnt == 1) + fprintf (stderr, " %s", excl); + else + fprintf (stderr, " { %s }", excl); + } else + fprintf (stderr, " [ %s ]", excl); + } + + + /* second, find short options without arguments... */ + + for (optn = option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + /* We don`t exclude CLIF_EXTRA hear: + simple one char don`t eat a lot of space... + */ + + if (!optn->short_opt || + optn->arg_name || + (optn->flags & CLIF_EXCL) + ) continue; + + if (optn->function_plus) { + if (optn->function) { + if (mp < end_mp) *mp++ = optn->short_opt[0]; + } else { + if (p < end_p) *p++ = optn->short_opt[0]; + } + } else { + if (m < end_m) *m++ = optn->short_opt[0]; + } + } + + if (m > (char *) m_buf) { + *m = '\0'; + fprintf (stderr, " [ -%s ]", m_buf); + } + if (p > (char *) p_buf) { + *p = '\0'; + fprintf (stderr, " [ +%s ]", p_buf); + } + if (mp > (char *) mp_buf) { + *mp = '\0'; + fprintf (stderr, " [ " SHORT_PLUS_MINUS "%s ]", mp_buf); + } + + + /* third, print all another... */ + + for (optn = option_list; + optn->short_opt || optn->long_opt; + optn++ + ) { + if (optn->flags & CLIF_EXTRA) continue; + + if (optn->flags & CLIF_EXCL) + continue; /* already handled */ + + if (optn->short_opt) { + if (optn->arg_name) + fprintf (stderr, " [ %s ]", show_short (optn)); + else + /* already handled */; + } else + fprintf (stderr, " [ %s ]", show_long (optn)); + } + } + + + if (argument_list) { + const CLIF_argument *argm; + int deep = 0; + + for (argm = argument_list; argm->name; argm++) { + + if (argm->flags & CLIF_STRICT) { + if (deep > 0) { + fputc (' ', stderr); + while (deep--) fputc (']', stderr); + deep = 0; + } + + fprintf (stderr, " %s", argm->name); + } else { + if (argm->flags & CLIF_MORE) + fprintf (stderr, " [ %s ...", argm->name); + else if (argm->flags & CLIF_ACC_PREV) { + fprintf (stderr, " %s", argm->name); + --deep; /* ugly, but easy */ + } else + fprintf (stderr, " [ %s", argm->name); + + deep++; + } + } + + if (deep > 0) { + fputc (' ', stderr); + while (deep--) fputc (']', stderr); + } + } + + + fprintf (stderr, "\n"); +} + + +int CLIF_current_help (void) { + + if (!curr.argc) return -1; /* i.e., not inited... */ + + CLIF_print_usage ("Usage:", curr.argv[0], curr.option_list, + curr.argument_list); + + if (curr.option_list) + CLIF_print_options ("Options:", curr.option_list); + + if (curr.argument_list) + CLIF_print_arguments ("\nArguments:", curr.argument_list); + + return 0; +} + + +/* Common useful option handlers. */ + +int CLIF_version_handler (CLIF_option *optn, char *arg) { + + if (!optn->data) return -1; + + fprintf (stderr, "%s\n", ((char *) optn->data)); + + return 0; /* be happy */ +} + + +int CLIF_set_flag (CLIF_option *optn, char *arg) { + + if (!optn->data) return -1; + + *((int *) optn->data) = 1; + + return 0; +} + + +int CLIF_unset_flag (CLIF_option *optn, char *arg) { + + if (!optn->data) return -1; + + *((int *) optn->data) = 0; + + return 0; +} + + +static int set_string (char **data, char *arg) { + + if (!data) return -1; + + *data = arg; + + return 0; +} + +int CLIF_set_string (CLIF_option *optn, char *arg) { + + return set_string (optn->data, arg); +} + +int CLIF_arg_string (CLIF_argument *argm, char *arg, int index) { + + return set_string (argm->data, arg); +} + + +static int set_int (int *data, char *arg) { + char *q; + + if (!data) return -1; + + *data = (int) strtol (arg, &q, 0); + + return (q == arg || *q) ? -1 : 0; +} + +static int set_uint (unsigned int *data, char *arg) { + char *q; + + if (!data) return -1; + + *data = (unsigned int) strtoul (arg, &q, 0); + + return (q == arg || *q) ? -1 : 0; +} + +static int set_double (double *data, char *arg) { + char *q; + + if (!data) return -1; + + *data = strtod (arg, &q); + + return (q == arg || *q) ? -1 : 0; +} + + +int CLIF_set_int (CLIF_option *optn, char *arg) { + + return set_int (optn->data, arg); +} + +int CLIF_set_uint (CLIF_option *optn, char *arg) { + + return set_uint (optn->data, arg); +} + +int CLIF_set_double (CLIF_option *optn, char *arg) { + + return set_double (optn->data, arg); +} + +int CLIF_arg_int (CLIF_argument *argm, char *arg, int index) { + + return set_int (argm->data, arg); +} + +int CLIF_arg_uint (CLIF_argument *argm, char *arg, int index) { + + return set_uint (argm->data, arg); +} + +int CLIF_arg_double (CLIF_argument *argm, char *arg, int index) { + + return set_double (argm->data, arg); +} + + +int CLIF_call_func (CLIF_option *optn, char *arg) { + + if (!optn->data) return -1; + + if (optn->arg_name) { + int (*func) (char *) = optn->data; + + return func (arg); + } else { + int (*func) (void) = optn->data; + + return func (); + } +} + +int CLIF_arg_func (CLIF_argument *argm, char *arg, int index) { + int (*func) (char *, int); + + if (!argm->data) return -1; + + func = (int (*) (char *, int)) argm->data; + + return func (arg, index); +} + diff --git a/iputils/traceroute/clif.h b/iputils/traceroute/clif.h new file mode 100755 index 0000000000..c798a8df8f --- /dev/null +++ b/iputils/traceroute/clif.h @@ -0,0 +1,121 @@ +/* + Copyright (c) 2000, 2003 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: LGPL v2.1 or any later + + See COPYING.LIB for the status of this software. +*/ + +#ifndef _CLIF_H +#define _CLIF_H + + +typedef struct CLIF_option_struct CLIF_option; +struct CLIF_option_struct { + const char *short_opt; + const char *long_opt; + const char *arg_name; + const char *help_string; + int (*function) (CLIF_option *optn, char *arg); + void *data; + int (*function_plus) (CLIF_option *optn, char *arg); + unsigned int flags; +}; +#define CLIF_END_OPTION { 0, 0, 0, 0, 0, 0, 0, 0 } + +typedef struct CLIF_argument_struct CLIF_argument; +struct CLIF_argument_struct { + const char *name; + const char *help_string; + int (*function) (CLIF_argument *argm, char *arg, int index); + void *data; + unsigned int flags; +}; +#define CLIF_END_ARGUMENT { 0, 0, 0, 0, 0 } + +/* Argument flag bits. */ +#define CLIF_MORE (0x01) /* null or several */ +#define CLIF_STRICT (0x02) /* arg must be present */ +#define CLIF_ACC_PREV (0x04) /* arg must be accompanied with previous */ + + +/* Option flag bits. */ + +/* affected only by per-option flags */ +#define CLIF_EXTRA (0x0001) /* don`t show in usage line */ +#define CLIF_EXIT (0x0002) /* exit after handler return */ +#define CLIF_EXCL (0x0004) /* at exclusive area */ + +/* affected by per-option flags and by common `parse_flags' argument + of CLIF_parse_cmdline(). In last case appropriate bits are translated + for all the options. +*/ +#define CLIF_MAY_JOIN_ARG (0x0010) +#define _CLIF_STRICT_JOIN_ARG (0x0020) +#define CLIF_JOIN_ARG (CLIF_MAY_JOIN_ARG|_CLIF_STRICT_JOIN_ARG) +#define CLIF_MAY_NOEQUAL (0x0040) +#define _CLIF_STRICT_NOEQUAL (0x0080) +#define CLIF_NOEQUAL (CLIF_MAY_NOEQUAL|_CLIF_STRICT_NOEQUAL) +#define CLIF_MAY_KEYWORD (0x0100) +#define _CLIF_STRICT_KEYWORD (0x0200) +#define CLIF_KEYWORD (CLIF_MAY_KEYWORD|_CLIF_STRICT_KEYWORD) +#define CLIF_MAY_ONEDASH (0x0400) +#define _CLIF_STRICT_ONEDASH (0x0800) +#define CLIF_ONEDASH (CLIF_MAY_ONEDASH|_CLIF_STRICT_ONEDASH) +#define CLIF_OPTARG (0x1000) /* allow missing optarg */ +#define CLIF_ABBREV (0x2000) /* allow long opt abbreviation */ +#define CLIF_SEVERAL (0x4000) /* several args in one opt`s arg */ + +/* affected only by common `parse_flags' arg of CLIF_parse_cmdline() . */ +#define CLIF_HELP_EMPTY (0x10000) /* print help on empty cmdline */ +#define CLIF_POSIX (0x20000) /* follow POSIX standard */ +#define CLIF_FIRST_GROUP (0x40000) /* first arg - options` group */ +#define CLIF_STRICT_EXCL (0x80000) /* at least one exclusive */ +#define CLIF_SILENT (0x100000) /* no errors on stderr */ + +#define CLIF_MIN_ABBREV 2 /* a minimal match length in abbrev */ + + +extern int CLIF_parse (int argc, char **argv, CLIF_option *option_list, + CLIF_argument *arg_list, unsigned int parse_flags); +/* history compatibility... */ +#define CLIF_parse_cmdline(ARGC,ARGV,OPTN,ARGS,FLAGS) \ + CLIF_parse (ARGC, ARGV, OPTN, ARGS, FLAGS) + +extern void CLIF_print_options (const char *header, + const CLIF_option *option_list); +extern void CLIF_print_arguments (const char *header, + const CLIF_argument *argument_list); +extern void CLIF_print_usage (const char *header, const char *progname, + const CLIF_option *option_list, + const CLIF_argument *argument_list); + +extern int CLIF_current_help (void); + +/* Common useful option handlers. */ +extern int CLIF_version_handler (CLIF_option *optn, char *arg); +extern int CLIF_set_flag (CLIF_option *optn, char *arg); +extern int CLIF_unset_flag (CLIF_option *optn, char *arg); +extern int CLIF_set_string (CLIF_option *optn, char *arg); +extern int CLIF_set_int (CLIF_option *optn, char *arg); +extern int CLIF_set_uint (CLIF_option *optn, char *arg); +extern int CLIF_set_double (CLIF_option *optn, char *arg); +extern int CLIF_call_func (CLIF_option *optn, char *arg); + +extern int CLIF_arg_string (CLIF_argument *argm, char *arg, int index); +extern int CLIF_arg_int (CLIF_argument *argm, char *arg, int index); +extern int CLIF_arg_uint (CLIF_argument *argm, char *arg, int index); +extern int CLIF_arg_double (CLIF_argument *argm, char *arg, int index); +extern int CLIF_arg_func (CLIF_argument *argm, char *arg, int index); + + +/* Some useful macros. */ + +#define CLIF_HELP_OPTION \ + { 0, "help", 0, "Read this help and exit", \ + CLIF_call_func, CLIF_current_help, 0, CLIF_EXTRA | CLIF_EXIT } +#define CLIF_VERSION_OPTION(STR) \ + { "V", "version", 0, "Print version info and exit", \ + CLIF_version_handler, STR, 0, CLIF_EXTRA | CLIF_EXIT } + +#endif /* _CLIF_H */ diff --git a/iputils/traceroute/csum.c b/iputils/traceroute/csum.c new file mode 100755 index 0000000000..ea3a50da04 --- /dev/null +++ b/iputils/traceroute/csum.c @@ -0,0 +1,34 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> + +#include "traceroute.h" + + +uint16_t in_csum (const void *ptr, size_t len) { + const uint16_t *p = (const uint16_t *) ptr; + size_t nw = len / 2; + unsigned int sum = 0; + uint16_t res; + + while (nw--) sum += *p++; + + if (len & 0x1) + sum += htons (*((unsigned char *) p) << 8); + + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + res = ~sum; + if (!res) res = ~0; + + return res; +} + diff --git a/iputils/traceroute/extension.c b/iputils/traceroute/extension.c new file mode 100755 index 0000000000..515a8dbed7 --- /dev/null +++ b/iputils/traceroute/extension.c @@ -0,0 +1,132 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "traceroute.h" + + +struct icmp_ext_header { +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned int version:4; + unsigned int reserved:4; +#else + unsigned int reserved:4; + unsigned int version:4; +#endif + uint8_t reserved1; + uint16_t checksum; +} __attribute__ ((packed)); + + +struct icmp_ext_object { + uint16_t length; + uint8_t class; + uint8_t c_type; + uint8_t data[0]; +}; + +#define MPLS_CLASS 1 +#define MPLS_C_TYPE 1 + + +#define do_snprintf(CURR, END, FMT, ARGS...) \ + do { \ + CURR += snprintf (CURR, END - CURR, (FMT), ## ARGS);\ + if (CURR > END) CURR = END; \ + } while (0) + + +static int try_extension (probe *pb, char *buf, size_t len) { + struct icmp_ext_header *iext = (struct icmp_ext_header *) buf; + char str[1024]; + char *curr = str; + char *end = str + sizeof (str) / sizeof (*str); + + + /* a check for len >= 8 already done for all cases */ + + if (iext->version != 2) return -1; + + if (iext->checksum && + in_csum (iext, len) != (uint16_t) ~0 + ) return -1; + + buf += sizeof (*iext); + len -= sizeof (*iext); + + + while (len >= sizeof (struct icmp_ext_object)) { + struct icmp_ext_object *obj = (struct icmp_ext_object *) buf; + size_t objlen = ntohs (obj->length); + size_t data_len; + uint32_t *ui = (uint32_t *) obj->data; + int i, n; + + if (objlen < sizeof (*obj) || + objlen > len + ) return -1; + + data_len = objlen - sizeof (*obj); + if (data_len % sizeof (uint32_t)) + return -1; /* must be 32bit rounded... */ + + n = data_len / sizeof (*ui); + + + if (curr > (char *) str && curr < end) + *curr++ = ';'; /* a separator */ + + if (obj->class == MPLS_CLASS && + obj->c_type == MPLS_C_TYPE && + n >= 1 + ) { /* people prefer MPLS to be parsed... */ + + do_snprintf (curr, end, "MPLS:"); + + for (i = 0; i < n; i++, ui++) { + uint32_t mpls = ntohl (*ui); + + do_snprintf (curr, end, "%sL=%u,E=%u,S=%u,T=%u", + i ? "/" : "", + mpls >> 12, + (mpls >> 9) & 0x7, + (mpls >> 8) & 0x1, + mpls & 0xff); + } + + } + else { /* common case... */ + + do_snprintf (curr, end, "%u/%u:", obj->class, obj->c_type); + + for (i = 0; i < n && curr < end; i++, ui++) + do_snprintf (curr, end, "%s%08x", i ? "," : "", ntohl(*ui)); + } + + buf += objlen; + len -= objlen; + } + + if (len) return -1; + + + pb->ext = strdup (str); + + return 0; +} + + +void handle_extensions (probe *pb, char *buf, int len, int step) { + + if (!step) + try_extension (pb, buf, len); + else { + for ( ; len >= 8; buf += step, len -= step) + if (try_extension (pb, buf, len) == 0) + break; + } + + return; +} + diff --git a/iputils/traceroute/flowlabel.h b/iputils/traceroute/flowlabel.h new file mode 100755 index 0000000000..af5a65ae28 --- /dev/null +++ b/iputils/traceroute/flowlabel.h @@ -0,0 +1,40 @@ +/* + It is just a stripped copy of the kernel header "linux/in6.h" + + "Flow label" things are still not defined in "netinet/in*.h" headers, + but we cannot use "linux/in6.h" immediately because it currently + conflicts with "netinet/in.h" . +*/ + +struct in6_flowlabel_req +{ + struct in6_addr flr_dst; + __u32 flr_label; + __u8 flr_action; + __u8 flr_share; + __u16 flr_flags; + __u16 flr_expires; + __u16 flr_linger; + __u32 __flr_pad; + /* Options in format of IPV6_PKTOPTIONS */ +}; + +#define IPV6_FL_A_GET 0 +#define IPV6_FL_A_PUT 1 +#define IPV6_FL_A_RENEW 2 + +#define IPV6_FL_F_CREATE 1 +#define IPV6_FL_F_EXCL 2 + +#define IPV6_FL_S_NONE 0 +#define IPV6_FL_S_EXCL 1 +#define IPV6_FL_S_PROCESS 2 +#define IPV6_FL_S_USER 3 +#define IPV6_FL_S_ANY 255 + +#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff +#define IPV6_FLOWINFO_PRIORITY 0x0ff00000 + +#define IPV6_FLOWLABEL_MGR 32 +#define IPV6_FLOWINFO_SEND 33 + diff --git a/iputils/traceroute/mod-dccp.c b/iputils/traceroute/mod-dccp.c new file mode 100755 index 0000000000..077c20cef9 --- /dev/null +++ b/iputils/traceroute/mod-dccp.c @@ -0,0 +1,294 @@ +/* + Copyright (c) 2012 Samuel Jero <sj323707@ohio.edu> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <linux/dccp.h> + + +#include "traceroute.h" + + +#define DEF_SERVICE_CODE 1885957735 + +#define DCCP_HEADER_LEN (sizeof (struct dccp_hdr) + \ + sizeof (struct dccp_hdr_ext) \ + + sizeof (struct dccp_hdr_request)) + +static sockaddr_any dest_addr = {{ 0, }, }; +static unsigned int dest_port = 0; + +static int raw_sk = -1; +static int last_ttl = 0; + +static uint8_t buf[1024]; /* enough, enough... */ +static size_t csum_len = 0; +static struct dccp_hdr *dh = NULL; +static struct dccp_hdr_ext *dhe = NULL; +static struct dccp_hdr_request *dhr = NULL; +static unsigned int service_code = DEF_SERVICE_CODE; + + +static CLIF_option dccp_options[] = { + { 0, "service", "NUM", "Set DCCP service code to %s (default is " + _TEXT (DEF_SERVICE_CODE) ")", + CLIF_set_uint, &service_code, 0, CLIF_ABBREV }, + CLIF_END_OPTION +}; + + +static int dccp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int af = dest->sa.sa_family; + sockaddr_any src; + socklen_t len; + uint8_t *ptr; + uint16_t *lenp; + + + dest_addr = *dest; + dest_addr.sin.sin_port = 0; /* raw sockets can be confused */ + + if (!port_seq) port_seq = DEF_DCCP_PORT; + dest_port = htons (port_seq); + + + /* Create raw socket for DCCP */ + raw_sk = socket (af, SOCK_RAW, IPPROTO_DCCP); + if (raw_sk < 0) + error_or_perm ("socket"); + + tune_socket (raw_sk); /* including bind, if any */ + + if (connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0) + error ("connect"); + + len = sizeof (src); + if (getsockname (raw_sk, &src.sa, &len) < 0) + error ("getsockname"); + + + if (!raw_can_connect ()) { /* work-around for buggy kernels */ + close (raw_sk); + raw_sk = socket (af, SOCK_RAW, IPPROTO_DCCP); + if (raw_sk < 0) error ("socket"); + tune_socket (raw_sk); + /* but do not connect it... */ + } + + + use_recverr (raw_sk); + + add_poll (raw_sk, POLLIN | POLLERR); + + + /* Now create the sample packet. */ + + /* For easy checksum computing: + saddr + daddr + length + protocol + dccphdr + */ + + ptr = buf; + + if (af == AF_INET) { + len = sizeof (src.sin.sin_addr); + memcpy (ptr, &src.sin.sin_addr, len); + ptr += len; + memcpy (ptr, &dest_addr.sin.sin_addr, len); + ptr += len; + } else { + len = sizeof (src.sin6.sin6_addr); + memcpy (ptr, &src.sin6.sin6_addr, len); + ptr += len; + memcpy (ptr, &dest_addr.sin6.sin6_addr, len); + ptr += len; + } + + lenp = (uint16_t *) ptr; + ptr += sizeof (uint16_t); + *((uint16_t *) ptr) = htons ((uint16_t) IPPROTO_DCCP); + ptr += sizeof (uint16_t); + + + /* Construct DCCP header */ + dh = (struct dccp_hdr *) ptr; + + dh->dccph_ccval = 0; + dh->dccph_checksum = 0; + dh->dccph_cscov = 0; + dh->dccph_dport = dest_port; + dh->dccph_reserved = 0; + dh->dccph_sport = 0; /* temporary */ + dh->dccph_x = 1; + dh->dccph_type = DCCP_PKT_REQUEST; + dh->dccph_seq2 = 0; /* reserved if using 48 bit sequence numbers */ + /* high 16 bits of sequence number. Always make 0 for simplicity. */ + dh->dccph_seq = 0; + ptr += sizeof (struct dccp_hdr); + + dhe = (struct dccp_hdr_ext *) ptr; + dhe->dccph_seq_low = 0; /* temporary */ + ptr += sizeof (struct dccp_hdr_ext); + + dhr = (struct dccp_hdr_request *) ptr; + dhr->dccph_req_service = htonl (service_code); + ptr += sizeof (struct dccp_hdr_request); + + + csum_len = ptr - buf; + + if (csum_len > sizeof (buf)) + error ("impossible"); /* paranoia */ + + len = ptr - (uint8_t *) dh; + if (len & 0x03) error ("impossible"); /* as >>2 ... */ + + *lenp = htons (len); + dh->dccph_doff = len >> 2; + + + *packet_len_p = len; + + return 0; +} + + +static void dccp_send_probe (probe *pb, int ttl) { + int sk; + int af = dest_addr.sa.sa_family; + sockaddr_any addr; + socklen_t len = sizeof (addr); + + + /* To make sure we have chosen a free unused "source port", + just create, (auto)bind and hold a socket while the port is needed. + */ + + sk = socket (af, SOCK_DCCP, IPPROTO_DCCP); + if (sk < 0) error ("socket"); + + bind_socket (sk); + + if (getsockname (sk, &addr.sa, &len) < 0) + error ("getsockname"); + + /* When we reach the target host, it can send us either Reset or Response. + For Reset all is OK (we and kernel just answer nothing), but + for Response we should reply with our Close. + It is well-known "half-open technique", used by port scanners etc. + This way we do not touch remote applications at all, unlike + the ordinary connect(2) call. + As the port-holding socket neither connect() nor listen(), + it means "no such port yet" for remote ends, and kernel always + send Reset in such a situation automatically (we have to do nothing). + */ + + + dh->dccph_sport = addr.sin.sin_port; + + dhe->dccph_seq_low = random_seq (); + + dh->dccph_checksum = 0; + dh->dccph_checksum = in_csum (buf, csum_len); + + + if (ttl != last_ttl) { + set_ttl (raw_sk, ttl); + last_ttl = ttl; + } + + + pb->send_time = get_time (); + + if (do_send (raw_sk, dh, dh->dccph_doff << 2, &dest_addr) < 0) { + close (sk); + pb->send_time = 0; + return; + } + + + pb->seq = dh->dccph_sport; + + pb->sk = sk; + + return; +} + + +static probe *dccp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + probe *pb; + struct dccp_hdr *ndh = (struct dccp_hdr *) buf; + uint16_t sport, dport; + + + if (len < 8) return NULL; /* too short */ + + + if (err) { + sport = ndh->dccph_sport; + dport = ndh->dccph_dport; + } else { + sport = ndh->dccph_dport; + dport = ndh->dccph_sport; + } + + + if (dport != dest_port) + return NULL; + + if (!equal_addr (&dest_addr, from)) + return NULL; + + pb = probe_by_seq (sport); + if (!pb) return NULL; + + if (!err) pb->final = 1; + + return pb; +} + + +static void dccp_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), dccp_check_reply); +} + + +static void dccp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module dccp_ops = { + .name = "dccp", + .init = dccp_init, + .send_probe = dccp_send_probe, + .recv_probe = dccp_recv_probe, + .expire_probe = dccp_expire_probe, + .options = dccp_options, +}; + +TR_MODULE (dccp_ops); + +void tr_module_dccp_insert() +{ + +} diff --git a/iputils/traceroute/mod-icmp.c b/iputils/traceroute/mod-icmp.c new file mode 100755 index 0000000000..ed13b5096e --- /dev/null +++ b/iputils/traceroute/mod-icmp.c @@ -0,0 +1,253 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/icmp6.h> +#include <netinet/ip_icmp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> + +#include "traceroute.h" + + +static sockaddr_any dest_addr = {{ 0, }, }; +static uint16_t seq = 1; +static uint16_t ident = 0; + +static char *data; +static size_t *length_p; + +static int icmp_sk = -1; +static int last_ttl = 0; + +static int raw = 0; +static int dgram = 0; + + +static CLIF_option icmp_options[] = { + { 0, "raw", 0, "Use raw sockets way only. Default is try this way " + "first (probably not allowed for unprivileged users), " + "then try dgram", + CLIF_set_flag, &raw, 0, CLIF_EXCL }, + { 0, "dgram", 0, "Use dgram sockets way only. May be not implemented " + "by old kernels or restricted by sysadmins", + CLIF_set_flag, &dgram, 0, CLIF_EXCL }, + CLIF_END_OPTION +}; + + +static int icmp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int i; + int af = dest->sa.sa_family; + int protocol; + + dest_addr = *dest; + dest_addr.sin.sin_port = 0; + + if (port_seq) seq = port_seq; + + length_p = packet_len_p; + if (*length_p < sizeof (struct icmphdr)) + *length_p = sizeof (struct icmphdr); + + data = malloc (*length_p); + if (!data) error ("malloc"); + + for (i = sizeof (struct icmphdr); i < *length_p; i++) + data[i] = 0x40 + (i & 0x3f); + + + protocol = (af == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6; + + if (!raw) { + icmp_sk = socket (af, SOCK_DGRAM, protocol); + if (icmp_sk < 0 && dgram) + error ("socket"); + } + + if (!dgram) { + int raw_sk = socket (af, SOCK_RAW, protocol); + if (raw_sk < 0) { + if (raw || icmp_sk < 0) + error_or_perm ("socket"); + dgram = 1; + } else { + /* prefer the traditional "raw" way when possible */ + close (icmp_sk); + icmp_sk = raw_sk; + } + } + + + tune_socket (icmp_sk); + + /* Don't want to catch packets from another hosts */ + if (raw_can_connect () && + connect (icmp_sk, &dest_addr.sa, sizeof (dest_addr)) < 0 + ) error ("connect"); + + use_recverr (icmp_sk); + + + if (dgram) { + sockaddr_any addr; + socklen_t len = sizeof (addr); + + if (getsockname (icmp_sk, &addr.sa, &len) < 0) + error ("getsockname"); + ident = ntohs (addr.sin.sin_port); /* both IPv4 and IPv6 */ + + } else + ident = getpid () & 0xffff; + + + add_poll (icmp_sk, POLLIN | POLLERR); + + return 0; +} + + +static void icmp_send_probe (probe *pb, int ttl) { + int af = dest_addr.sa.sa_family; + + + if (ttl != last_ttl) { + + set_ttl (icmp_sk, ttl); + + last_ttl = ttl; + } + + + if (af == AF_INET) { + struct icmp *icmp = (struct icmp *) data; + + icmp->icmp_type = ICMP_ECHO; + icmp->icmp_code = 0; + icmp->icmp_cksum = 0; + icmp->icmp_id = htons (ident); + icmp->icmp_seq = htons (seq); + + icmp->icmp_cksum = in_csum (data, *length_p); + } + else if (af == AF_INET6) { + struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) data; + + icmp6->icmp6_type = ICMP6_ECHO_REQUEST; + icmp6->icmp6_code = 0; + icmp6->icmp6_cksum = 0; + icmp6->icmp6_id = htons (ident); + icmp6->icmp6_seq = htons(seq); + + /* icmp6->icmp6_cksum always computed by kernel internally */ + } + + + pb->send_time = get_time (); + + if (do_send (icmp_sk, data, *length_p, &dest_addr) < 0) { + pb->send_time = 0; + return; + } + + + pb->seq = seq; + + seq++; + + return; +} + + +static probe *icmp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + int af = dest_addr.sa.sa_family; + int type; + uint16_t recv_id, recv_seq; + probe *pb; + + + if (len < sizeof (struct icmphdr)) + return NULL; + + + if (af == AF_INET) { + struct icmp *icmp = (struct icmp *) buf; + + type = icmp->icmp_type; + + recv_id = ntohs (icmp->icmp_id); + recv_seq = ntohs (icmp->icmp_seq); + + } + else { /* AF_INET6 */ + struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) buf; + + type = icmp6->icmp6_type; + + recv_id = ntohs (icmp6->icmp6_id); + recv_seq = ntohs (icmp6->icmp6_seq); + } + + + if (recv_id != ident) + return NULL; + + pb = probe_by_seq (recv_seq); + if (!pb) return NULL; + + + if (!err) { + + if (!(af == AF_INET && type == ICMP_ECHOREPLY) && + !(af == AF_INET6 && type == ICMP6_ECHO_REPLY) + ) return NULL; + + pb->final = 1; + } + + return pb; +} + + +static void icmp_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), icmp_check_reply); +} + + +static void icmp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module icmp_ops = { + .name = "icmp", + .init = icmp_init, + .send_probe = icmp_send_probe, + .recv_probe = icmp_recv_probe, + .expire_probe = icmp_expire_probe, + .options = icmp_options, +}; + +TR_MODULE (icmp_ops); + +void tr_module_icmp_insert() +{ + +} + diff --git a/iputils/traceroute/mod-raw.c b/iputils/traceroute/mod-raw.c new file mode 100755 index 0000000000..6a05516ce2 --- /dev/null +++ b/iputils/traceroute/mod-raw.c @@ -0,0 +1,168 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/icmp6.h> +#include <netinet/ip_icmp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netdb.h> + +#include "traceroute.h" + + +static sockaddr_any dest_addr = {{ 0, }, }; +static int protocol = DEF_RAW_PROT; + +static char *data = NULL; +static size_t *length_p; + +static int raw_sk = -1; +static int last_ttl = 0; +static int seq = 0; + + +static int set_protocol (CLIF_option *optn, char *arg) { + char *q; + + protocol = strtoul (arg, &q, 0); + if (q == arg) { + struct protoent *p = getprotobyname (arg); + + if (!p) return -1; + protocol = p->p_proto; + } + + return 0; +} + + +static CLIF_option raw_options[] = { + { 0, "protocol", "PROT", "Use protocol %s (default is " + _TEXT (DEF_RAW_PROT) ")", + set_protocol, 0, 0, CLIF_ABBREV }, + CLIF_END_OPTION +}; + + +static int raw_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int i; + int af = dest->sa.sa_family; + + dest_addr = *dest; + dest_addr.sin.sin_port = 0; + + if (port_seq) protocol = port_seq; + + + length_p = packet_len_p; + + if (*length_p && + !(data = malloc (*length_p)) + ) error ("malloc"); + + for (i = 0; i < *length_p; i++) + data[i] = 0x40 + (i & 0x3f); + + + raw_sk = socket (af, SOCK_RAW, protocol); + if (raw_sk < 0) + error_or_perm ("socket"); + + tune_socket (raw_sk); + + /* Don't want to catch packets from another hosts */ + if (raw_can_connect () && + connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0 + ) error ("connect"); + + use_recverr (raw_sk); + + + add_poll (raw_sk, POLLIN | POLLERR); + + return 0; +} + + +static void raw_send_probe (probe *pb, int ttl) { + + if (ttl != last_ttl) { + + set_ttl (raw_sk, ttl); + + last_ttl = ttl; + } + + + pb->send_time = get_time (); + + if (do_send (raw_sk, data, *length_p, &dest_addr) < 0) { + pb->send_time = 0; + return; + } + + + pb->seq = ++seq; + + return; +} + + +static probe *raw_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + probe *pb; + + if (!equal_addr (&dest_addr, from)) + return NULL; + + pb = probe_by_seq (seq); + if (!pb) return NULL; + + if (!err) pb->final = 1; + + return pb; +} + + +static void raw_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), raw_check_reply); +} + + +static void raw_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module raw_ops = { + .name = "raw", + .init = raw_init, + .send_probe = raw_send_probe, + .recv_probe = raw_recv_probe, + .expire_probe = raw_expire_probe, + .options = raw_options, + .one_per_time = 1, +}; + +TR_MODULE (raw_ops); + +void tr_module_raw_insert() +{ + +} diff --git a/iputils/traceroute/mod-tcp.c b/iputils/traceroute/mod-tcp.c new file mode 100755 index 0000000000..b5b0ca2d52 --- /dev/null +++ b/iputils/traceroute/mod-tcp.c @@ -0,0 +1,512 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/icmp6.h> +#include <netinet/ip_icmp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/tcp.h> + + +#include "traceroute.h" + + +#ifndef IP_MTU +#define IP_MTU 14 +#endif + + +static sockaddr_any dest_addr = {{ 0, }, }; +static unsigned int dest_port = 0; + +static int raw_sk = -1; +static int last_ttl = 0; + +static uint8_t buf[1024]; /* enough, enough... */ +static size_t csum_len = 0; +static struct tcphdr *th = NULL; + +#define TH_FLAGS(TH) (((uint8_t *) (TH))[13]) +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 +#define TH_ECE 0x40 +#define TH_CWR 0x80 + + +static int flags = 0; /* & 0xff == tcp_flags ... */ +static int sysctl = 0; +static int reuse = 0; +static unsigned int mss = 0; +static int info = 0; + +#define FL_FLAGS 0x0100 +#define FL_ECN 0x0200 +#define FL_SACK 0x0400 +#define FL_TSTAMP 0x0800 +#define FL_WSCALE 0x1000 + + +static struct { + const char *name; + unsigned int flag; +} tcp_flags[] = { + { "fin", TH_FIN }, + { "syn", TH_SYN }, + { "rst", TH_RST }, + { "psh", TH_PSH }, + { "ack", TH_ACK }, + { "urg", TH_URG }, + { "ece", TH_ECE }, + { "cwr", TH_CWR }, +}; + +static char *names_by_flags (unsigned int flags) { + int i; + char str[64]; /* enough... */ + char *curr = str; + char *end = str + sizeof (str) / sizeof (*str); + + for (i = 0; i < sizeof (tcp_flags) / sizeof (*tcp_flags); i++) { + const char *p; + + if (!(flags & tcp_flags[i].flag)) continue; + + if (curr > str && curr < end) *curr++ = ','; + for (p = tcp_flags[i].name; *p && curr < end; *curr++ = *p++) ; + } + + *curr = '\0'; + + return strdup (str); +} + +static int set_tcp_flag (CLIF_option *optn, char *arg) { + int i; + + for (i = 0; i < sizeof (tcp_flags) / sizeof (*tcp_flags); i++) { + if (!strcmp (optn->long_opt, tcp_flags[i].name)) { + flags |= tcp_flags[i].flag; + return 0; + } + } + + return -1; +} + +static int set_tcp_flags (CLIF_option *optn, char *arg) { + char *q; + unsigned long value; + + value = strtoul (arg, &q, 0); + if (q == arg) return -1; + + flags = (flags & ~0xff) | (value & 0xff) | FL_FLAGS; + return 0; +} + +static int set_flag (CLIF_option *optn, char *arg) { + + flags |= (unsigned long) optn->data; + + return 0; +} + +static CLIF_option tcp_options[] = { + { 0, "syn", 0, "Set tcp flag SYN (default if no other " + "tcp flags specified)", set_tcp_flag, 0, 0, 0 }, + { 0, "ack", 0, "Set tcp flag ACK,", set_tcp_flag, 0, 0, 0 }, + { 0, "fin", 0, "FIN,", set_tcp_flag, 0, 0, 0 }, + { 0, "rst", 0, "RST,", set_tcp_flag, 0, 0, 0 }, + { 0, "psh", 0, "PSH,", set_tcp_flag, 0, 0, 0 }, + { 0, "urg", 0, "URG,", set_tcp_flag, 0, 0, 0 }, + { 0, "ece", 0, "ECE,", set_tcp_flag, 0, 0, 0 }, + { 0, "cwr", 0, "CWR", set_tcp_flag, 0, 0, 0 }, + { 0, "flags", "NUM", "Set tcp flags exactly to value %s", + set_tcp_flags, 0, 0, CLIF_ABBREV }, + { 0, "ecn", 0, "Send syn packet with tcp flags ECE and CWR " + "(for Explicit Congestion Notification, rfc3168)", + set_flag, (void *) FL_ECN, 0, 0 }, + { 0, "sack", 0, "Use sack,", + set_flag, (void *) FL_SACK, 0, 0 }, + { 0, "timestamps", 0, "timestamps,", + set_flag, (void *) FL_TSTAMP, 0, CLIF_ABBREV }, + { 0, "window_scaling", 0, "window_scaling option for tcp", + set_flag, (void *) FL_WSCALE, 0, CLIF_ABBREV }, + { 0, "sysctl", 0, "Use current sysctl (/proc/sys/net/*) setting " + "for the tcp options and ecn. Always set by default " + "(with \"syn\") if nothing else specified", + CLIF_set_flag, &sysctl, 0, 0 }, + { 0, "reuse", 0, "Allow to reuse local port numbers " + "for the huge workloads (SO_REUSEADDR)", + CLIF_set_flag, &reuse, 0, 0 }, + { 0, "mss", "NUM", "Use value of %s for maxseg tcp option (when syn)", + CLIF_set_uint, &mss, 0, 0 }, + { 0, "info", 0, "Print tcp flags of final tcp replies when target " + "host is reached. Useful to determine whether " + "an application listens the port etc.", + CLIF_set_flag, &info, 0, 0 }, + CLIF_END_OPTION +}; + + +#define SYSCTL_PREFIX "/proc/sys/net/ipv4/tcp_" +static int check_sysctl (const char *name) { + int fd, res; + char buf[sizeof (SYSCTL_PREFIX) + strlen (name) + 1]; + uint8_t ch; + + strcpy (buf, SYSCTL_PREFIX); + strcat (buf, name); + + fd = open (buf, O_RDONLY, 0); + if (fd < 0) return 0; + + res = read (fd, &ch, sizeof (ch)); + close (fd); + + if (res != sizeof (ch)) + return 0; + + /* since kernel 2.6.31 "tcp_ecn" can have value of '2'... */ + if (ch == '1') return 1; + + return 0; +} + + +static int tcp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int af = dest->sa.sa_family; + sockaddr_any src; + int mtu; + socklen_t len; + uint8_t *ptr; + uint16_t *lenp; + + + dest_addr = *dest; + dest_addr.sin.sin_port = 0; /* raw sockets can be confused */ + + if (!port_seq) port_seq = DEF_TCP_PORT; + dest_port = htons (port_seq); + + + /* Create raw socket for tcp */ + + raw_sk = socket (af, SOCK_RAW, IPPROTO_TCP); + if (raw_sk < 0) + error_or_perm ("socket"); + + tune_socket (raw_sk); /* including bind, if any */ + + if (connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0) + error ("connect"); + + len = sizeof (src); + if (getsockname (raw_sk, &src.sa, &len) < 0) + error ("getsockname"); + + + len = sizeof (mtu); + if (getsockopt (raw_sk, af == AF_INET ? SOL_IP : SOL_IPV6, + af == AF_INET ? IP_MTU : IPV6_MTU, + &mtu, &len) < 0 || mtu < 576 + ) mtu = 576; + + /* mss = mtu - headers */ + mtu -= af == AF_INET ? sizeof (struct iphdr) : sizeof (struct ip6_hdr); + mtu -= sizeof (struct tcphdr); + + + if (!raw_can_connect ()) { /* work-around for buggy kernels */ + close (raw_sk); + raw_sk = socket (af, SOCK_RAW, IPPROTO_TCP); + if (raw_sk < 0) error ("socket"); + tune_socket (raw_sk); + /* but do not connect it... */ + } + + + use_recverr (raw_sk); + + add_poll (raw_sk, POLLIN | POLLERR); + + + /* Now create the sample packet. */ + + if (!flags) sysctl = 1; + + if (sysctl) { + if (check_sysctl ("ecn")) flags |= FL_ECN; + if (check_sysctl ("sack")) flags |= FL_SACK; + if (check_sysctl ("timestamps")) flags |= FL_TSTAMP; + if (check_sysctl ("window_scaling")) flags |= FL_WSCALE; + } + + if (!(flags & (FL_FLAGS | 0xff))) { /* no any tcp flag set */ + flags |= TH_SYN; + if (flags & FL_ECN) + flags |= TH_ECE | TH_CWR; + } + + + /* For easy checksum computing: + saddr + daddr + length + protocol + tcphdr + tcpoptions + */ + + ptr = buf; + + if (af == AF_INET) { + len = sizeof (src.sin.sin_addr); + memcpy (ptr, &src.sin.sin_addr, len); + ptr += len; + memcpy (ptr, &dest_addr.sin.sin_addr, len); + ptr += len; + } else { + len = sizeof (src.sin6.sin6_addr); + memcpy (ptr, &src.sin6.sin6_addr, len); + ptr += len; + memcpy (ptr, &dest_addr.sin6.sin6_addr, len); + ptr += len; + } + + lenp = (uint16_t *) ptr; + ptr += sizeof (uint16_t); + *((uint16_t *) ptr) = htons ((uint16_t) IPPROTO_TCP); + ptr += sizeof (uint16_t); + + + /* Construct TCP header */ + + th = (struct tcphdr *) ptr; + + th->source = 0; /* temporary */ + th->dest = dest_port; + th->seq = 0; /* temporary */ + th->ack_seq = 0; + th->doff = 0; /* later... */ + TH_FLAGS(th) = flags & 0xff; + th->window = htons (4 * mtu); + th->check = 0; + th->urg_ptr = 0; + + + /* Build TCP options */ + + ptr = (uint8_t *) (th + 1); + + if (flags & TH_SYN) { + *ptr++ = TCPOPT_MAXSEG; /* 2 */ + *ptr++ = TCPOLEN_MAXSEG; /* 4 */ + *((uint16_t *) ptr) = htons (mss ? mss : mtu); + ptr += sizeof (uint16_t); + } + + if (flags & FL_TSTAMP) { + + if (flags & FL_SACK) { + *ptr++ = TCPOPT_SACK_PERMITTED; /* 4 */ + *ptr++ = TCPOLEN_SACK_PERMITTED;/* 2 */ + } else { + *ptr++ = TCPOPT_NOP; /* 1 */ + *ptr++ = TCPOPT_NOP; /* 1 */ + } + *ptr++ = TCPOPT_TIMESTAMP; /* 8 */ + *ptr++ = TCPOLEN_TIMESTAMP; /* 10 */ + + *((uint32_t *) ptr) = random_seq (); /* really! */ + ptr += sizeof (uint32_t); + *((uint32_t *) ptr) = (flags & TH_ACK) ? random_seq () : 0; + ptr += sizeof (uint32_t); + } + else if (flags & FL_SACK) { + *ptr++ = TCPOPT_NOP; /* 1 */ + *ptr++ = TCPOPT_NOP; /* 1 */ + *ptr++ = TCPOPT_SACK_PERMITTED; /* 4 */ + *ptr++ = TCPOLEN_SACK_PERMITTED; /* 2 */ + } + + if (flags & FL_WSCALE) { + *ptr++ = TCPOPT_NOP; /* 1 */ + *ptr++ = TCPOPT_WINDOW; /* 3 */ + *ptr++ = TCPOLEN_WINDOW; /* 3 */ + *ptr++ = 2; /* assume some corect value... */ + } + + + csum_len = ptr - buf; + + if (csum_len > sizeof (buf)) + error ("impossible"); /* paranoia */ + + len = ptr - (uint8_t *) th; + if (len & 0x03) error ("impossible"); /* as >>2 ... */ + + *lenp = htons (len); + th->doff = len >> 2; + + + *packet_len_p = len; + + return 0; +} + + +static void tcp_send_probe (probe *pb, int ttl) { + int sk; + int af = dest_addr.sa.sa_family; + sockaddr_any addr; + socklen_t len = sizeof (addr); + + + /* To make sure we have chosen a free unused "source port", + just create, (auto)bind and hold a socket while the port is needed. + */ + + sk = socket (af, SOCK_STREAM, 0); + if (sk < 0) error ("socket"); + + if (reuse && setsockopt (sk, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) + error ("setsockopt SO_REUSEADDR"); + + bind_socket (sk); + + if (getsockname (sk, &addr.sa, &len) < 0) + error ("getsockname"); + + /* When we reach the target host, it can send us either RST or SYN+ACK. + For RST all is OK (we and kernel just answer nothing), but + for SYN+ACK we should reply with our RST. + It is well-known "half-open technique", used by port scanners etc. + This way we do not touch remote applications at all, unlike + the ordinary connect(2) call. + As the port-holding socket neither connect() nor listen(), + it means "no such port yet" for remote ends, and kernel always + send RST in such a situation automatically (we have to do nothing). + */ + + + th->source = addr.sin.sin_port; + + th->seq = random_seq (); + + th->check = 0; + th->check = in_csum (buf, csum_len); + + + if (ttl != last_ttl) { + + set_ttl (raw_sk, ttl); + + last_ttl = ttl; + } + + + pb->send_time = get_time (); + + if (do_send (raw_sk, th, th->doff << 2, &dest_addr) < 0) { + close (sk); + pb->send_time = 0; + return; + } + + + pb->seq = th->source; + + pb->sk = sk; + + return; +} + + +static probe *tcp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + probe *pb; + struct tcphdr *tcp = (struct tcphdr *) buf; + uint16_t sport, dport; + + + if (len < 8) return NULL; /* too short */ + + + if (err) { + sport = tcp->source; + dport = tcp->dest; + } else { + sport = tcp->dest; + dport = tcp->source; + } + + + if (dport != dest_port) + return NULL; + + if (!equal_addr (&dest_addr, from)) + return NULL; + + pb = probe_by_seq (sport); + if (!pb) return NULL; + + + if (!err) { + + pb->final = 1; + + if (info) + pb->ext = names_by_flags (TH_FLAGS(tcp)); + } + + return pb; +} + + +static void tcp_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), tcp_check_reply); +} + + +static void tcp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module tcp_ops = { + .name = "tcp", + .init = tcp_init, + .send_probe = tcp_send_probe, + .recv_probe = tcp_recv_probe, + .expire_probe = tcp_expire_probe, + .options = tcp_options, +}; + +TR_MODULE (tcp_ops); + +void tr_module_tcp_insert() +{ + +} diff --git a/iputils/traceroute/mod-tcpconn.c b/iputils/traceroute/mod-tcpconn.c new file mode 100755 index 0000000000..df37bb556b --- /dev/null +++ b/iputils/traceroute/mod-tcpconn.c @@ -0,0 +1,233 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/icmp6.h> +#include <netinet/ip_icmp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/tcp.h> +#include <errno.h> + +#include "traceroute.h" + + +static sockaddr_any dest_addr = {{ 0, }, }; + +static int icmp_sk = -1; + + +static int tcp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + int af = dest->sa.sa_family; + + dest_addr = *dest; + dest_addr.sin.sin_port = htons (DEF_TCP_PORT); + + if (port_seq) + dest_addr.sin.sin_port = htons (port_seq); + + + /* Currently an ICMP socket is the only way + to obtain the needed info... + */ + icmp_sk = socket (af, SOCK_RAW, (af == AF_INET) ? IPPROTO_ICMP + : IPPROTO_ICMPV6); + if (icmp_sk < 0) + error_or_perm ("socket"); + + /* icmp_sk not need full tune_socket() here, just a receiving one */ + bind_socket (icmp_sk); + use_timestamp (icmp_sk); + use_recv_ttl (icmp_sk); + + add_poll (icmp_sk, POLLIN); + + return 0; +} + + +static void tcp_send_probe (probe *pb, int ttl) { + int sk; + int af = dest_addr.sa.sa_family; + sockaddr_any addr; + socklen_t length = sizeof (addr); + + + sk = socket (af, SOCK_STREAM, 0); + if (sk < 0) error ("socket"); + + tune_socket (sk); /* common stuff */ + + set_ttl (sk, ttl); + + + pb->send_time = get_time (); + + if (connect (sk, &dest_addr.sa, sizeof (dest_addr)) < 0) { + if (errno != EINPROGRESS) + error ("connect"); + } + + + if (getsockname (sk, &addr.sa, &length) < 0) + error ("getsockname"); + + pb->seq = addr.sin.sin_port; /* both ipv4/ipv6 */ + + pb->sk = sk; + + add_poll (sk, POLLERR | POLLHUP | POLLOUT); + + return; +} + + +static probe *tcp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + int af = dest_addr.sa.sa_family; + int type, code, info; + probe *pb; + struct tcphdr *tcp; + + + if (len < sizeof (struct icmphdr)) + return NULL; + + + if (af == AF_INET) { + struct icmp *icmp = (struct icmp *) buf; + struct iphdr *ip; + int hlen; + + type = icmp->icmp_type; + code = icmp->icmp_code; + info = icmp->icmp_void; + + if (type != ICMP_TIME_EXCEEDED && type != ICMP_DEST_UNREACH) + return NULL; + + if (len < sizeof (struct icmphdr) + sizeof (struct iphdr) + 8) + /* `8' - rfc1122: 3.2.2 */ + return NULL; + + ip = (struct iphdr *) (((char *)icmp) + sizeof(struct icmphdr)); + hlen = ip->ihl << 2; + + if (len < sizeof (struct icmphdr) + hlen + 8) + return NULL; + if (ip->protocol != IPPROTO_TCP) + return NULL; + + tcp = (struct tcphdr *) (((char *) ip) + hlen); + + } + else { /* AF_INET6 */ + struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) buf; + struct ip6_hdr *ip6; + + type = icmp6->icmp6_type; + code = icmp6->icmp6_code; + info = icmp6->icmp6_mtu; + + if (type != ICMP6_TIME_EXCEEDED && + type != ICMP6_DST_UNREACH && + type != ICMP6_PACKET_TOO_BIG + ) return NULL; + + if (len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr) + 8) + return NULL; + + ip6 = (struct ip6_hdr *) (icmp6 + 1); + if (ip6->ip6_nxt != IPPROTO_TCP) + return NULL; + + tcp = (struct tcphdr *) (ip6 + 1); + + } + + + if (tcp->dest != dest_addr.sin.sin_port) + return NULL; + + pb = probe_by_seq (tcp->source); + if (!pb) return NULL; + + + /* here only, high level has no data to do this */ + parse_icmp_res (pb, type, code, info); + + return pb; +} + + +static void tcp_recv_probe (int sk, int revents) { + + if (sk != icmp_sk) { /* a tcp socket */ + probe *pb; + + pb = probe_by_sk (sk); + if (!pb) { + del_poll (sk); + return; + } + + + /* do connect() again and check errno, regardless of revents */ + if (connect (sk, &dest_addr.sa, sizeof (dest_addr)) < 0) { + if (errno != EISCONN && errno != ECONNREFUSED) + return; /* ICMP say more */ + } + + /* we have reached the dest host (either connected or refused) */ + + memcpy (&pb->res, &dest_addr, sizeof (pb->res)); + + pb->final = 1; + + pb->recv_time = get_time (); + + probe_done (pb); + + return; + } + + + /* ICMP stuff */ + + if (!(revents & POLLIN)) + return; + + recv_reply (icmp_sk, 0, tcp_check_reply); +} + + +static void tcp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +static tr_module tcp_ops = { + .name = "tcpconn", + .init = tcp_init, + .send_probe = tcp_send_probe, + .recv_probe = tcp_recv_probe, + .expire_probe = tcp_expire_probe, +}; + +TR_MODULE (tcp_ops); + +void tr_module_tcpconn_insert() +{ + +} diff --git a/iputils/traceroute/mod-udp.c b/iputils/traceroute/mod-udp.c new file mode 100755 index 0000000000..b2d319d7f8 --- /dev/null +++ b/iputils/traceroute/mod-udp.c @@ -0,0 +1,239 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/in.h> +#include <netinet/udp.h> + +#include "traceroute.h" + + +#ifndef IPPROTO_UDPLITE +#define IPPROTO_UDPLITE 136 +#endif + +#ifndef UDPLITE_SEND_CSCOV +#define UDPLITE_SEND_CSCOV 10 +#define UDPLITE_RECV_CSCOV 11 +#endif + + +static sockaddr_any dest_addr = {{ 0, }, }; +static unsigned int curr_port = 0; +static unsigned int protocol = IPPROTO_UDP; + + +static char *data = NULL; +static size_t *length_p; + +static void fill_data (size_t *packet_len_p) { + int i; + + length_p = packet_len_p; + + if (*length_p && + !(data = malloc (*length_p)) + ) error ("malloc"); + + for (i = 0; i < *length_p; i++) + data[i] = 0x40 + (i & 0x3f); + + return; +} + + +static int udp_default_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + + curr_port = port_seq ? port_seq : DEF_START_PORT; + + dest_addr = *dest; + dest_addr.sin.sin_port = htons (curr_port); + + fill_data (packet_len_p); + + return 0; +} + + +static int udp_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + + dest_addr = *dest; + + if (!port_seq) port_seq = DEF_UDP_PORT; + dest_addr.sin.sin_port = htons ((uint16_t) port_seq); + + fill_data (packet_len_p); + + return 0; +} + + +static unsigned int coverage = 0; +#define MIN_COVERAGE (sizeof (struct udphdr)) + +static void set_coverage (int sk) { + int val = MIN_COVERAGE; + + if (setsockopt (sk, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV, + &coverage, sizeof (coverage)) < 0 + ) error ("UDPLITE_SEND_CSCOV"); + + if (setsockopt (sk, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV, + &val, sizeof (val)) < 0 + ) error ("UDPLITE_RECV_CSCOV"); +} + +static CLIF_option udplite_options[] = { + { 0, "coverage", "NUM", "Set udplite send coverage to %s (default is " + _TEXT(MIN_COVERAGE) ")", + CLIF_set_uint, &coverage, 0, CLIF_ABBREV }, + CLIF_END_OPTION +}; + +static int udplite_init (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len_p) { + + dest_addr = *dest; + + if (!port_seq) port_seq = DEF_UDP_PORT; /* XXX: Hmmm... */ + dest_addr.sin.sin_port = htons ((uint16_t) port_seq); + + protocol = IPPROTO_UDPLITE; + + if (!coverage) coverage = MIN_COVERAGE; + + fill_data (packet_len_p); + + return 0; +} + + +static void udp_send_probe (probe *pb, int ttl) { + int sk; + int af = dest_addr.sa.sa_family; + + + sk = socket (af, SOCK_DGRAM, protocol); + if (sk < 0) error ("socket"); + + tune_socket (sk); /* common stuff */ + + if (coverage) set_coverage (sk); /* udplite case */ + + set_ttl (sk, ttl); + + + if (connect (sk, &dest_addr.sa, sizeof (dest_addr)) < 0) + error ("connect"); + + use_recverr (sk); + + + pb->send_time = get_time (); + + if (do_send (sk, data, *length_p, NULL) < 0) { + close (sk); + pb->send_time = 0; + return; + } + + + pb->sk = sk; + + add_poll (sk, POLLIN | POLLERR); + + pb->seq = dest_addr.sin.sin_port; + + if (curr_port) { /* traditional udp method */ + curr_port++; + dest_addr.sin.sin_port = htons (curr_port); /* both ipv4 and ipv6 */ + } + + return; +} + + +static probe *udp_check_reply (int sk, int err, sockaddr_any *from, + char *buf, size_t len) { + probe *pb; + + pb = probe_by_sk (sk); + if (!pb) return NULL; + + if (pb->seq != from->sin.sin_port) + return NULL; + + if (!err) pb->final = 1; + + return pb; +} + + +static void udp_recv_probe (int sk, int revents) { + + if (!(revents & (POLLIN | POLLERR))) + return; + + recv_reply (sk, !!(revents & POLLERR), udp_check_reply); +} + + +static void udp_expire_probe (probe *pb) { + + probe_done (pb); +} + + +/* All three modules share the same methods except the init... */ + +static tr_module default_ops = { + .name = "default", + .init = udp_default_init, + .send_probe = udp_send_probe, + .recv_probe = udp_recv_probe, + .expire_probe = udp_expire_probe, + .header_len = sizeof (struct udphdr), +}; + +TR_MODULE (default_ops); + + +static tr_module udp_ops = { + .name = "udp", + .init = udp_init, + .send_probe = udp_send_probe, + .recv_probe = udp_recv_probe, + .expire_probe = udp_expire_probe, + .header_len = sizeof (struct udphdr), +}; + +TR_MODULE (udp_ops); + + +static tr_module udplite_ops = { + .name = "udplite", + .init = udplite_init, + .send_probe = udp_send_probe, + .recv_probe = udp_recv_probe, + .expire_probe = udp_expire_probe, + .header_len = sizeof (struct udphdr), + .options = udplite_options, +}; + +TR_MODULE (udplite_ops); + +void tr_module_udp_insert() +{ + +} diff --git a/iputils/traceroute/module.c b/iputils/traceroute/module.c new file mode 100755 index 0000000000..f8f228ef63 --- /dev/null +++ b/iputils/traceroute/module.c @@ -0,0 +1,51 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. + */ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +#include "traceroute.h" + +void tr_module_icmp_insert(); +void tr_module_udp_insert(); +void tr_module_tcp_insert(); +void tr_module_tcpconn_insert(); +void tr_module_raw_insert(); +void tr_module_dccp_insert(); + +static tr_module *base = NULL; + +void tr_register_module(tr_module *ops) { + + ops->next = base; + base = ops; +// printf("tr_register_module name=%s\n", ops->name); +} + +const tr_module *tr_get_module(const char *name) { + const tr_module *ops; + + tr_module_icmp_insert(); + tr_module_udp_insert(); + tr_module_tcp_insert(); + tr_module_tcpconn_insert(); + tr_module_raw_insert(); + tr_module_dccp_insert(); + + if(!name) + return 0; + + for(ops = base; ops; ops = ops->next) { + if(!strcasecmp(name, ops->name)) + return ops; + } + + return NULL; +} diff --git a/iputils/traceroute/poll.c b/iputils/traceroute/poll.c new file mode 100755 index 0000000000..988c456030 --- /dev/null +++ b/iputils/traceroute/poll.c @@ -0,0 +1,93 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <poll.h> +#include <errno.h> +#include <math.h> + +#include "traceroute.h" + + +static struct pollfd *pfd = NULL; +static unsigned int num_polls = 0; + + +void add_poll (int fd, int events) { + int i; + + for (i = 0; i < num_polls && pfd[i].fd > 0; i++) ; + + if (i == num_polls) { + pfd = realloc (pfd, ++num_polls * sizeof (*pfd)); + if (!pfd) error ("realloc"); + } + + pfd[i].fd = fd; + pfd[i].events = events; +} + + +void del_poll (int fd) { + int i; + + for (i = 0; i < num_polls && pfd[i].fd != fd; i++) ; + + if (i < num_polls) pfd[i].fd = -1; /* or just zero it... */ +} + + +static int cleanup_polls (void) { + int i; + + for (i = 0; i < num_polls && pfd[i].fd > 0; i++) ; + + if (i < num_polls) { /* a hole have found */ + int j; + + for (j = i + 1; j < num_polls; j++) { + if (pfd[j].fd > 0) { + pfd[i++] = pfd[j]; + pfd[j].fd = -1; + } + } + } + + return i; +} + + +void do_poll (double timeout, void (*callback) (int fd, int revents)) { + int nfds; + int msecs = ceil (timeout * 1000); + + while ((nfds = cleanup_polls ()) > 0) { + int i, n; + + n = poll (pfd, nfds, msecs); + + if (n <= 0) { + if (n == 0 || errno == EINTR) + return; + error ("poll"); + } + + for (i = 0; n && i < num_polls; i++) { + if (pfd[i].revents) { + callback (pfd[i].fd, pfd[i].revents); + n--; + } + } + + msecs = 0; /* no more wait, just eat all the pending */ + } + + return; +} + diff --git a/iputils/traceroute/random.c b/iputils/traceroute/random.c new file mode 100755 index 0000000000..5a8f911e14 --- /dev/null +++ b/iputils/traceroute/random.c @@ -0,0 +1,28 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/times.h> + +#include "traceroute.h" + + +static void __init_random_seq (void) __attribute__ ((constructor)); +static void __init_random_seq (void) { + + srand (times (NULL) + getpid ()); +} + + +unsigned int random_seq (void) { + + /* To not worry about RANDOM_MAX and precision... */ + return (rand () << 16) ^ (rand () << 8) ^ rand () ^ (rand () >> 8); +} + diff --git a/iputils/traceroute/time.c b/iputils/traceroute/time.c new file mode 100755 index 0000000000..16ea82483c --- /dev/null +++ b/iputils/traceroute/time.c @@ -0,0 +1,27 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> + +#include "traceroute.h" + + +/* Just returns current time as double, with most possible precision... */ + +double get_time (void) { + struct timeval tv; + double d; + + gettimeofday (&tv, NULL); + + d = ((double) tv.tv_usec) / 1000000. + (unsigned long) tv.tv_sec; + + return d; +} diff --git a/iputils/traceroute/traceroute.c b/iputils/traceroute/traceroute.c new file mode 100755 index 0000000000..b0e92bc0eb --- /dev/null +++ b/iputils/traceroute/traceroute.c @@ -0,0 +1,1731 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <poll.h> +#include <netinet/icmp6.h> +#include <netinet/ip_icmp.h> +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <netdb.h> +#include <errno.h> +#include <locale.h> +#include <sys/utsname.h> +#include <linux/types.h> +#include <linux/errqueue.h> + +/* XXX: Remove this when things will be defined properly in netinet/ ... */ +#include "flowlabel.h" + +#include <clif.h> +//#include "version.h" +#include <stdbool.h> +#include <glib.h> +#include "traceroute.h" +#include "../iputils.h" + +#define UNUSED(x) (void)(x) + +#ifndef ICMP6_DST_UNREACH_BEYONDSCOPE +#ifdef ICMP6_DST_UNREACH_NOTNEIGHBOR +#define ICMP6_DST_UNREACH_BEYONDSCOPE ICMP6_DST_UNREACH_NOTNEIGHBOR +#else +#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 +#endif +#endif + +#ifndef IPV6_RECVHOPLIMIT +#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT +#endif + +#ifndef IP_PMTUDISC_PROBE +#define IP_PMTUDISC_PROBE 3 +#endif + +#ifndef IPV6_PMTUDISC_PROBE +#define IPV6_PMTUDISC_PROBE 3 +#endif + +#ifndef AI_IDN +#define AI_IDN 0 +#endif + +#ifndef NI_IDN +#define NI_IDN 0 +#endif + +#define MAX_HOPS 255 +#define MAX_PROBES 10 +#define MAX_GATEWAYS_4 8 +#define MAX_GATEWAYS_6 127 +#define DEF_HOPS 30 +#define DEF_SIM_PROBES 16 /* including several hops */ +#define DEF_NUM_PROBES 3 //1 +#define DEF_WAIT_SECS 5.0 +#define DEF_HERE_FACTOR 3 +#define DEF_NEAR_FACTOR 10 +#ifndef DEF_WAIT_PREC +#define DEF_WAIT_PREC 0.001 /* +1 ms to avoid precision issues */ +#endif +#define DEF_SEND_SECS 0 +#define DEF_DATA_LEN 40 /* all but IP header... */ +#define MAX_PACKET_LEN 65000 +#ifndef DEF_AF +#define DEF_AF AF_INET +#endif + +#define ttl2hops(X) (((X) <= 64 ? 65 : ((X) <= 128 ? 129 : 256)) - (X)) + +static char version_string[] = "Modern traceroute for Linux, " + "version " _TEXT(VERSION) +"\nCopyright (c) 2016 Dmitry Butskoy, " +" License: GPL v2 or any later"; +static int debug = 0; +static unsigned int first_hop = 1; +static unsigned int max_hops = DEF_HOPS; +static unsigned int sim_probes = DEF_SIM_PROBES; +static unsigned int probes_per_hop = DEF_NUM_PROBES; + +static char **gateways = NULL; +static int num_gateways = 0; +static unsigned char *rtbuf = NULL; +static size_t rtbuf_len = 0; +static unsigned int ipv6_rthdr_type = 2; /* IPV6_RTHDR_TYPE_2 */ + +static size_t header_len = 0; +static size_t data_len = 0; + +static int dontfrag = 0; +static int noresolve = 0; +static int extension = 0; +static int as_lookups = 0; +static unsigned int dst_port_seq = 0; +static unsigned int tos = 0; +static unsigned int flow_label = 0; +static int noroute = 0; +static unsigned int fwmark = 0; +static int packet_len = -1; +static double wait_secs = DEF_WAIT_SECS; +static double here_factor = DEF_HERE_FACTOR; +static double near_factor = DEF_NEAR_FACTOR; +static double send_secs = DEF_SEND_SECS; +static int mtudisc = 0; +static int backward = 0; + +static sockaddr_any dst_addr = { { 0, }, }; +static char *dst_name = NULL; +static char *device = NULL; +static sockaddr_any src_addr = { { 0, }, }; +static unsigned int src_port = 0; + +static const char *module = "default"; +static const tr_module *ops = NULL; + +static char *opts[16] = { NULL, }; /* assume enough */ +static unsigned int opts_idx = 1; /* first one reserved... */ + +static int af = 0; + +static probe *probes = NULL; +static unsigned int num_probes = 0; + +static void ex_error(const char *format, ...) { + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + + fprintf(stderr, "\n"); + + //exit(2); +} + +void error(const char *str) { + + fprintf(stderr, "\n"); + + perror(str); + + exit(1); +} + +void error_or_perm(const char *str) { + + if(errno == EPERM) + fprintf(stderr, "You do not have enough privileges to use " + "this traceroute method."); + error(str); +} + +/* Set initial parameters according to how we was called */ + +static void check_progname(const char *name) { + const char *p; + int l; + + p = strrchr(name, '/'); + if(p) + p++; + else + p = name; + + l = strlen(p); + if(l <= 0) + return; + l--; + + if(p[l] == '6') + af = AF_INET6; + else if(p[l] == '4') + af = AF_INET; + + if(!strncmp(p, "tcp", 3)) + module = "tcp"; + if(!strncmp(p, "tracert", 7)) + module = "icmp"; + + return; +} + +static int getaddr(const char *name, sockaddr_any *addr) { + int ret; + struct addrinfo hints, *ai, *res = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_flags = AI_IDN; + + ret = getaddrinfo(name, NULL, &hints, &res); + if(ret) { + fprintf(stderr, "%s: %s\n", name, gai_strerror(ret)); + return -1; + } + + for(ai = res; ai; ai = ai->ai_next) { + if(ai->ai_family == af) + break; + /* when af not specified, choose DEF_AF if present */ + if(!af && ai->ai_family == DEF_AF) + break; + } + if(!ai) + ai = res; /* anything... */ + + if(ai->ai_addrlen > sizeof(*addr)) + return -1; /* paranoia */ + memcpy(addr, ai->ai_addr, ai->ai_addrlen); + + freeaddrinfo(res); + + return 0; +} + +static void make_fd_used(int fd) { + int nfd; + + if(fcntl(fd, F_GETFL) != -1) + return; + + if(errno != EBADF) + error("fcntl F_GETFL"); + + nfd = open("/dev/null", O_RDONLY); + if(nfd < 0) + error("open /dev/null"); + + if(nfd != fd) { + dup2(nfd, fd); + close(nfd); + } + + return; +} + +static char addr2str_buf[INET6_ADDRSTRLEN]; + +static const char *addr2str(const sockaddr_any *addr) { + + getnameinfo(&addr->sa, sizeof(*addr), + addr2str_buf, sizeof(addr2str_buf), 0, 0, NI_NUMERICHOST); + + return addr2str_buf; +} + +/* IP options stuff */ + +static int init_ip_options(void) { + sockaddr_any *gates; + int i, max; + + if(!num_gateways) + return 0; + + /* check for TYPE,ADDR,ADDR... form */ + if(af == AF_INET6 && num_gateways > 1 && gateways[0]) { + char *q; + unsigned int value = strtoul(gateways[0], &q, 0); + + if(!*q) { + ipv6_rthdr_type = value; + num_gateways--; + for(i = 0; i < num_gateways; i++) + gateways[i] = gateways[i + 1]; + } + } + + max = af == AF_INET ? MAX_GATEWAYS_4 : MAX_GATEWAYS_6; + if(num_gateways > max) { + ex_error("Too many gateways specified. No more than %d", max); + return -1; + } + + gates = alloca(num_gateways * sizeof(*gates)); + + for(i = 0; i < num_gateways; i++) { + + if(!gateways[i]) + error("strdup"); + + if(getaddr(gateways[i], &gates[i]) < 0) { + ex_error(""); /* already reported */ + return -1; + } + if(gates[i].sa.sa_family != af) { + ex_error("IP versions mismatch in gateway addresses"); + return -1; + } + + free(gateways[i]); + } + + free(gateways); + gateways = NULL; + + if(af == AF_INET) { + struct in_addr *in; + + rtbuf_len = 4 + (num_gateways + 1) * sizeof(*in); + rtbuf = malloc(rtbuf_len); + if(!rtbuf) + error("malloc"); + + in = (struct in_addr *) &rtbuf[4]; + for(i = 0; i < num_gateways; i++) + memcpy(&in[i], &gates[i].sin.sin_addr, sizeof(*in)); + /* final hop */ + memcpy(&in[i], &dst_addr.sin.sin_addr, sizeof(*in)); + i++; + + rtbuf[0] = IPOPT_NOP; + rtbuf[1] = IPOPT_LSRR; + rtbuf[2] = (i * sizeof(*in)) + 3; + rtbuf[3] = IPOPT_MINOFF; + + } + else if(af == AF_INET6) { + struct in6_addr *in6; + struct ip6_rthdr *rth; + + /* IPV6_RTHDR_TYPE_0 length is 8 */ + rtbuf_len = 8 + num_gateways * sizeof(*in6); + rtbuf = malloc(rtbuf_len); + if(!rtbuf) + error("malloc"); + + rth = (struct ip6_rthdr *) rtbuf; + rth->ip6r_nxt = 0; + rth->ip6r_len = 2 * num_gateways; + rth->ip6r_type = ipv6_rthdr_type; + rth->ip6r_segleft = num_gateways; + + *((uint32_t *) (rth + 1)) = 0; + + in6 = (struct in6_addr *) (rtbuf + 8); + for(i = 0; i < num_gateways; i++) + memcpy(&in6[i], &gates[i].sin6.sin6_addr, sizeof(*in6)); + } + + return 0; +} + +/* Command line stuff */ +__attribute__((unused)) +static int set_af(CLIF_option *optn, char *arg) { + UNUSED(arg); + int vers = (long) optn->data; + + if(vers == 4) + af = AF_INET; + else if(vers == 6) + af = AF_INET6; + else + return -1; + + return 0; +} + +static int add_gateway(CLIF_option *optn, char *arg) { + UNUSED(optn); + if(num_gateways >= MAX_GATEWAYS_6) { /* 127 > 8 ... :) */ + fprintf(stderr, "Too many gateways specified."); + return -1; + } + + gateways = realloc(gateways, (num_gateways + 1) * sizeof(*gateways)); + if(!gateways) + error("malloc"); + gateways[num_gateways++] = strdup(arg); + + return 0; +} + +static int set_source(CLIF_option *optn, char *arg) { + UNUSED(optn); + return getaddr(arg, &src_addr); +} + +static int set_port(CLIF_option *optn, char *arg) { + unsigned int *up = (unsigned int *) optn->data; + char *q; + + *up = strtoul(arg, &q, 0); + if(q == arg) { + struct servent *s = getservbyname(arg, NULL); + + if(!s) + return -1; + *up = ntohs(s->s_port); + } + + return 0; +} + +static int set_module(CLIF_option *optn, char *arg) { + UNUSED(arg); + module = (char *) optn->data; + + return 0; +} + +static int set_mod_option(CLIF_option *optn, char *arg) { + UNUSED(optn); + if(!strcmp(arg, "help")) { + const tr_module *mod = tr_get_module(module); + + if(mod && mod->options) { + /* just to set common keyword flag... */ + CLIF_parse(1, &arg, 0, 0, CLIF_KEYWORD); + CLIF_print_options(NULL, mod->options); + } else + fprintf(stderr, "No options for module `%s'\n", module); + + exit(0); + } + + if(opts_idx >= sizeof(opts) / sizeof(*opts)) { + fprintf(stderr, "Too many module options\n"); + return -1; + } + + opts[opts_idx] = strdup(arg); + if(!opts[opts_idx]) + error("strdup"); + opts_idx++; + + return 0; +} + +static int set_raw(CLIF_option *optn, char *arg) { + char buf[1024]; + + module = "raw"; + + snprintf(buf, sizeof(buf), "protocol=%s", arg); + return set_mod_option(optn, buf); +} + +static int set_wait_specs(CLIF_option *optn, char *arg) { + UNUSED(optn); + char *p, *q; + + here_factor = near_factor = 0; + + wait_secs = strtod(p = arg, &q); + if(q == p) + return -1; + if(!*q++) + return 0; + + here_factor = strtod(p = q, &q); + if(q == p) + return -1; + if(!*q++) + return 0; + + near_factor = strtod(p = q, &q); + if(q == p || *q) + return -1; + + return 0; +} + +static int set_host(CLIF_argument *argm, char *arg, int index) { + UNUSED(argm); + UNUSED(index); + if(getaddr(arg, &dst_addr) < 0) + return -1; + + dst_name = arg; + + /* i.e., guess it by the addr in cmdline... */ + if(!af) + af = dst_addr.sa.sa_family; + + return 0; +} + +static CLIF_option option_list[] = { + { "4", 0, 0, "Use IPv4", set_af, (void *) 4, 0, CLIF_EXTRA }, + { "6", 0, 0, "Use IPv6", set_af, (void *) 6, 0, 0 }, + { "d", "debug", 0, "Enable socket level debugging", + CLIF_set_flag, &debug, 0, 0 }, + { "F", "dont-fragment", 0, "Do not fragment packets", + CLIF_set_flag, &dontfrag, 0, CLIF_ABBREV }, + { "f", "first", "first_ttl", "Start from the %s hop (instead from 1)", + CLIF_set_uint, &first_hop, 0, 0 }, + { "g", "gateway", "gate", "Route packets through the specified gateway " + "(maximum " _TEXT(MAX_GATEWAYS_4) " for IPv4 and " + _TEXT(MAX_GATEWAYS_6) " for IPv6)", + add_gateway, 0, 0, CLIF_SEVERAL }, + { "I", "icmp", 0, "Use ICMP ECHO for tracerouting", + set_module, "icmp", 0, 0 }, + { "T", "tcp", 0, "Use TCP SYN for tracerouting (default " + "port is " _TEXT(DEF_TCP_PORT) ")", + set_module, "tcp", 0, 0 }, + { "i", "interface", "device", "Specify a network interface " + "to operate with", + CLIF_set_string, &device, 0, 0 }, + { "m", "max-hops", "max_ttl", "Set the max number of hops (max TTL " + "to be reached). Default is " _TEXT(DEF_HOPS), + CLIF_set_uint, &max_hops, 0, 0 }, + { "N", "sim-queries", "squeries", "Set the number of probes " + "to be tried simultaneously (default is " + _TEXT(DEF_SIM_PROBES) ")", + CLIF_set_uint, &sim_probes, 0, 0 }, + { "n", 0, 0, "Do not resolve IP addresses to their domain names", + CLIF_set_flag, &noresolve, 0, 0 }, + { "p", "port", "port", "Set the destination port to use. " + "It is either initial udp port value for " + "\"default\" method (incremented by each probe, " + "default is " _TEXT(DEF_START_PORT) "), " + "or initial seq for \"icmp\" (incremented as well, " + "default from 1), or some constant destination port" + " for other methods (with default of " + _TEXT(DEF_TCP_PORT) " for \"tcp\", " + _TEXT(DEF_UDP_PORT) " for \"udp\", etc.)", + set_port, &dst_port_seq, 0, 0 }, + { "t", "tos", "tos", "Set the TOS (IPv4 type of service) or TC " + "(IPv6 traffic class) value for outgoing packets", + CLIF_set_uint, &tos, 0, 0 }, + { "l", "flowlabel", "flow_label", "Use specified %s for IPv6 packets", + CLIF_set_uint, &flow_label, 0, 0 }, + { "w", "wait", "MAX,HERE,NEAR", "Wait for a probe no more than HERE " + "(default " _TEXT(DEF_HERE_FACTOR) ") times longer " + "than a response from the same hop, or no more " + "than NEAR (default " _TEXT(DEF_NEAR_FACTOR) ") " + "times than some next hop, or MAX (default " + _TEXT(DEF_WAIT_SECS) ") seconds " + "(float point values allowed too)", + set_wait_specs, 0, 0, 0 }, + { "q", "queries", "nqueries", "Set the number of probes per each hop. " + "Default is " _TEXT(DEF_NUM_PROBES), + CLIF_set_uint, &probes_per_hop, 0, 0 }, + { "r", 0, 0, "Bypass the normal routing and send directly to a host " + "on an attached network", + CLIF_set_flag, &noroute, 0, 0 }, + { "s", "source", "src_addr", "Use source %s for outgoing packets", + set_source, 0, 0, 0 }, + { "z", "sendwait", "sendwait", "Minimal time interval between probes " + "(default " _TEXT(DEF_SEND_SECS) "). If the value " + "is more than 10, then it specifies a number " + "in milliseconds, else it is a number of seconds " + "(float point values allowed too)", + CLIF_set_double, &send_secs, 0, 0 }, + { "e", "extensions", 0, "Show ICMP extensions (if present), " + "including MPLS", + CLIF_set_flag, &extension, 0, CLIF_ABBREV }, + { "A", "as-path-lookups", 0, "Perform AS path lookups in routing " + "registries and print results directly after " + "the corresponding addresses", + CLIF_set_flag, &as_lookups, 0, 0 }, + { "M", "module", "name", "Use specified module (either builtin or " + "external) for traceroute operations. Most methods " + "have their shortcuts (`-I' means `-M icmp' etc.)", + CLIF_set_string, &module, 0, CLIF_EXTRA }, + { "O", "options", "OPTS", "Use module-specific option %s for the " + "traceroute module. Several %s allowed, separated " + "by comma. If %s is \"help\", print info about " + "available options", + set_mod_option, 0, 0, CLIF_SEVERAL | CLIF_EXTRA }, + { 0, "sport", "num", "Use source port %s for outgoing packets. " + "Implies `-N 1'", + set_port, &src_port, 0, CLIF_EXTRA }, + #ifdef SO_MARK + { 0, "fwmark", "num", "Set firewall mark for outgoing packets", + CLIF_set_uint, &fwmark, 0, 0 }, + #endif + { "U", "udp", 0, "Use UDP to particular port for tracerouting " + "(instead of increasing the port per each probe), " + "default port is " _TEXT(DEF_UDP_PORT), + set_module, "udp", 0, CLIF_EXTRA }, + { 0, "UL", 0, "Use UDPLITE for tracerouting (default dest port is " + _TEXT(DEF_UDP_PORT) ")", + set_module, "udplite", 0, CLIF_ONEDASH | CLIF_EXTRA }, + { "D", "dccp", 0, "Use DCCP Request for tracerouting (default " + "port is " _TEXT(DEF_DCCP_PORT) ")", + set_module, "dccp", 0, CLIF_EXTRA }, + { "P", "protocol", "prot", "Use raw packet of protocol %s " + "for tracerouting", + set_raw, 0, 0, CLIF_EXTRA }, + { 0, "mtu", 0, "Discover MTU along the path being traced. " + "Implies `-F -N 1'", + CLIF_set_flag, &mtudisc, 0, CLIF_EXTRA }, + { 0, "back", 0, "Guess the number of hops in the backward path " + "and print if it differs", + CLIF_set_flag, &backward, 0, CLIF_EXTRA }, + CLIF_VERSION_OPTION (version_string), + CLIF_HELP_OPTION, + CLIF_END_OPTION + }; + +static CLIF_argument arg_list[] = { + { "host", "The host to traceroute to", + set_host, 0, CLIF_STRICT }, + { "packetlen", "The full packet length (default is the length of " + "an IP header plus " _TEXT(DEF_DATA_LEN) "). Can be " + "ignored or increased to a minimal allowed value", + CLIF_arg_int, &packet_len, 0 }, + CLIF_END_ARGUMENT + }; + +static void do_it(void); + +int traceroute_main(int argc, char *argv[]) { + + setlocale(LC_ALL, ""); + setlocale(LC_NUMERIC, "C"); /* avoid commas in msec printed */ + + check_progname(argv[0]); + + if(CLIF_parse(argc, argv, option_list, arg_list, + CLIF_MAY_JOIN_ARG | CLIF_HELP_EMPTY) < 0 + ) + return -1; //exit(2); + + ops = tr_get_module(module); + if(!ops) { + ex_error("Unknown traceroute module %s", module); + return -1; + } + + if(!first_hop || first_hop > max_hops) { + ex_error("first hop out of range"); + return -1; + } + if(max_hops > MAX_HOPS) { + ex_error("max hops cannot be more than " _TEXT(MAX_HOPS)); + return -1; + } + if(!probes_per_hop || probes_per_hop > MAX_PROBES) { + ex_error("no more than " _TEXT(MAX_PROBES) " probes per hop"); + return -1; + } + if(wait_secs < 0 || here_factor < 0 || near_factor < 0) { + ex_error("bad wait specifications `%g,%g,%g' used", + wait_secs, here_factor, near_factor); + return -1; + } + if(packet_len > MAX_PACKET_LEN) { + ex_error("too big packetlen %d specified", packet_len); + return -1; + } + if(src_addr.sa.sa_family && src_addr.sa.sa_family != af) { + ex_error("IP version mismatch in addresses specified"); + return -1; + } + if(send_secs < 0) { + ex_error("bad sendtime `%g' specified", send_secs); + return -1; + } + if(send_secs >= 10) /* it is milliseconds */ + send_secs /= 1000; + + if(af == AF_INET6 && (tos || flow_label)) + dst_addr.sin6.sin6_flowinfo = + htonl(((tos & 0xff) << 20) | (flow_label & 0x000fffff)); + + if(src_port) { + src_addr.sin.sin_port = htons((uint16_t) src_port); + src_addr.sa.sa_family = af; + } + + if(src_port || ops->one_per_time) { + sim_probes = 1; + here_factor = near_factor = 0; + } + + /* make sure we don't std{in,out,err} to open sockets */ + make_fd_used(0); + make_fd_used(1); + make_fd_used(2); + + if(init_ip_options() == -1) + return -1; + + header_len = (af == AF_INET ? sizeof(struct iphdr) + : + sizeof(struct ip6_hdr)) + + rtbuf_len + ops->header_len; + + if(mtudisc) { + dontfrag = 1; + sim_probes = 1; + if(packet_len < 0) + packet_len = MAX_PACKET_LEN; + } + + if(packet_len < 0) { + if(DEF_DATA_LEN >= ops->header_len) + data_len = DEF_DATA_LEN - ops->header_len; + } else { + if(packet_len >= (int) header_len) + data_len = packet_len - header_len; + } + + num_probes = max_hops * probes_per_hop; + probes = calloc(num_probes, sizeof(*probes)); + if(!probes) + error("calloc"); + + if(ops->options && opts_idx > 1) { + opts[0] = strdup(module); /* aka argv[0] ... */ + if(CLIF_parse(opts_idx, opts, ops->options, 0, CLIF_KEYWORD) < 0) + return -1; //exit(2); + } + + if(ops->init(&dst_addr, dst_port_seq, &data_len) < 0) + ex_error("trace method's init failed"); + + do_it(); + return 0; +} + +/* PRINT STUFF */ + +static void print_header(void) { + + /* Note, without ending new-line! */ + log_printf("traceroute to %s (%s), %u hops max, %zu byte packets", + dst_name, addr2str(&dst_addr), max_hops, + header_len + data_len); + fflush(stdout); +} + +static bool print_addr(sockaddr_any *res) { + bool is_final = false; + const char *str; + + if(!res->sa.sa_family) + return is_final; + + str = addr2str(res); + + if(noresolve) { + log_printf("%s", str); + //ret_str = g_strdup_printf("%s", str); + if(!strcmp(dst_name, str)) + is_final = true; + } + else { + char buf[1024]; + + buf[0] = '\0'; + getnameinfo(&res->sa, sizeof(*res), buf, sizeof(buf), + 0, 0, NI_IDN); + log_printf(" %s (%s)", buf[0] ? buf : str, str); + //ret_str = g_strdup_printf("%s", buf[0] ? buf : str); + if(buf[0] && !strcmp(dst_name, buf)) + is_final = true; + } + + if(as_lookups) + log_printf(" [%s]", get_as_path(str)); + + return is_final; +} + +static bool print_probe(probe *pb) { + bool is_final = false; + unsigned int idx = (pb - probes); + unsigned int ttl = idx / probes_per_hop + 1; + unsigned int np = idx % probes_per_hop; + + if(np == 0) + log_printf("\n%2u ", ttl); + + if(!pb->res.sa.sa_family) + log_printf(" *"); + else { + int prn = !np; /* print if the first... */ + + if(np) { /* ...and if differs with previous */ + probe *p; + + /* skip expired */ + for(p = pb - 1; np && !p->res.sa.sa_family; p--, np--) + ; + + if(!np || + !equal_addr(&p->res, &pb->res) || + (p->ext != pb->ext && + !(p->ext && pb->ext && !strcmp(p->ext, pb->ext))) || + (backward && p->recv_ttl != pb->recv_ttl) + ) + prn = 1; + } + + if(prn) { + is_final = print_addr(&pb->res); + if(pb->ext) + log_printf(" <%s>", pb->ext); + + if(backward && pb->recv_ttl) { + int hops = ttl2hops(pb->recv_ttl); + if(hops != (int) ttl) + log_printf(" '-%d'", hops); + } + } + } + + if(pb->recv_time) { + double diff = pb->recv_time - pb->send_time; + + log_printf(" %.3f ms", diff * 1000); + } + + if(pb->err_str[0]) + log_printf(" %s", pb->err_str); + + fflush(stdout); + + return is_final; +} + +static void print_end(void) { + + log_printf("\n"); +} + +/* Compute timeout stuff */ + +static double get_timeout(probe *pb) { + double value; + + if(here_factor) { + /* check for already replied from the same hop */ + int i, idx = (pb - probes); + probe *p = &probes[idx - (idx % probes_per_hop)]; + + for(i = 0; i < (int) probes_per_hop; i++, p++) { + /* `p == pb' skipped since !pb->done */ + + if(p->done && (value = p->recv_time - p->send_time) > 0) { + value += DEF_WAIT_PREC; + value *= here_factor; + return value < wait_secs ? value : wait_secs; + } + } + } + + if(near_factor) { + /* check forward for already replied */ + probe *p, *endp = probes + num_probes; + + for(p = pb + 1; p < endp && p->send_time; p++) { + + if(p->done && (value = p->recv_time - p->send_time) > 0) { + value += DEF_WAIT_PREC; + value *= near_factor; + return value < wait_secs ? value : wait_secs; + } + } + } + + return wait_secs; +} + +/* Check expiration stuff */ + +static void check_expired(probe *pb) { + int idx = (pb - probes); + probe *p, *endp = probes + num_probes; + probe *fp = NULL, *pfp = NULL; + + if(!pb->done) /* an ops method still not release it */ + return; + + /* check all the previous in the same hop */ + for(p = &probes[idx - (idx % probes_per_hop)]; p < pb; p++) { + + if(!p->done || /* too early to decide something */ + !p->final /* already ttl-exceeded in the same hop */ + ) + return; + + pfp = p; /* some of the previous probes is final */ + } + + /* check forward all the sent probes */ + for(p = pb + 1; p < endp && p->send_time; p++) { + + if(p->done) { /* some next probe already done... */ + if(!p->final) /* ...was ttl-exceeded. OK, we are expired. */ + return; + else { + fp = p; + break; + } + } + } + + if(!fp) /* no any final probe found. Assume expired. */ + return; + + /* Well. There is a situation "*(this) * * * * ... * * final" + We cannot guarantee that "final" is in its right place. + We've sent "sim_probes" simultaneously, and the final hop + can drop some of them and answer only for latest ones. + If we can detect/assume that it so, then just put "final" + to the (pseudo-expired) "this" place. + */ + + /* It seems that the case of "answers for latest ones only" + occurs mostly with icmp_unreach error answers ("!H" etc.). + Icmp_echoreply, tcp_reset and even icmp_port_unreach looks + like going in the right order. + */ + if(!fp->err_str[0]) /* not an icmp_unreach error report... */ + return; + + if(pfp || + (idx % probes_per_hop) + (fp - pb) < probes_per_hop + ) { + /* Either some previous (pfp) or some next probe + in this hop is final. It means that the whole hop is final. + Do the replace (it also causes further "final"s to be shifted + here too). + */ + goto replace_by_final; + } + + /* If the final probe is an icmp_unreachable report + (either in a case of some error, like "!H", or just port_unreach), + it could follow the "time-exceed" report from the *same* hop. + */ + for(p = pb - 1; p >= probes; p--) { + if(equal_addr(&p->res, &fp->res)) { + /* ...Yes. Put "final" to the "this" place. */ + goto replace_by_final; + } + } + + if(fp->recv_ttl) { + /* Consider the ttl value of the report packet and guess where + the "final" should be. If it seems that it should be + in the same hop as "this", then do replace. + */ + int back_hops, ttl; + + /* We assume that the reporting one has an initial ttl value + of either 64, or 128, or 255. It is most widely used + in the modern routers and computers. + The idea comes from tracepath(1) routine. + */ + back_hops = ttl2hops(fp->recv_ttl); + + /* It is possible that the back path differs from the forward + and therefore has different number of hops. To minimize + such an influence, get the nearest previous time-exceeded + probe and compare with it. + */ + for(p = pb - 1; p >= probes; p--) { + if(p->done && !p->final && p->recv_ttl) { + int hops = ttl2hops(p->recv_ttl); + + if(hops < back_hops) { + ttl = (p - probes) / probes_per_hop + 1; + back_hops = (back_hops - hops) + ttl; + break; + } + } + } + + ttl = idx / probes_per_hop + 1; + if(back_hops == ttl) + /* Yes! It seems that "final" should be at "this" place */ + goto replace_by_final; + else if(back_hops < ttl) + /* Hmmm... Assume better to replace here too... */ + goto replace_by_final; + + } + + /* No idea what to do. Assume expired. */ + + return; + + replace_by_final: + + *pb = *fp; + + memset(fp, 0, sizeof(*fp)); + /* block extra re-send */ + fp->send_time = 1.; + + return; +} + +probe *probe_by_seq(int seq) { + int n; + + if(seq <= 0) + return NULL; + + for(n = 0; n < (int) num_probes; n++) { + if(probes[n].seq == seq) + return &probes[n]; + } + + return NULL; +} + +probe *probe_by_sk(int sk) { + int n; + + if(sk <= 0) + return NULL; + + for(n = 0; n < (int) num_probes; n++) { + if(probes[n].sk == sk) + return &probes[n]; + } + + return NULL; +} + +static void poll_callback(int fd, int revents) { + + ops->recv_probe(fd, revents); +} + +static void do_it(void) { + int start = (first_hop - 1) * probes_per_hop; + int end = num_probes; + double last_send = 0; + + print_header(); + + while(start < end) { + int n, num = 0; + double next_time = 0; + double now_time = get_time(); + + for(n = start; n < end; n++) { + probe *pb = &probes[n]; + + if(n == start && /* probably time to print... */ + !pb->done && pb->send_time /* ...but yet not replied */ + ) { + double expire_time = pb->send_time + get_timeout(pb); + + if(expire_time > now_time) + next_time = expire_time; + else { + ops->expire_probe(pb); + check_expired(pb); + } + } + + if(pb->done) { + + if(n == start) { /* can print it now */ + if(print_probe(pb)) + pb->final = 1; + //log_printf("(host=%s,%s recv_ttl=%d)", dst_name, (host) ? host : "-", pb->recv_ttl); + start++; + } + + if(pb->final) + end = (n / probes_per_hop + 1) * probes_per_hop; + + continue; + } + + if(!pb->send_time) { + int ttl; + double next; + + if(send_secs && (next = last_send + send_secs) > now_time) { + next_time = next; + break; + } + + ttl = n / probes_per_hop + 1; + + ops->send_probe(pb, ttl); + + if(!pb->send_time) { + if(next_time) + break; /* have chances later */ + else + error("send probe"); + } + + last_send = pb->send_time; + } + + if(!next_time) + next_time = pb->send_time + get_timeout(pb); + + num++; + if(num >= (int) sim_probes) + break; + } + + if(next_time) { + double timeout = next_time - get_time(); + + if(timeout < 0) + timeout = 0; + + do_poll(timeout, poll_callback); + } + + } + + print_end(); + + return; +} + +void tune_socket(int sk) { + int i = 0; + + if(debug) { + i = 1; + if(setsockopt(sk, SOL_SOCKET, SO_DEBUG, &i, sizeof(i)) < 0) + error("setsockopt SO_DEBUG"); + } + +#ifdef SO_MARK + if(fwmark) { + if(setsockopt(sk, SOL_SOCKET, SO_MARK, + &fwmark, sizeof(fwmark)) < 0 + ) + error("setsockopt SO_MARK"); + } +#endif + + if(rtbuf && rtbuf_len) { + if(af == AF_INET) { + if(setsockopt(sk, IPPROTO_IP, IP_OPTIONS, + rtbuf, rtbuf_len) < 0 + ) + error("setsockopt IP_OPTIONS"); + } + else if(af == AF_INET6) { + if(setsockopt(sk, IPPROTO_IPV6, IPV6_RTHDR, + rtbuf, rtbuf_len) < 0 + ) + error("setsockopt IPV6_RTHDR"); + } + } + + bind_socket(sk); + + if(af == AF_INET) { + + i = dontfrag ? IP_PMTUDISC_PROBE : IP_PMTUDISC_DONT; + if(setsockopt(sk, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0 && + (!dontfrag || (i = IP_PMTUDISC_DO, + setsockopt(sk, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0)) + ) + error("setsockopt IP_MTU_DISCOVER"); + + if(tos) { + i = tos; + if(setsockopt(sk, SOL_IP, IP_TOS, &i, sizeof(i)) < 0) + error("setsockopt IP_TOS"); + } + + } + else if(af == AF_INET6) { + + i = dontfrag ? IPV6_PMTUDISC_PROBE : IPV6_PMTUDISC_DONT; + if(setsockopt(sk, SOL_IPV6, IPV6_MTU_DISCOVER, &i, sizeof(i)) < 0 && + (!dontfrag || (i = IPV6_PMTUDISC_DO, + setsockopt(sk, SOL_IPV6, IPV6_MTU_DISCOVER, &i, sizeof(i)) < 0)) + ) + error("setsockopt IPV6_MTU_DISCOVER"); + + if(flow_label) { + struct in6_flowlabel_req flr; + + memset(&flr, 0, sizeof(flr)); + flr.flr_label = htonl(flow_label & 0x000fffff); + flr.flr_action = IPV6_FL_A_GET; + flr.flr_flags = IPV6_FL_F_CREATE; + flr.flr_share = IPV6_FL_S_ANY; + memcpy(&flr.flr_dst, &dst_addr.sin6.sin6_addr, + sizeof(flr.flr_dst)); + + if(setsockopt(sk, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, + &flr, sizeof(flr)) < 0 + ) + error("setsockopt IPV6_FLOWLABEL_MGR"); + } + + if(tos) { + i = tos; + if(setsockopt(sk, IPPROTO_IPV6, IPV6_TCLASS, + &i, sizeof(i)) < 0 + ) + error("setsockopt IPV6_TCLASS"); + } + + if(tos || flow_label) { + i = 1; + if(setsockopt(sk, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, + &i, sizeof(i)) < 0 + ) + error("setsockopt IPV6_FLOWINFO_SEND"); + } + } + + if(noroute) { + i = noroute; + if(setsockopt(sk, SOL_SOCKET, SO_DONTROUTE, &i, sizeof(i)) < 0) + error("setsockopt SO_DONTROUTE"); + } + + use_timestamp(sk); + + use_recv_ttl(sk); + + fcntl(sk, F_SETFL, O_NONBLOCK); + + return; +} + +void parse_icmp_res(probe *pb, int type, int code, int info) { + char *str = NULL; + char buf[sizeof(pb->err_str)]; + + if(af == AF_INET) { + + if(type == ICMP_TIME_EXCEEDED) { + if(code == ICMP_EXC_TTL) + return; + } + else if(type == ICMP_DEST_UNREACH) { + + switch (code) { + case ICMP_UNREACH_NET: + case ICMP_UNREACH_NET_UNKNOWN: + case ICMP_UNREACH_ISOLATED: + case ICMP_UNREACH_TOSNET: + str = "!N"; + break; + + case ICMP_UNREACH_HOST: + case ICMP_UNREACH_HOST_UNKNOWN: + case ICMP_UNREACH_TOSHOST: + str = "!H"; + break; + + case ICMP_UNREACH_NET_PROHIB: + case ICMP_UNREACH_HOST_PROHIB: + case ICMP_UNREACH_FILTER_PROHIB: + str = "!X"; + break; + + case ICMP_UNREACH_PORT: + /* dest host is reached */ + str = ""; + break; + + case ICMP_UNREACH_PROTOCOL: + str = "!P"; + break; + + case ICMP_UNREACH_NEEDFRAG: + snprintf(buf, sizeof(buf), "!F-%d", info); + str = buf; + break; + + case ICMP_UNREACH_SRCFAIL: + str = "!S"; + break; + + case ICMP_UNREACH_HOST_PRECEDENCE: + str = "!V"; + break; + + case ICMP_UNREACH_PRECEDENCE_CUTOFF: + str = "!C"; + break; + + default: + snprintf(buf, sizeof(buf), "!<%u>", code); + str = buf; + break; + } + } + + } + else if(af == AF_INET6) { + + if(type == ICMP6_TIME_EXCEEDED) { + if(code == ICMP6_TIME_EXCEED_TRANSIT) + return; + } + else if(type == ICMP6_DST_UNREACH) { + + switch (code) { + + case ICMP6_DST_UNREACH_NOROUTE: + str = "!N"; + break; + + case ICMP6_DST_UNREACH_BEYONDSCOPE: + case ICMP6_DST_UNREACH_ADDR: + str = "!H"; + break; + + case ICMP6_DST_UNREACH_ADMIN: + str = "!X"; + break; + + case ICMP6_DST_UNREACH_NOPORT: + /* dest host is reached */ + str = ""; + break; + + default: + snprintf(buf, sizeof(buf), "!<%u>", code); + str = buf; + break; + } + } + else if(type == ICMP6_PACKET_TOO_BIG) { + snprintf(buf, sizeof(buf), "!F-%d", info); + str = buf; + } + } + + if(!str) { + snprintf(buf, sizeof(buf), "!<%u-%u>", type, code); + str = buf; + } + + if(*str) { + strncpy(pb->err_str, str, sizeof(pb->err_str)); + pb->err_str[sizeof(pb->err_str) - 1] = '\0'; + } + // final present + pb->final = 1; + + return; +} + +static void parse_local_res(probe *pb, int ee_errno, int info) { + + if(ee_errno == EMSGSIZE && info != 0) { + snprintf(pb->err_str, sizeof(pb->err_str) - 1, "!F-%d", info); + pb->final = 1; + return; + } + + errno = ee_errno; + error("local recverr"); +} + +void probe_done(probe *pb) { + + if(pb->sk) { + del_poll(pb->sk); + close(pb->sk); + pb->sk = 0; + } + + pb->seq = 0; + + pb->done = 1; +} + +void recv_reply(int sk, int err, check_reply_t check_reply) { + struct msghdr msg; + sockaddr_any from; + struct iovec iov; + int n; + probe *pb; + char buf[1280]; /* min mtu for ipv6 ( >= 576 for ipv4) */ + char *bufp = buf; + char control[1024]; + struct cmsghdr *cm; + double recv_time = 0; + int recv_ttl = 0; + struct sock_extended_err *ee = NULL; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &from; + msg.msg_namelen = sizeof(from); + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + n = recvmsg(sk, &msg, err ? MSG_ERRQUEUE : 0); + if(n < 0) + return; + + /* when not MSG_ERRQUEUE, AF_INET returns full ipv4 header + on raw sockets... + */ + + if(!err && + af == AF_INET && + /* XXX: Assume that the presence of an extra header means + that it is not a raw socket... + */ + ops->header_len == 0 + ) { + struct iphdr *ip = (struct iphdr *) bufp; + int hlen; + + if(n < (int) sizeof(struct iphdr)) + return; + + hlen = ip->ihl << 2; + if(n < hlen) + return; + + bufp += hlen; + n -= hlen; + } + + pb = check_reply(sk, err, &from, bufp, n); + if(!pb) { + + /* for `frag needed' case at the local host, + kernel >= 3.13 sends local error (no more icmp) + */ + if(!n && err && dontfrag) { + pb = &probes[(first_hop - 1) * probes_per_hop]; + if(pb->done) + return; + } else + return; + } + + /* Parse CMSG stuff */ + + for(cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) { + void *ptr = CMSG_DATA(cm); + + if(cm->cmsg_level == SOL_SOCKET) { + + if(cm->cmsg_type == SO_TIMESTAMP) { + struct timeval *tv = (struct timeval *) ptr; + + recv_time = tv->tv_sec + tv->tv_usec / 1000000.; + } + } + else if(cm->cmsg_level == SOL_IP) { + + if(cm->cmsg_type == IP_TTL) + recv_ttl = *((int *) ptr); + else if(cm->cmsg_type == IP_RECVERR) { + + ee = (struct sock_extended_err *) ptr; + + if(ee->ee_origin != SO_EE_ORIGIN_ICMP && + ee->ee_origin != SO_EE_ORIGIN_LOCAL + ) + return; + + /* dgram icmp sockets might return extra things... */ + if(ee->ee_origin == SO_EE_ORIGIN_ICMP && + (ee->ee_type == ICMP_SOURCE_QUENCH || + ee->ee_type == ICMP_REDIRECT) + ) + return; + } + } + else if(cm->cmsg_level == SOL_IPV6) { + + if(cm->cmsg_type == IPV6_HOPLIMIT) + recv_ttl = *((int *) ptr); + else if(cm->cmsg_type == IPV6_RECVERR) { + + ee = (struct sock_extended_err *) ptr; + + if(ee->ee_origin != SO_EE_ORIGIN_ICMP6 && + ee->ee_origin != SO_EE_ORIGIN_LOCAL + ) + return; + } + } + } + + if(!recv_time) + recv_time = get_time(); + + if(!err) + memcpy(&pb->res, &from, sizeof(pb->res)); + + pb->recv_time = recv_time; + + pb->recv_ttl = recv_ttl; + + if(ee && ee->ee_origin != SO_EE_ORIGIN_LOCAL) { /* icmp or icmp6 */ + memcpy(&pb->res, SO_EE_OFFENDER(ee), sizeof(pb->res)); + parse_icmp_res(pb, ee->ee_type, ee->ee_code, ee->ee_info); + } + + if(ee && ee->ee_origin == SO_EE_ORIGIN_LOCAL) + parse_local_res(pb, ee->ee_errno, ee->ee_info); + + if(ee && + mtudisc && + ee->ee_info >= header_len && + ee->ee_info < header_len + data_len + ) { + data_len = ee->ee_info - header_len; + + probe_done(pb); + + /* clear this probe (as actually the previous hop answers here) + but fill its `err_str' by the info obtained. Ugly, but easy... + */ + memset(pb, 0, sizeof(*pb)); + snprintf(pb->err_str, sizeof(pb->err_str) - 1, "F=%d", ee->ee_info); + + return; + } + + if(ee && + extension && + header_len + n >= (128 + 8) && /* at least... (rfc4884) */ + header_len <= 128 && /* paranoia */ + ((af == AF_INET && (ee->ee_type == ICMP_TIME_EXCEEDED || + ee->ee_type == ICMP_DEST_UNREACH || + ee->ee_type == ICMP_PARAMETERPROB)) || + (af == AF_INET6 && (ee->ee_type == ICMP6_TIME_EXCEEDED || + ee->ee_type == ICMP6_DST_UNREACH)) + ) + ) { + int step; + int offs = 128 - header_len; + + if(n > (int) data_len) + step = 0; /* guaranteed at 128 ... */ + else + step = af == AF_INET ? 4 : 8; + + handle_extensions(pb, bufp + offs, n - offs, step); + } + + probe_done(pb); +} + +int equal_addr(const sockaddr_any *a, const sockaddr_any *b) { + + if(!a->sa.sa_family) + return 0; + + if(a->sa.sa_family != b->sa.sa_family) + return 0; + + if(a->sa.sa_family == AF_INET6) + return !memcmp(&a->sin6.sin6_addr, &b->sin6.sin6_addr, + sizeof(a->sin6.sin6_addr)); + else + return !memcmp(&a->sin.sin_addr, &b->sin.sin_addr, + sizeof(a->sin.sin_addr)); + return 0; /* not reached */ +} + +void bind_socket(int sk) { + sockaddr_any *addr, tmp; + + if(device) { + if(setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, + device, strlen(device) + 1) < 0 + ) + error("setsockopt SO_BINDTODEVICE"); + } + + if(!src_addr.sa.sa_family) { + memset(&tmp, 0, sizeof(tmp)); + tmp.sa.sa_family = af; + addr = &tmp; + } else + addr = &src_addr; + + if(bind(sk, &addr->sa, sizeof(*addr)) < 0) + error("bind"); + + return; +} + +void use_timestamp(int sk) { + int n = 1; + + setsockopt(sk, SOL_SOCKET, SO_TIMESTAMP, &n, sizeof(n)); + /* foo on errors... */ +} + +void use_recv_ttl(int sk) { + int n = 1; + + if(af == AF_INET) + setsockopt(sk, SOL_IP, IP_RECVTTL, &n, sizeof(n)); + else if(af == AF_INET6) + setsockopt(sk, SOL_IPV6, IPV6_RECVHOPLIMIT, &n, sizeof(n)); + /* foo on errors */ +} + +void use_recverr(int sk) { + int val = 1; + + if(af == AF_INET) { + if(setsockopt(sk, SOL_IP, IP_RECVERR, &val, sizeof(val)) < 0) + error("setsockopt IP_RECVERR"); + } + else if(af == AF_INET6) { + if(setsockopt(sk, SOL_IPV6, IPV6_RECVERR, &val, sizeof(val)) < 0) + error("setsockopt IPV6_RECVERR"); + } +} + +void set_ttl(int sk, int ttl) { + + if(af == AF_INET) { + if(setsockopt(sk, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) + error("setsockopt IP_TTL"); + } + else if(af == AF_INET6) { + if(setsockopt(sk, SOL_IPV6, IPV6_UNICAST_HOPS, + &ttl, sizeof(ttl)) < 0 + ) + error("setsockopt IPV6_UNICAST_HOPS"); + } +} + +int do_send(int sk, const void *data, size_t len, const sockaddr_any *addr) { + int res; + + if(!addr || raw_can_connect()) + res = send(sk, data, len, 0); + else + res = sendto(sk, data, len, 0, &addr->sa, sizeof(*addr)); + + if(res < 0) { + if(errno == ENOBUFS || errno == EAGAIN) + return res; + if(errno == EMSGSIZE) + return 0; /* recverr will say more... */ + error("send"); /* not recoverable */ + } + + return res; +} + +/* There is a bug in the kernel before 2.6.25, which prevents icmp errors + to be obtained by MSG_ERRQUEUE for ipv6 connected raw sockets. + */ +static int can_connect = -1; + +#define VER(A,B,C,D) (((((((A) << 8) | (B)) << 8) | (C)) << 8) | (D)) + +int raw_can_connect(void) { + + if(can_connect < 0) { + + if(af == AF_INET) + can_connect = 1; + else { /* AF_INET6 */ + struct utsname uts; + int n; + unsigned int a, b, c, d = 0; + + if(uname(&uts) < 0) + return 0; + + n = sscanf(uts.release, "%u.%u.%u.%u", &a, &b, &c, &d); + can_connect = (n >= 3 && VER (a, b, c, d) >= VER(2, 6, 25, 0)); + } + } + + return can_connect; +} + +int traceroute_util(const char *addr, int *hops, int *time_usec) +{ + UNUSED(hops); + int type = 6; // 4 or 6 + //int total_hops = 0; + long int total_time_usec = 0; // latency in microseconds + int argc = 3; + const char *argv[argc]; + if(type != 4) + argv[0] = "traceroute6"; + else + argv[0] = "traceroute4"; + //argv[1] = "-A"; + argv[1] = "-4"; // -n -m 16 + argv[2] = addr; + + int ret = traceroute_main(argc, (char**) argv); + if(!ret) + for(int i = 0; i < (int) num_probes; i++) { + probe *one_probe = probes + i; + if(one_probe->done && one_probe->final && one_probe->recv_ttl) { + *hops = i / DEF_NUM_PROBES + 1; + *time_usec = (int) ((one_probe->recv_time - one_probe->send_time) * 1000000); + ret = 1; + break; + } + /*if(one_probe->done) + printf("%d(%d) dseq=%d sk=%d done=%d final=%d recv_ttl=%d dt=%lf\n", i + 1, i / DEF_NUM_PROBES + 1, + one_probe->seq, one_probe->sk, one_probe->done, + one_probe->final, one_probe->recv_ttl, one_probe->recv_time - one_probe->send_time);*/ + } + free(probes); + + return (ret == 1) ? 0 : -1; +} diff --git a/iputils/traceroute/traceroute.h b/iputils/traceroute/traceroute.h new file mode 100755 index 0000000000..53dc793355 --- /dev/null +++ b/iputils/traceroute/traceroute.h @@ -0,0 +1,107 @@ +/* + Copyright (c) 2006, 2007 Dmitry Butskoy + <buc@citadel.stu.neva.ru> + License: GPL v2 or any later + + See COPYING for the status of this software. +*/ + +#include <netinet/in.h> + +#include <clif.h> + + +union common_sockaddr { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; +typedef union common_sockaddr sockaddr_any; + +struct probe_struct { + int done; + int final; + sockaddr_any res; + double send_time; + double recv_time; + int recv_ttl; + int sk; + int seq; + char *ext; + char err_str[16]; /* assume enough */ +}; +typedef struct probe_struct probe; + + +struct tr_module_struct { + struct tr_module_struct *next; + const char *name; + int (*init) (const sockaddr_any *dest, + unsigned int port_seq, size_t *packet_len); + void (*send_probe) (probe *pb, int ttl); + void (*recv_probe) (int fd, int revents); + void (*expire_probe) (probe *pb); + CLIF_option *options; /* per module options, if any */ + int one_per_time; /* no simultaneous probes */ + size_t header_len; /* additional header length (aka for udp) */ +}; +typedef struct tr_module_struct tr_module; + + +#define __TEXT(X) #X +#define _TEXT(X) __TEXT(X) + +#define DEF_START_PORT 33434 /* start for traditional udp method */ +#define DEF_UDP_PORT 53 /* dns */ +#define DEF_TCP_PORT 80 /* web */ +#define DEF_DCCP_PORT DEF_START_PORT /* is it a good choice?... */ +#define DEF_RAW_PROT 253 /* for experimentation and testing, rfc3692 */ + + +void error (const char *str) __attribute__((noreturn)); +void error_or_perm (const char *str) __attribute__((noreturn)); + +double get_time (void); +void tune_socket (int sk); +void parse_icmp_res (probe *pb, int type, int code, int info); +void probe_done (probe *pb); + +typedef probe *(*check_reply_t) (int sk, int err, sockaddr_any *from, + char *buf, size_t len); +void recv_reply (int sk, int err, check_reply_t check_reply); + +int equal_addr (const sockaddr_any *a, const sockaddr_any *b); + +probe *probe_by_seq (int seq); +probe *probe_by_sk (int sk); + +void bind_socket (int sk); +void use_timestamp (int sk); +void use_recv_ttl (int sk); +void use_recverr (int sk); +void set_ttl (int sk, int ttl); +int do_send (int sk, const void *data, size_t len, const sockaddr_any *addr); + +void add_poll (int fd, int events); +void del_poll (int fd); +void do_poll (double timeout, void (*callback) (int fd, int revents)); + +void handle_extensions (probe *pb, char *buf, int len, int step); +const char *get_as_path (const char *query); + +int raw_can_connect (void); + +unsigned int random_seq (void); +uint16_t in_csum (const void *ptr, size_t len); + + +void tr_register_module (tr_module *module); +const tr_module *tr_get_module (const char *name); + +#define TR_MODULE(MOD) \ +static void __init_ ## MOD (void) __attribute__ ((constructor)); \ +static void __init_ ## MOD (void) { \ + \ + tr_register_module (&MOD); \ +} + diff --git a/iputils/traceroute6.c b/iputils/traceroute6.c new file mode 100755 index 0000000000..ffe1d8dfb6 --- /dev/null +++ b/iputils/traceroute6.c @@ -0,0 +1,981 @@ +/* + * Modified for NRL 4.4BSD IPv6 release. + * 07/31/96 bgp + * + * Search for "#ifdef NRL" to find the changes. + */ + +/* + * Modified for Linux IPv6 by Pedro Roque <roque@di.fc.ul.pt> + * 31/07/1996 + * + * As ICMP error messages for IPv6 now include more than 8 bytes + * UDP datagrams are now sent via an UDP socket instead of magic + * RAW socket tricks. + * + * Original copyright and comments left intact. They might not + * match the code anymore. + */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * traceroute host - trace the route ip packets follow going to "host". + * + * Attempt to trace the route an ip packet would follow to some + * internet host. We find out intermediate hops by launching probe + * packets with a small ttl (time to live) then listening for an + * icmp "time exceeded" reply from a gateway. We start our probes + * with a ttl of one and increase by one until we get an icmp "port + * unreachable" (which means we got to "host") or hit a max (which + * defaults to 30 hops & can be changed with the -m flag). Three + * probes (change with -q flag) are sent at each ttl setting and a + * line is printed showing the ttl, address of the gateway and + * round trip time of each probe. If the probe answers come from + * different gateways, the address of each responding system will + * be printed. If there is no response within a 5 sec. timeout + * interval (changed with the -w flag), a "*" is printed for that + * probe. + * + * Probe packets are UDP format. We don't want the destination + * host to process them so the destination port is set to an + * unlikely value (if some clod on the destination is using that + * value, it can be changed with the -p flag). + * + * A sample use might be: + * + * [yak 71]% traceroute nis.nsf.net. + * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet + * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms + * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms + * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms + * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms + * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms + * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms + * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms + * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms + * + * Note that lines 2 & 3 are the same. This is due to a buggy + * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards + * packets with a zero ttl. + * + * A more interesting example is: + * + * [yak 72]% traceroute allspice.lcs.mit.edu. + * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max + * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms + * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms + * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms + * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms + * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms + * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms + * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms + * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms + * 12 * * * + * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms + * 14 * * * + * 15 * * * + * 16 * * * + * 17 * * * + * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms + * + * (I start to see why I'm having so much trouble with mail to + * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away + * either don't send ICMP "time exceeded" messages or send them + * with a ttl too small to reach us. 14 - 17 are running the + * MIT C Gateway code that doesn't send "time exceeded"s. God + * only knows what's going on with 12. + * + * The silent gateway 12 in the above may be the result of a bug in + * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3) + * sends an unreachable message using whatever ttl remains in the + * original datagram. Since, for gateways, the remaining ttl is + * zero, the icmp "time exceeded" is guaranteed to not make it back + * to us. The behavior of this bug is slightly more interesting + * when it appears on the destination system: + * + * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms + * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms + * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms + * 7 * * * + * 8 * * * + * 9 * * * + * 10 * * * + * 11 * * * + * 12 * * * + * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! + * + * Notice that there are 12 "gateways" (13 is the final + * destination) and exactly the last half of them are "missing". + * What's really happening is that rip (a Sun-3 running Sun OS3.5) + * is using the ttl from our arriving datagram as the ttl in its + * icmp reply. So, the reply will time out on the return path + * (with no notice sent to anyone since icmp's aren't sent for + * icmp's) until we probe with a ttl that's at least twice the path + * length. I.e., rip is really only 7 hops away. A reply that + * returns with a ttl of 1 is a clue this problem exists. + * Traceroute prints a "!" after the time if the ttl is <= 1. + * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or + * non-standard (HPUX) software, expect to see this problem + * frequently and/or take care picking the target host of your + * probes. + * + * Other possible annotations after the time are !H, !N, !P (got a host, + * network or protocol unreachable, respectively), !S or !F (source + * route failed or fragmentation needed -- neither of these should + * ever occur and the associated gateway is busted if you see one), + * !X (communication administratively prohibited). If + * almost all the probes result in some kind of unreachable, traceroute + * will give up and exit. + * + * Notes + * ----- + * This program must be run by root or be setuid. (I suggest that + * you *don't* make it setuid -- casual use could result in a lot + * of unnecessary traffic on our poor, congested nets.) + * + * This program requires a kernel mod that does not appear in any + * system available from Berkeley: A raw ip socket using proto + * IPPROTO_RAW must interpret the data sent as an ip datagram (as + * opposed to data to be wrapped in a ip datagram). See the README + * file that came with the source to this program for a description + * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may + * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE + * MODIFIED TO RUN THIS PROGRAM. + * + * The udp port usage may appear bizarre (well, ok, it is bizarre). + * The problem is that an icmp message only contains 8 bytes of + * data from the original datagram. 8 bytes is the size of a udp + * header so, if we want to associate replies with the original + * datagram, the necessary information must be encoded into the + * udp header (the ip id could be used but there's no way to + * interlock with the kernel's assignment of ip id's and, anyway, + * it would have taken a lot more kernel hacking to allow this + * code to set the ip id). So, to allow two or more users to + * use traceroute simultaneously, we use this task's pid as the + * source port (the high bit is set to move the port number out + * of the "likely" range). To keep track of which probe is being + * replied to (so times and/or hop counts don't get confused by a + * reply that was delayed in transit), we increment the destination + * port number before each probe. + * + * Don't use this as a coding example. I was trying to find a + * routing problem and this code sort-of popped out after 48 hours + * without sleep. I was amazed it ever compiled, much less ran. + * + * I stole the idea for this program from Steve Deering. Since + * the first release, I've learned that had I attended the right + * IETF working group meetings, I also could have stolen it from Guy + * Almes or Matt Mathis. I don't know (or care) who came up with + * the idea first. I envy the originators' perspicacity and I'm + * glad they didn't keep the idea a secret. + * + * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or + * enhancements to the original distribution. + * + * I've hacked up a round-trip-route version of this that works by + * sending a loose-source-routed udp datagram through the destination + * back to yourself. Unfortunately, SO many gateways botch source + * routing, the thing is almost worthless. Maybe one day... + * + * -- Van Jacobson (van@helios.ee.lbl.gov) + * Tue Dec 20 03:50:13 PST 1988 + */ + +#include <arpa/inet.h> +#include <errno.h> +#include <linux/types.h> +#include <netdb.h> +#include <net/if.h> +#include <netinet/icmp6.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip6.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <netinet/udp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <time.h> +#include <unistd.h> +#include <glib.h> + +#include "iputils.h" + +#if __linux__ +# include <endian.h> +#endif + +#ifdef HAVE_LIBCAP +# include <sys/capability.h> +#endif + +#ifdef USE_IDN +# include <locale.h> +# ifndef NI_IDN +# define NI_IDN 32 +# endif +# define ADDRINFO_IDN_FLAGS AI_IDN +# define getnameinfo_flags NI_IDN +#else +# define getnameinfo_flags 0 +# define ADDRINFO_IDN_FLAGS 0 +#endif + +#ifndef SOL_IPV6 +# define SOL_IPV6 IPPROTO_IPV6 +#endif + +enum { + DEFAULT_PROBES = 3, + DEFAULT_HOPS = 30, + DEFAULT_PORT = 32768 + 666, + DEFAULT_WAIT = 5, + PACKET_SIZE = 512, + MAXPACKET = 65535, + + /* + * The following are copy from linux/icmpv6.h that cannot be + * included because icmp6_filter prototype is redefined in + * netinet/icmp6.h header. + */ + ICMPV6_DEST_UNREACH = 1, + ICMPV6_PKT_TOOBIG = 2, + ICMPV6_TIME_EXCEED = 3, + ICMPV6_PARAMPROB = 4, + ICMPV6_ECHO_REQUEST = 128, + ICMPV6_ECHO_REPLY = 129, + ICMPV6_MGM_QUERY = 130, + ICMPV6_MGM_REPORT = 131, + ICMPV6_MGM_REDUCTION = 132, + ICMPV6_NI_QUERY = 139, + ICMPV6_NI_REPLY = 140, + ICMPV6_MLD2_REPORT = 143, + ICMPV6_DHAAD_REQUEST = 144, + ICMPV6_DHAAD_REPLY = 145, + ICMPV6_MOBILE_PREFIX_SOL = 146, + ICMPV6_MOBILE_PREFIX_ADV = 147, + + /* + * ICMP codes for neighbour discovery messages. These are from + * linux kernel source include/net/ndisc.h file. The user api + * includes does not have these values. + */ + NDISC_ROUTER_SOLICITATION = 133, + NDISC_ROUTER_ADVERTISEMENT = 134, + NDISC_NEIGHBOUR_SOLICITATION = 135, + NDISC_NEIGHBOUR_ADVERTISEMENT = 136, + NDISC_REDIRECT = 137, +}; + +#ifndef FD_SET +# define NFDBITS (8 * sizeof(fd_set)) +# define FD_SETSIZE NFDBITS +# define FD_SET(n, p) ((p)->fds_bits[(n) / NFDBITS] |= (1 << ((n) % NFDBITS))) +# define FD_CLR(n, p) ((p)->fds_bits[(n) / NFDBITS] &= ~(1 << ((n) % NFDBITS))) +# define FD_ISSET(n, p) ((p)->fds_bits[(n) / NFDBITS] & (1 << ((n) % NFDBITS))) +# define FD_ZERO(p) memset((char *)(p), 0, sizeof(*(p))) +#endif + +struct run_state { + unsigned char packet[PACKET_SIZE]; /* last inbound (icmp) packet */ + int icmp_sock; /* receive (icmp) socket file descriptor */ + int sndsock; /* send (udp) socket file descriptor */ + char *sendbuff; + int datalen; + struct sockaddr_in6 whereto; /* Who to try to reach */ + struct sockaddr_in6 saddr; + struct sockaddr_in6 firsthop; + char *source; + char *device; + char *hostname; + int nprobes; + int max_ttl; + pid_t ident; + unsigned short port; /* start udp dest port # for probe packets */ + int options; /* socket options */ + int waittime; /* time to wait for response (in seconds) */ + unsigned int + nflag:1, /* print addresses numerically */ + verbose:1; +}; + +struct pkt_format { + uint32_t ident; + uint32_t seq; + struct timespec ts; +}; + +/* + * All includes, definitions, struct declarations, and global variables are + * above. After this comment all you can find is functions. + */ + +static int wait_for_reply(struct run_state *ctl, struct sockaddr_in6 *from, + struct in6_addr *to, const uint8_t reset_timer) +{ + fd_set fds; + static struct timeval wait; + ssize_t cc = 0; + char cbuf[PACKET_SIZE]; + + FD_ZERO(&fds); + FD_SET(ctl->icmp_sock, &fds); + if (reset_timer) { + /* + * traceroute could hang if someone else has a ping + * running and our ICMP reply gets dropped but we don't + * realize it because we keep waking up to handle those + * other ICMP packets that keep coming in. To fix this, + * "reset_timer" will only be true if the last packet that + * came in was for us or if this is the first time we're + * waiting for a reply since sending out a probe. Note + * that this takes advantage of the select() feature on + * Linux where the remaining timeout is written to the + * struct timeval area. + */ + wait.tv_sec = ctl->waittime; + wait.tv_usec = 0; + } + + if (select(ctl->icmp_sock + 1, &fds, NULL, NULL, &wait) > 0) { + struct iovec iov = { + .iov_base = ctl->packet, + .iov_len = sizeof(ctl->packet) + }; + struct msghdr msg = { + .msg_name = (void *)from, + .msg_namelen = sizeof(*from), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cbuf, + .msg_controllen = sizeof(cbuf), + 0 + }; + + cc = recvmsg(ctl->icmp_sock, &msg, 0); + if (cc >= 0) { + struct cmsghdr *cmsg; + struct in6_pktinfo *ipi; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_IPV6) + continue; + switch (cmsg->cmsg_type) { + case IPV6_PKTINFO: +#ifdef IPV6_2292PKTINFO + case IPV6_2292PKTINFO: +#endif + ipi = (struct in6_pktinfo *) + CMSG_DATA(cmsg); + memcpy(to, ipi, sizeof(*to)); + } + } + } + } + + return (cc); +} + +static void send_probe(struct run_state *ctl, uint32_t seq, int ttl) +{ + struct pkt_format *pkt = (struct pkt_format *)ctl->sendbuff; + int i; + + pkt->ident = htonl(ctl->ident); + pkt->seq = htonl(seq); + clock_gettime(CLOCK_MONOTONIC_RAW, &pkt->ts); + + i = setsockopt(ctl->sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); + if (i < 0) { + perror("setsockopt"); + exit(1); + } + + do { + i = sendto(ctl->sndsock, ctl->sendbuff, ctl->datalen, 0, + (struct sockaddr *)&ctl->whereto, sizeof(ctl->whereto)); + } while (i < 0 && errno == ECONNREFUSED); + + if (i < 0 || i != ctl->datalen) { + if (i < 0) + perror("sendto"); + printf("traceroute: wrote %s %d chars, ret=%d\n", ctl->hostname, ctl->datalen, i); + fflush(stdout); + } +} + +static double deltaT(struct timespec *a, struct timespec *b) +{ + struct timespec c; + double dt; + + if ((b->tv_nsec - a->tv_nsec) < 0) { + c.tv_sec = b->tv_sec - a->tv_sec - 1UL; + c.tv_nsec = b->tv_nsec - a->tv_nsec + 1000000000UL; + } else { + c.tv_sec = b->tv_sec - a->tv_sec; + c.tv_nsec = b->tv_nsec - a->tv_nsec; + } + dt = (double)(c.tv_sec * 1000.0L) + (double)(c.tv_nsec / 1000000.0L); + return (dt); +} + +/* + * Convert an ICMP "type" field to a printable string. + */ +static char const *pr_type(const uint8_t t) +{ + switch (t) { + /* Unknown */ + case 0: + return "Error"; + case ICMPV6_DEST_UNREACH: + return "Destination Unreachable"; + case ICMPV6_PKT_TOOBIG: + return "Packet Too Big"; + case ICMPV6_TIME_EXCEED: + return "Time Exceeded in Transit"; + case ICMPV6_PARAMPROB: + return "Parameter Problem"; + case ICMPV6_ECHO_REQUEST: + return "Echo Request"; + case ICMPV6_ECHO_REPLY: + return "Echo Reply"; + case ICMPV6_MGM_QUERY: + return "Membership Query"; + case ICMPV6_MGM_REPORT: + return "Membership Report"; + case ICMPV6_MGM_REDUCTION: + return "Membership Reduction"; + case NDISC_ROUTER_SOLICITATION: + return "Router Solicitation"; + case NDISC_ROUTER_ADVERTISEMENT: + return "Router Advertisement"; + case NDISC_NEIGHBOUR_SOLICITATION: + return "Neighbor Solicitation"; + case NDISC_NEIGHBOUR_ADVERTISEMENT: + return "Neighbor Advertisement"; + case NDISC_REDIRECT: + return "Redirect"; + case ICMPV6_NI_QUERY: + return "Neighbor Query"; + case ICMPV6_NI_REPLY: + return "Neighbor Reply"; + case ICMPV6_MLD2_REPORT: + return "Multicast Listener Report packet"; + case ICMPV6_DHAAD_REQUEST: + return "Home Agent Address Discovery Request Message"; + case ICMPV6_DHAAD_REPLY: + return "Home Agent Address Discovery Reply message"; + case ICMPV6_MOBILE_PREFIX_SOL: + return "Mobile Prefix Solicitation Message"; + case ICMPV6_MOBILE_PREFIX_ADV: + return "Mobile Prefix Solicitation Advertisement"; + default: + return "OUT-OF-RANGE"; + } + abort(); +} + +static int packet_ok(struct run_state *ctl, int cc, struct sockaddr_in6 *from, + struct in6_addr *to, uint32_t seq, struct timespec *ts) +{ + struct icmp6_hdr *icp = (struct icmp6_hdr *)ctl->packet; + uint8_t type, code; + + type = icp->icmp6_type; + code = icp->icmp6_code; + + if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT) + || type == ICMP6_DST_UNREACH) { + struct ip6_hdr *hip; + struct udphdr *up; + int nexthdr; + + hip = (struct ip6_hdr *)(icp + 1); + up = (struct udphdr *)(hip + 1); + nexthdr = hip->ip6_nxt; + + if (nexthdr == 44) { + nexthdr = *(unsigned char *)up; + up++; + } + if (nexthdr == IPPROTO_UDP) { + struct pkt_format *pkt; + + pkt = (struct pkt_format *)(up + 1); + + if (ntohl(pkt->ident) == (uint32_t) ctl->ident && ntohl(pkt->seq) == seq) { + *ts = pkt->ts; + return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1); + } + } + + } + + if (ctl->verbose) { + unsigned char *p; + char pa1[NI_MAXHOST]; + char pa2[NI_MAXHOST]; + int i; + + p = (unsigned char *)(icp + 1); + + printf("\n%d bytes from %s to %s", cc, + inet_ntop(AF_INET6, &from->sin6_addr, pa1, sizeof(pa1)), + inet_ntop(AF_INET6, to, pa2, sizeof(pa2))); + + printf(": icmp type %d (%s) code %d\n", type, pr_type(type), icp->icmp6_code); + + cc -= sizeof(struct icmp6_hdr); + for (i = 0; i < cc; i++) { + if (i % 16 == 0) + printf("%04x:", i); + if (i % 4 == 0) + printf(" "); + printf("%02x", 0xff & (unsigned)p[i]); + if (i % 16 == 15 && i + 1 < cc) + printf("\n"); + } + printf("\n"); + } + + return (0); +} + +static void print(struct run_state *ctl, struct sockaddr_in6 *from) +{ + char pa[NI_MAXHOST] = ""; + char hnamebuf[NI_MAXHOST] = ""; + + if (ctl->nflag) + printf(" %s", inet_ntop(AF_INET6, &from->sin6_addr, pa, sizeof(pa))); + else { + inet_ntop(AF_INET6, &from->sin6_addr, pa, sizeof(pa)); + getnameinfo((struct sockaddr *)from, sizeof *from, hnamebuf, + sizeof hnamebuf, NULL, 0, getnameinfo_flags); + + printf(" %s (%s)", hnamebuf[0] ? hnamebuf : pa, pa); + } +} + +static __attribute__((noreturn)) void usage(void) +{ + fprintf(stderr, + "\nUsage:\n" + " traceroute6 [options] <destination>\n" + "\nOptions:\n" + " -d use SO_DEBUG socket option\n" + " -i <device> bind to <device>\n" + " -m <hops> use maximum <hops>\n" + " -n no dns name resolution\n" + " -p <port> use destination <port>\n" + " -q <nprobes> number of probes\n" + " -r use SO_DONTROUTE socket option\n" + " -s <address> use source <address>\n" + " -v verbose output\n" + " -w <timeout> time to wait for response\n" + "\nFor more details see traceroute6(8).\n"); + exit(1); +} + +static uint16_t get_ip_unprivileged_port_start(const uint16_t fallback) +{ + FILE *f; + uint16_t nr = fallback; + + f = fopen("/proc/sys/net/ipv4/ip_unprivileged_port_start", "r"); + if (f) { + if (fscanf(f, "%" SCNu16, &nr) != 1) + nr = fallback; + fclose(f); + } + return nr; +} + +int traceroute6_main(int argc, char **argv) +{ + struct run_state ctl = { + .nprobes = DEFAULT_PROBES, + .max_ttl = DEFAULT_HOPS, + .port = DEFAULT_PORT, + .waittime = DEFAULT_WAIT, + 0 + }; + char pa[NI_MAXHOST]; + extern char *optarg; + extern int optind; + struct addrinfo hints6 = { + .ai_family = AF_INET6, + .ai_socktype = SOCK_RAW, + .ai_flags = AI_CANONNAME | ADDRINFO_IDN_FLAGS + }; + struct addrinfo *result; + int status; + struct sockaddr_in6 from; + struct sockaddr_in6 *to = (struct sockaddr_in6 *)&ctl.whereto; + int ch, i, probe, ttl, on = 1; + uint32_t seq = 0; + int socket_errno; + char *resolved_hostname = NULL; + + ctl.datalen = sizeof(struct pkt_format); + ctl.icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + socket_errno = errno; + + if (setuid(getuid())) { + perror("traceroute6: setuid"); + exit(-1); + } +#ifdef HAVE_LIBCAP + { + cap_t caps = cap_init(); + + if (cap_set_proc(caps)) { + perror("traceroute6: cap_set_proc"); + exit(-1); + } + cap_free(caps); + } +#endif + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif + while ((ch = getopt(argc, argv, "dm:np:q:rs:w:vi:V")) != EOF) { + switch (ch) { + case 'd': + ctl.options |= SO_DEBUG; + break; + case 'm': + ctl.max_ttl = atoi(optarg); + if (ctl.max_ttl <= 1) { + fprintf(stderr, "traceroute: max ttl must be >1.\n"); + exit(1); + } + break; + case 'n': + ctl.nflag = 1; + break; + case 'p': + ctl.port = atoi(optarg); + if (ctl.port < 1) { + fprintf(stderr, "traceroute: port must be >0.\n"); + exit(1); + } + break; + case 'q': + ctl.nprobes = atoi(optarg); + if (ctl.nprobes < 1) { + fprintf(stderr, "traceroute: nprobes must be >0.\n"); + exit(1); + } + break; + case 'r': + ctl.options |= SO_DONTROUTE; + break; + case 's': + /* + * set the ip source address of the outbound probe + * (e.g., on a multi-homed host). + */ + ctl.source = optarg; + break; + case 'i': + ctl.device = optarg; + break; + case 'v': + ctl.verbose = 1; + break; + case 'w': + ctl.waittime = atoi(optarg); + if (ctl.waittime <= 1) { + fprintf(stderr, "traceroute: wait must be >1 sec.\n"); + exit(1); + } + break; + case 'V': + printf(IPUTILS_VERSION("traceroute6")); + exit(0); + default: + usage(); + } + } + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) + usage(); + + setlinebuf(stdout); + + memset((char *)&ctl.whereto, 0, sizeof(ctl.whereto)); + + to->sin6_family = AF_INET6; + + if (inet_pton(AF_INET6, *argv, &to->sin6_addr) > 0) { + ctl.hostname = *argv; + } else { + status = getaddrinfo(*argv, NULL, &hints6, &result); + if (status) { + fprintf(stderr, "traceroute: %s: %s\n", *argv, gai_strerror(status)); + exit(1); + } + + memcpy(to, result->ai_addr, sizeof *to); + resolved_hostname = strdup(result->ai_canonname); + if (resolved_hostname == NULL) { + fprintf(stderr, "traceroute: cannot allocate memory\n"); + exit(1); + } + ctl.hostname = resolved_hostname; + freeaddrinfo(result); + } + + to->sin6_port = htons(ctl.port); + + ctl.firsthop = *to; + if (*++argv) { + ctl.datalen = atoi(*argv); + /* + * Message for rpm maintainers: have _shame_. If you want + * to fix something send the patch to me for sanity + * checking. "datalen" patch is a shit. + */ + if (ctl.datalen == 0) + ctl.datalen = sizeof(struct pkt_format); + else if (ctl.datalen < (int)sizeof(struct pkt_format) || ctl.datalen >= MAXPACKET) { + fprintf(stderr, + "traceroute: packet size must be %d <= s < %d.\n", + (int)sizeof(struct pkt_format), MAXPACKET); + exit(1); + } + } + + ctl.ident = getpid(); + + ctl.sendbuff = malloc(ctl.datalen); + if (ctl.sendbuff == NULL) { + fprintf(stderr, "malloc failed\n"); + exit(1); + } + + if (ctl.icmp_sock < 0) { + errno = socket_errno; + perror("traceroute6: icmp socket"); + exit(1); + } +#ifdef IPV6_RECVPKTINFO + setsockopt(ctl.icmp_sock, SOL_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + setsockopt(ctl.icmp_sock, SOL_IPV6, IPV6_2292PKTINFO, &on, sizeof(on)); +#else + setsockopt(ctl.icmp_sock, SOL_IPV6, IPV6_PKTINFO, &on, sizeof(on)); +#endif + + if (ctl.options & SO_DEBUG) + setsockopt(ctl.icmp_sock, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)); + if (ctl.options & SO_DONTROUTE) + setsockopt(ctl.icmp_sock, SOL_SOCKET, SO_DONTROUTE, (char *)&on, sizeof(on)); + +#ifdef __linux__ + on = 2; + if (setsockopt(ctl.icmp_sock, SOL_RAW, IPV6_CHECKSUM, &on, sizeof(on)) < 0) { + /* + * checksum should be enabled by default and setting this + * option might fail anyway. + */ + fprintf(stderr, "setsockopt(RAW_CHECKSUM) failed - try to continue."); + } +#endif + + if ((ctl.sndsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + perror("traceroute: UDP socket"); + exit(5); + } +#ifdef SO_SNDBUF + if (setsockopt(ctl.sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&ctl.datalen, + sizeof(ctl.datalen)) < 0) { + perror("traceroute: SO_SNDBUF"); + exit(6); + } +#endif /* SO_SNDBUF */ + + if (ctl.options & SO_DEBUG) + setsockopt(ctl.sndsock, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)); + if (ctl.options & SO_DONTROUTE) + setsockopt(ctl.sndsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on, sizeof(on)); + + if (ctl.source == NULL) { + socklen_t alen; + int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0); + + if (probe_fd < 0) { + perror("socket"); + exit(1); + } + if (ctl.device) { + if (setsockopt + (probe_fd, SOL_SOCKET, SO_BINDTODEVICE, ctl.device, + strlen(ctl.device) + 1) == -1) + perror("WARNING: interface is ignored"); + } + ctl.firsthop.sin6_port = htons(get_ip_unprivileged_port_start(1025)); + if (connect(probe_fd, (struct sockaddr *)&ctl.firsthop, + sizeof(ctl.firsthop)) == -1) { + perror("connect"); + exit(1); + } + alen = sizeof(ctl.saddr); + if (getsockname(probe_fd, (struct sockaddr *)&ctl.saddr, &alen) == -1) { + perror("getsockname"); + exit(1); + } + ctl.saddr.sin6_port = 0; + close(probe_fd); + } else { + memset((char *)&ctl.saddr, 0, sizeof(ctl.saddr)); + ctl.saddr.sin6_family = AF_INET6; + if (inet_pton(AF_INET6, ctl.source, &ctl.saddr.sin6_addr) <= 0) { + printf("traceroute: unknown addr %s\n", ctl.source); + exit(1); + } + } + + if (bind(ctl.sndsock, (struct sockaddr *)&ctl.saddr, sizeof(ctl.saddr)) < 0) { + perror("traceroute: bind sending socket"); + exit(1); + } + if (bind(ctl.icmp_sock, (struct sockaddr *)&ctl.saddr, sizeof(ctl.saddr)) < 0) { + perror("traceroute: bind icmp6 socket"); + exit(1); + } + + fprintf(stderr, "traceroute to %s (%s)", ctl.hostname, + inet_ntop(AF_INET6, &to->sin6_addr, pa, sizeof(pa))); + + fprintf(stderr, " from %s", inet_ntop(AF_INET6, &ctl.saddr.sin6_addr, pa, sizeof(pa))); + fprintf(stderr, ", %d hops max, %d byte packets\n", ctl.max_ttl, ctl.datalen); + fflush(stderr); + + for (ttl = 1; ttl <= ctl.max_ttl; ++ttl) { + struct in6_addr lastaddr = { {{0,}} }; + uint8_t got_there = 0; + int unreachable = 0; + + printf("%2d ", ttl); + for (probe = 0; probe < ctl.nprobes; ++probe) { + ssize_t cc; + uint8_t reset_timer = 1; + struct timespec t1, t2; + struct in6_addr to_addr; + + clock_gettime(CLOCK_MONOTONIC_RAW, &t1); + send_probe(&ctl, ++seq, ttl); + while ((cc = wait_for_reply(&ctl, &from, &to_addr, reset_timer)) != 0) { + clock_gettime(CLOCK_MONOTONIC_RAW, &t2); + if ((i = packet_ok(&ctl, cc, &from, &to_addr, seq, &t1))) { + if (memcmp(&from.sin6_addr, &lastaddr, + sizeof(from.sin6_addr))) { + print(&ctl, &from); + memcpy(&lastaddr, + &from.sin6_addr, sizeof(lastaddr)); + } + printf(" %.4f ms", deltaT(&t1, &t2)); + switch (i - 1) { + case ICMP6_DST_UNREACH_NOPORT: + got_there = 1; + break; + + case ICMP6_DST_UNREACH_NOROUTE: + ++unreachable; + printf(" !N"); + break; + case ICMP6_DST_UNREACH_ADDR: + ++unreachable; + printf(" !H"); + break; + + case ICMP6_DST_UNREACH_ADMIN: + ++unreachable; + printf(" !X"); + break; + } + break; + } else + reset_timer = 0; + } + if (cc <= 0) + printf(" *"); + fflush(stdout); + } + putchar('\n'); + if (got_there || (unreachable > 0 && unreachable >= ctl.nprobes - 1)) + break; + } + free(resolved_hostname); + return 0; +} + +int traceroute6_util(const char *addr, int *hops, int *time_usec) +{ + int type = 4; // 4 or 6 + int total_hops = 0; + long int total_time_usec = 0; // latency in microseconds + int argc = 4; + const char *argv[argc]; + if(type != 4) + argv[0] = "raceroute6"; + else + argv[0] = "raceroute4"; + argv[1] = "-v"; // print both name and ip + argv[2] = "-v"; // -n -m 16 + argv[3] = addr; + int ret = traceroute6_main(argc, (char**) argv); + return ret; +} -- GitLab