diff -urd mtr-0.69.orig/ChangeLog mtr-0.69/ChangeLog
--- mtr-0.69.orig/ChangeLog	2004-08-26 10:56:53.000000000 +0300
+++ mtr-0.69/ChangeLog	2005-01-27 22:15:14.046214500 +0200
@@ -1,3 +1,8 @@
+2005-01-27  Cougar <cougar@random.ee>
+	* includes TTL/MPLS patch from Andres Kroonmaa
+	+ show TTL changes
+	+ show MPLS labels if available (based on http://e.wheel.dk/~jesper/traceroute.diff)
+
 2002-03-06  Cougar <cougar@random.ee>
 	+ If hop doesn't respond, draw its name in red (GTK) or bold (curses)
 
diff -urd mtr-0.69.orig/configure.in mtr-0.69/configure.in
--- mtr-0.69.orig/configure.in	2005-01-11 10:46:56.000000000 +0200
+++ mtr-0.69/configure.in	2005-01-27 22:23:18.529243879 +0200
@@ -1,5 +1,5 @@
 AC_INIT(mtr.c)
-AM_INIT_AUTOMAKE(mtr, 0.69)
+AM_INIT_AUTOMAKE(mtr, 0.69-C20050127)
 
 
 AC_SUBST(GTK_OBJ)
diff -urd mtr-0.69.orig/curses.c mtr-0.69/curses.c
--- mtr-0.69.orig/curses.c	2005-01-11 10:32:11.000000000 +0200
+++ mtr-0.69/curses.c	2005-01-27 22:42:16.592531085 +0200
@@ -76,6 +76,20 @@
 extern float WaitTime;
 extern int af;
 
+struct mpls_header {
+#ifdef _BIG_ENDIAN
+    uint32 label:20;
+    u_char  exp:3;
+    u_char  s:1;
+    u_char  ttl:8;
+#else
+    u_char  ttl:8;
+    u_char  s:1;
+    u_char  exp:3;
+    uint32 label:20;
+#endif
+};
+
 void pwcenter(char *str) 
 {
   int maxx, maxy;
@@ -286,6 +300,7 @@
   int i, j;
   int hd_len;
   char buf[1024];
+  struct mpls_header *mpls;
 
   max = net_max();
 
@@ -345,7 +360,11 @@
         }
         attroff(A_BOLD);
       }
-
+      mpls = (struct mpls_header *) net_mpls(at);
+      if (mpls->label) {
+	printw("\n     MPLS Label=%d CoS=%d TTL=%d S=%d",
+		mpls->label, mpls->exp, mpls->ttl, mpls->s);
+      }
     } else {
       printw("???");
     }
diff -urd mtr-0.69.orig/gtk.c mtr-0.69/gtk.c
--- mtr-0.69.orig/gtk.c	2005-01-11 10:36:49.000000000 +0200
+++ mtr-0.69/gtk.c	2005-01-27 22:14:27.766569834 +0200
@@ -219,8 +219,8 @@
 }
 
 
-char *Report_Text[] = { "Hostname", "Loss", "Rcv", "Snt", "Last", "Best", "Avg", "Worst", "StDev", NULL };
-int Report_Positions[] = { 10, 200, 240, 280, 320, 360, 400, 440, 480, 0 };
+char *Report_Text[] = { "Hostname", "Loss", "Rcv", "Snt", "Last", "Best", "Avg", "Worst", "StDev", "TTL!", NULL };
+int Report_Positions[] = { 10, 200, 240, 280, 320, 360, 400, 440, 480, 520, 0 };
 GtkWidget *Report;
 GtkWidget *ReportBody;
 
@@ -358,6 +358,7 @@
   gtk_set_field_num(List, row - net_min(), 6, "%.0f", net_avg(row)/1000.0);  
   gtk_set_field_num(List, row - net_min(), 7, "%.0f", net_worst(row)/1000.0);
   gtk_set_field_num(List, row - net_min(), 8, "%.2f", net_stdev(row)/1000.0);
+  gtk_set_field(List, row - net_min(), 9, net_regular_ttl(row));
 }
 
 
@@ -393,7 +394,7 @@
 
   gtk_window_set_title(GTK_WINDOW(Window), "My traceroute  [v" VERSION "]");
   gtk_window_set_wmclass(GTK_WINDOW(Window), "mtr", "Mtr");
-  gtk_widget_set_usize(Window, 600, 400); 
+  gtk_widget_set_usize(Window, 640, 400); 
   gtk_container_border_width(GTK_CONTAINER(Window), 10);
   VBox = gtk_vbox_new(FALSE, 10);
 
diff -urd mtr-0.69.orig/mtr.c mtr-0.69/mtr.c
--- mtr-0.69.orig/mtr.c	2005-01-11 10:33:41.000000000 +0200
+++ mtr-0.69/mtr.c	2005-01-27 22:14:27.766569834 +0200
@@ -74,7 +74,7 @@
 
 
 /* default display field(defined by key in net.h) and order */
-unsigned char fld_active[2*MAXFLD] = "LS NABWV";
+unsigned char fld_active[2*MAXFLD] = "LS NABWVT";
 int           fld_index[256];
 char          available_options[MAXFLD];
 
@@ -96,6 +96,7 @@
   {'M', "M: Jitter Mean/Avg.",    "Javg",   " %4.1f",   5, &net_javg  },
   {'X', "X: Worst Jitter",        "Jmax",   " %4.1f",   5, &net_jworst},
   {'I', "I: Interarrival Jitter", "Jint",   " %4.1f",   5, &net_jinta },
+  {'T', "T: Arrival TTL check",   "TTL!",   " %5s",     6, &net_regular_ttl },
   {'\0', NULL, NULL, NULL, 0, NULL}
 };
 
diff -urd mtr-0.69.orig/net.c mtr-0.69/net.c
--- mtr-0.69.orig/net.c	2005-01-13 10:13:53.000000000 +0200
+++ mtr-0.69/net.c	2005-01-27 23:05:32.004935394 +0200
@@ -68,7 +68,38 @@
   uint32 saddr;
   uint32 daddr;
 };
-  
+
+/*  Structure of an IPv6 header.  */
+struct IPv6Header {
+  uint8 version;
+  uint8 flow_lbl[3];
+  uint16 payload_len;
+  uint16 nexthdr;
+  uint8 hop_limit;
+  struct in6_addr saddr;
+  struct in6_addr daddr;
+};
+
+struct myip {
+#ifndef _BIG_ENDIAN
+        u_char  ip_hl:4,                /* header length */
+                ip_v:4;                 /* version */
+#else
+#warning _BIG_ENDIAN
+        u_char  ip_v:4,                 /* version */
+                ip_hl:4;                /* header length */
+#endif
+        u_char  ip_tos;                 /* type of service */
+        short   ip_len;                 /* total length */
+        u_short ip_id;                  /* identification */
+        short   ip_off;                 /* fragment offset field */
+#define IP_DF 0x4000                    /* dont fragment flag */
+#define IP_MF 0x2000                    /* more fragments flag */
+        u_char  ip_ttl;                 /* time to live */
+        u_char  ip_p;                   /* protocol */
+        u_short ip_sum;                 /* checksum */
+        struct  in_addr ip_src,ip_dst;  /* source and dest address */
+};
 
 #define ICMP_ECHO		8
 #define ICMP_ECHOREPLY		0
@@ -82,6 +113,20 @@
 #define SOL_IP 0
 #endif
 
+struct mpls_header {
+#ifdef _BIG_ENDIAN
+    uint32 label:20;
+    u_char  exp:3;
+    u_char  s:1;
+    u_char  ttl:8;
+#else
+    u_char  ttl:8;
+    u_char  s:1;
+    u_char  exp:3;
+    uint32 label:20;
+#endif
+} mpls_hdr;
+
 struct nethost {
   ip_t addr;
   ip_t addrs[MAXPATH];	/* for multi paths byMin */
@@ -101,11 +146,12 @@
   int jworst;	/* max jitter */
   int jinta;	/* estimated variance,? rfc1889's "Interarrival Jitter" */
   int transit;
+  int ret_ttl;
   int saved[SAVED_PINGS];
   int saved_seq_offset;
+  struct mpls_header mpls;
 };
 
-
 struct sequence {
   int index;
   int transit;
@@ -312,16 +358,161 @@
   first = 0;
 }
 
+/*
+ * Support for ICMP extensions
+ *
+ * http://www.ietf.org/proceedings/01aug/I-D/draft-ietf-mpls-icmp-02.txt
+ */
+#define ICMP_EXT_OFFSET    8 /* ICMP type, code, checksum, unused */ + \
+                         128 /* original datagram */
+#define ICMP_EXT_VERSION 2
+/*
+ * ICMP extensions, common header
+ */
+struct icmp_ext_cmn_hdr {
+#ifdef _BIG_ENDIAN
+       u_char   version:4;
+       u_char   reserved1:4;
+#else
+       u_char   reserved1:4;
+       u_char   version:4;
+#endif
+       u_char   reserved2;
+       u_short  checksum;
+};
+
+/*
+ * ICMP extensions, object header
+ */
+struct icmp_ext_obj_hdr {
+    u_short length;
+    u_char  class_num;
+#define MPLS_STACK_ENTRY_CLASS 1
+    u_char  c_type;
+#define MPLS_STACK_ENTRY_C_TYPE 1
+};
+
+void decode_extensions(u_char *buf, int ip_len, int index)
+{
+	struct icmp_ext_cmn_hdr *cmn_hdr;
+	struct icmp_ext_obj_hdr *obj_hdr;
+	struct mpls_header *mpls;
+	int datalen, obj_len;
+	uint32 mpls_h;
+	struct myip *ip = (struct myip *)buf;
+
+	if (ip_len <= sizeof(struct IPHeader) + ICMP_EXT_OFFSET) {
+	    /*
+	     * No support for ICMP extensions on this host
+	     */
+	    return;
+	}
+
+	/*
+	 * Move forward to the start of the ICMP extensions, if present
+	 */
+	buf += (ip->ip_hl << 2) + ICMP_EXT_OFFSET;
+	cmn_hdr = (struct icmp_ext_cmn_hdr *)buf;
+
+	if (cmn_hdr->version != ICMP_EXT_VERSION) {
+	    /*
+	     * Unknown version
+	     */
+	    return;
+	}
+
+	datalen = ip_len - ((u_char *)cmn_hdr - (u_char *)ip);
+
+	/*
+	 * Check the checksum, cmn_hdr->checksum == 0 means no checksum'ing
+	 * done by sender.
+	 *
+	 * If the checksum is ok, we'll get 0, as the checksum is calculated
+	 * with	the checksum field being 0'd.
+	 */
+	if (ntohs(cmn_hdr->checksum) &&
+	    checksum((u_short *)cmn_hdr, datalen)) {
+
+	    return;
+	}
+
+	buf += sizeof(*cmn_hdr);
+	datalen -= sizeof(*cmn_hdr);
+
+	while (datalen > 0) {
+	    obj_hdr = (struct icmp_ext_obj_hdr *)buf;
+	    obj_len = ntohs(obj_hdr->length);
+
+	    /*
+	     * Sanity check the length field
+	     */
+	    if (obj_len > datalen) {
+		return;
+	    }
+
+	    datalen -= obj_len;
+
+	    /*
+	     * Move past the object header
+	     */
+	    buf += sizeof(struct icmp_ext_obj_hdr);
+	    obj_len -= sizeof(struct icmp_ext_obj_hdr);
+
+	    switch (obj_hdr->class_num) {
+	    case MPLS_STACK_ENTRY_CLASS:
+		switch (obj_hdr->c_type) {
+		case MPLS_STACK_ENTRY_C_TYPE:
+		    while (obj_len >= sizeof(uint32)) {
+			mpls_h = ntohl(*(uint32 *)buf);
+
+			buf += sizeof(uint32);
+			obj_len -= sizeof(uint32);
+
+			mpls = (struct mpls_header *) &mpls_h;
+			host[index].mpls = *mpls;
+		    }
+		    if (obj_len > 0) {
+			/*
+			 * Something went wrong, and we're at a unknown offset
+			 * into the packet, ditch the rest of it.
+			 */
+			return;
+		    }
+		    break;
+		default:
+		    /*
+		     * Unknown object, skip past it
+		     */
+		    buf += ntohs(obj_hdr->length) -
+			sizeof(struct icmp_ext_obj_hdr);
+		    break;
+		}
+		break;
+
+	    default:
+		/*
+		 * Unknown object, skip past it
+		 */
+		buf += ntohs(obj_hdr->length) -
+		    sizeof(struct icmp_ext_obj_hdr);
+		break;
+	    }
+	}
+}
 
 /*   We got a return on something we sent out.  Record the address and
      time.  */
-void net_process_ping(int seq, void * addr, struct timeval now) 
+void net_process_ping(char *packet, int seq, void * addr, struct timeval now, int ip_len) 
 {
   int index;
   int totusec;
   int oldavg;	/* usedByMin */
   int oldjavg;	/* usedByMin */
   int i;	/* usedByMin */
+  struct IPHeader *ip = (struct IPHeader *)packet;
+#ifdef ENABLE_IPV6
+  struct IPv6Header *ipv6 = (struct IPv6Header *)packet;
+#endif
 
   if (seq < 0 || seq >= MaxSequence)
     return;
@@ -406,6 +597,18 @@
   host[index].sent = 0;
   host[index].up = 1;
   host[index].transit = 0;
+  switch ( af ) {
+  case AF_INET:
+    host[index].ret_ttl = ip->ttl;
+    break;
+#ifdef ENABLE_IPV6
+  case AF_INET6:
+    host[index].ret_ttl = ipv6->hop_limit;
+    break;
+#endif
+  }
+
+  decode_extensions((u_char *) ip, ip_len, index);
 
   net_save_return(index, sequence[seq].saved_seq, totusec);
   display_rawping(index, totusec);
@@ -473,7 +676,7 @@
     if(header->id != (uint16)getpid())
       return;
 
-    net_process_ping (header->sequence, (void *) fromaddress, now);
+    net_process_ping(packet, header->sequence, (void *) fromaddress, now, num);
   } else if (header->type == timeexceededtype) {
     switch ( af ) {
     case AF_INET:
@@ -502,10 +705,38 @@
     if (header->id != (uint16)getpid())
       return;
 
-    net_process_ping(header->sequence, (void *)fromaddress, now);
+    net_process_ping(packet, header->sequence, (void *) fromaddress, now, num);
   }
 }
 
+int net_ttl(int at) {
+  return host[at].ret_ttl;
+}
+
+char regular_ttl(int ttl)
+{
+        switch (ttl)
+        {
+            case MAX_START_TTL:
+            case NETWARE_TTL:
+            case OLD_PROTEON_TTL:
+            case NEW_PROTEON_TTL:
+            case OLD_BSD_TCP:
+            case NEW_BSD_TCP:
+                return ' ';
+        }
+
+        /* unknown value */
+        return '!';
+}
+
+char ttl_buf[18];
+
+char * net_regular_ttl(int at) {
+  char cc = regular_ttl(net_ttl(at)+at);
+  snprintf(ttl_buf, 8, "%3d%c",net_ttl(at), cc);
+  return ttl_buf;
+}
 
 ip_t *net_addr(int at) 
 {
@@ -518,6 +749,9 @@
   return (ip_t *)&(host[at].addrs[i]);
 }
 
+struct mpls_header *net_mpls(int at) {
+  return &host[at].mpls;
+}
 
 int net_loss(int at) 
 {
@@ -855,6 +1089,7 @@
     host[at].javg = 0;
     host[at].jworst = 0;
     host[at].jinta = 0;
+    host[at].ret_ttl = 0;
     for (i=0; i<SAVED_PINGS; i++) {
       host[at].saved[i] = -2;	/* unsent */
     }
diff -urd mtr-0.69.orig/net.h mtr-0.69/net.h
--- mtr-0.69.orig/net.h	2005-01-12 12:25:28.000000000 +0200
+++ mtr-0.69/net.h	2005-01-27 22:14:27.769569357 +0200
@@ -52,8 +52,10 @@
 int net_jworst(int at);
 int net_javg(int at);
 int net_jinta(int at);
+char * net_regular_ttl(int at);
 ip_t * net_addrs(int at, int i);
 char *net_localaddr(void); 
+struct mpls_header *net_mpls(int at);
 
 int net_send_batch(void);
 void net_end_transit(void);
@@ -85,6 +87,16 @@
 #define MAXPACKET 4470		/* largest test ICMP packet size */
 #define MINPACKET 28		/* 20 bytes IP header and 8 bytes ICMP */
 
+/*
+ * Miscellaneous TTL values. These are checked in module trace_route().
+ */
+#define MAX_START_TTL   255     /* Cisco and BSD4.3 */
+#define NETWARE_TTL     128     /* Netware routers */
+#define OLD_PROTEON_TTL 29      /* Proteon 8.1 and lower */
+#define NEW_PROTEON_TTL 59      /* Proteon 8.2 and higher */
+#define OLD_BSD_TCP     30
+#define NEW_BSD_TCP     60
+
 /* stuff used by display such as report, curses... --Min */
 #define MAXFLD 20		/* max stats fields to display */
 
diff -urd mtr-0.69.orig/split.c mtr-0.69/split.c
--- mtr-0.69.orig/split.c	2005-01-11 10:34:07.000000000 +0200
+++ mtr-0.69/split.c	2005-01-27 22:14:27.769569357 +0200
@@ -103,18 +103,20 @@
       name = dns_lookup(addr);
       if(name != NULL) {
 	/* May be we should test name's length */
-	sprintf(newLine, "%s %d %d %d %d %d %d", name,
+	sprintf(newLine, "%s %d %d %d %d %d %d %s", name,
 		net_loss(at),
 		net_returned(at), net_xmit(at),
 		net_best(at) /1000, net_avg(at)/1000, 
-		net_worst(at)/1000);
+		net_worst(at)/1000,
+		net_regular_ttl(at));
       } else {
-	sprintf(newLine, "%s %d %d %d %d %d %d", 
+	sprintf(newLine, "%s %d %d %d %d %d %d %s", 
 		strlongip( addr ),
 		net_loss(at),
 		net_returned(at), net_xmit(at),
 		net_best(at) /1000, net_avg(at)/1000, 
-		net_worst(at)/1000);
+		net_worst(at)/1000,
+		net_regular_ttl(at));
       }
     } else {
       sprintf(newLine, "???");
