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