- Removed old Changelog file appended at the end as oldest changes.
2011-03-28 Mark Kamichoff <prox@prolixium.com>
- Enable decoding of ICMP extensions for MPLS for curses and report
interfaces. Use the -e flag or press 'e' to enable it.
source: ftp://ftp.bitwizard.nl/mtr/mtr-0.82.tar.gz
--- /dev/null
+autom4te.cache
+.deps
+++ /dev/null
-2002-03-06 Cougar <cougar@random.ee>
- + If hop doesn't respond, draw its name in red (GTK) or bold (curses)
-
-2002-02-09 bodq <bohdan@vstu.edu.ua>
- * Added --address option to bind to given IP addess
-
-2001-04-15 root <alane@geeksrus.net>
-
- * Added this file so that automake won't complain.
- * Commented out the test for res_init in configure.in;
- it does not work for GLIBC2 systems (e.g., RedHat 7+).
- * Fixed the subordinate CHECK_LIBS on the test for res_mkquery,
- so that they test for res_mkquery, not res_init.
WHAT'S NEW?
- V0.81 Fix for https://bugs.launchpad.net/mtr/+bug/776211
- don't have time right now to integrate some submitted patches.
- Sorry.
+ V0.82 Removed old Changelog file appended at the end as oldest
+ changes.
+ 2011-03-28 Mark Kamichoff <prox@prolixium.com>
+ Enable decoding of ICMP extensions for MPLS for curses and
+ report interfaces. Use the -e flag or press 'e' to enable it.
+ V0.81 Moved to git. Testing git...
V0.80 Some compilation fixes for BSD by Jeremy Chadwick
<freebsd@jdc.parodius.com>
V0.78/0.79 some compilation fixes for BSD&others by
Both the build process and the networking code have
been cleaned up and reorganized. mtr now builds
cleanly with GTK+ 0.99.8.
+
+--- Below is the contents of the old "Changelog file" that annoyed some
+people as it didn't contain any recent changes/news.
+
+2002-03-06 Cougar <cougar@random.ee>
+ + If hop doesn't respond, draw its name in red (GTK) or bold (curses)
+
+2002-02-09 bodq <bohdan@vstu.edu.ua>
+ * Added --address option to bind to given IP addess
+
+2001-04-15 root <alane@geeksrus.net>
+
+ * Added this file so that automake won't complain.
+ * Commented out the test for res_init in configure.in;
+ it does not work for GLIBC2 systems (e.g., RedHat 7+).
+ * Fixed the subordinate CHECK_LIBS on the test for res_mkquery,
+ so that they test for res_mkquery, not res_init.
AC_INIT(mtr.c)
-AM_INIT_AUTOMAKE(mtr, 0.81)
+AM_INIT_AUTOMAKE(mtr, 0.82)
AC_SUBST(GTK_OBJ)
return ActionReset;
if (tolower(c) == 'd')
return ActionDisplay;
+ if (tolower(c) == 'e')
+ return ActionMPLS;
if (tolower(c) == 'n')
return ActionDNS;
if (c == '+')
mvprintw(2, 0, "Command:\n" );
printw(" ?|h help\n" );
printw(" d switching display mode\n" );
+ printw(" e toggle MPLS information on/off\n" );
printw(" n toggle DNS on/off\n" );
printw(" o str set the columns to display, default str='LRS N BAWV'\n" );
printw(" j toggle latency(LS NABWV)/jitter(DR AGJMXI) stats\n" );
{
int max;
int at;
+ struct mplslen *mpls, *mplss;
ip_t *addr, *addrs;
int y, x;
char *name;
- int i, j;
+ int i, j, k;
int hd_len;
char buf[1024];
for(at = net_min () + display_offset; at < max; at++) {
printw("%2d. ", at + 1);
addr = net_addr(at);
+ mpls = net_mpls(at);
if( addrcmp( (void *) addr, (void *) &unspec_addr, af ) != 0 ) {
name = dns_lookup(addr);
buf[hd_len] = 0;
printw("%s", buf);
+ for (k=0; k < mpls->labels && enablempls; k++) {
+ if((k+1 < mpls->labels) || (mpls->labels == 1)) {
+ /* if we have more labels */
+ printw("\n [MPLS: Lbl %lu Exp %u S %u TTL %u]", mpls->label[k], mpls->exp[k], mpls->s[k], mpls->ttl[k]);
+ } else {
+ /* bottom label */
+ printw("\n [MPLS: Lbl %lu Exp %u S %u TTL %u]", mpls->label[k], mpls->exp[k], mpls->s[k], mpls->ttl[k]);
+ }
+ }
+
/* Multi path by Min */
for (i=0; i < MAXPATH; i++ ) {
addrs = net_addrs(at, i);
+ mplss = net_mplss(at, i);
if ( addrcmp( (void *) addrs, (void *) addr, af ) == 0 ) continue;
if ( addrcmp( (void *) addrs, (void *) &unspec_addr, af ) == 0 ) break;
} else {
printw("\n %s", strlongip( addrs ) );
}
+ for (k=0; k < mplss->labels && enablempls; k++) {
+ printw("\n [MPLS: Lbl %lu Exp %u S %u TTL %u]", mplss->label[k], mplss->exp[k], mplss->s[k], mplss->ttl[k]);
+ }
attroff(A_BOLD);
}
/* Don't put a trailing comma in enumeration lists. Some compilers
(notably the one on Irix 5.2) do not like that. -- REW */
enum { ActionNone, ActionQuit, ActionReset, ActionDisplay,
- ActionClear, ActionPause, ActionResume, ActionDNS,
+ ActionClear, ActionPause, ActionResume, ActionMPLS, ActionDNS,
ActionScrollDown, ActionScrollUp };
enum { DisplayReport, DisplayCurses, DisplayGTK, DisplaySplit,
DisplayRaw, DisplayXML, DisplayCSV, DisplayTXT};
.SH SYNOPSIS
.B mtr
[\c
-.B \-hvrctglspniu46\c
+.B \-hvrctglspeniu46\c
]
[\c
.B \-\-help\c
.B \-\-raw\c
]
[\c
+.B \-\-mpls\c
+]
+[\c
.B \-\-no-dns\c
]
[\c
to use the curses based terminal
interface (if available).
+.TP
+.B \-e
+.TP
+.B \-\-mpls
+.br
+Use this option to tell
+.B mtr
+to display information from ICMP extensions for MPLS (RFC 4950)
+that are encoded in the response packets.
+
.TP
.B \-n
.TP
char *InterfaceAddress = NULL;
char LocalHostname[128];
int dns = 1;
+int enablempls = 0;
int cpacketsize = 64; /* default packet size */
int bitpattern = 0;
int tos = 0;
overload psize<0, ->rand(min,max) */
{ "bitpattern", 1, 0, 'b' },/* overload b>255, ->rand(0,255) */
{ "tos", 1, 0, 'Q' }, /* typeof service (0,255) */
+ { "mpls", 0, 0, 'e' },
{ "no-dns", 0, 0, 'n' },
{ "address", 1, 0, 'a' },
{ "first-ttl", 1, 0, 'f' }, /* -f & -m are borrowed from traceroute */
while(1) {
/* added f:m:o: byMin */
opt = getopt_long(argc, argv,
- "vhrwxtglpo:i:c:s:b:Q:na:f:m:u46", long_options, NULL);
+ "vhrwxtglpo:i:c:s:b:Q:ena:f:m:u46", long_options, NULL);
if(opt == -1)
break;
case 'a':
InterfaceAddress = optarg;
break;
+ case 'e':
+ enablempls = 1;
+ break;
case 'n':
dns = 0;
break;
if (PrintHelp) {
printf("usage: %s [-hvrwctglspniu46] [--help] [--version] [--report]\n"
"\t\t[--report-wide] [--report-cycles=COUNT] [--curses] [--gtk]\n"
- "\t\t[--raw] [--split] [--no-dns] [--address interface]\n" /* BL */
+ "\t\t[--raw] [--split] [--mpls] [--no-dns] [--address interface]\n" /* BL */
"\t\t[--psize=bytes/-s bytes]\n" /* ok */
"\t\t[--report-wide|-w] [-u]\n" /* rew */
"\t\t[--interval=SECONDS] HOSTNAME [PACKETSIZE]\n", argv[0]);
typedef struct in_addr ip_t;
#endif
+extern int enablempls;
extern int dns;
extern int use_dns;
int transit;
int saved[SAVED_PINGS];
int saved_seq_offset;
+ struct mplslen mpls;
+ struct mplslen mplss[MAXPATH];
};
/* 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(int seq, struct mplslen mpls, void * addr, struct timeval now)
{
int index;
int totusec;
(void *) &unspec_addr, af ) == 0 ) {
/* should be out of if as addr can change */
addrcpy( (void *) &(host[index].addr), addrcopy, af );
+ host[index].mpls = mpls;
display_rawhost(index, (void *) &(host[index].addr));
/* multi paths by Min */
addrcpy( (void *) &(host[index].addrs[0]), addrcopy, af );
+ host[index].mplss[0] = mpls;
} else {
for( i=0; i<MAXPATH; ) {
if( addrcmp( (void *) &(host[index].addrs[i]), (void *) &addrcopy,
if( addrcmp( (void *) &(host[index].addrs[i]), addrcopy, af ) != 0 &&
i<MAXPATH ) {
addrcpy( (void *) &(host[index].addrs[i]), addrcopy, af );
+ host[index].mplss[i] = mpls;
/* rafaelmartins: multi path support to '--raw' */
display_rawhost(index, (void *) &(host[index].addrs[i]));
int echoreplytype = 0, timeexceededtype = 0, unreachabletype = 0;
int sequence = 0;
+ /* MPLS decoding */
+ struct mplslen mpls;
+ mpls.labels = 0;
+
gettimeofday(&now, NULL);
switch ( af ) {
case AF_INET:
header = (struct ICMPHeader *)(packet + sizeof (struct IPHeader) +
sizeof (struct ICMPHeader) +
sizeof (struct IPHeader));
+
+ if(num > 160)
+ decodempls(num, packet, &mpls, 156);
+
break;
#ifdef ENABLE_IPV6
case AF_INET6:
header = (struct ICMPHeader *) ( packet +
sizeof (struct ICMPHeader) +
sizeof (struct ip6_hdr) );
+
+ if(num > 140)
+ decodempls(num, packet, &mpls, 136);
+
break;
#endif
}
udpheader = (struct UDPHeader *)(packet + sizeof (struct IPHeader) +
sizeof (struct ICMPHeader) +
sizeof (struct IPHeader));
+
+ if(num > 160)
+ decodempls(num, packet, &mpls, 156);
+
break;
#ifdef ENABLE_IPV6
case AF_INET6:
udpheader = (struct UDPHeader *) ( packet +
sizeof (struct ICMPHeader) +
sizeof (struct ip6_hdr) );
+
+ if(num > 140)
+ decodempls(num, packet, &mpls, 136);
+
break;
#endif
}
}
if (sequence)
- net_process_ping(sequence, (void *)fromaddress, now);
+ net_process_ping (sequence, mpls, (void *) fromaddress, now);
}
return (ip_t *)&(host[at].addrs[i]);
}
+void *net_mpls(int at)
+{
+ return (struct mplslen *)&(host[at].mplss);
+}
+
+void *net_mplss(int at, int i)
+{
+ return (struct mplslen *)&(host[at].mplss[i]);
+}
int net_loss(int at)
{
#endif
}
}
+
+/* Decode MPLS */
+void decodempls(int num, char *packet, struct mplslen *mpls, int offset) {
+
+ int i;
+ unsigned int ext_ver, ext_res, ext_chk, obj_hdr_len;
+ u_char obj_hdr_class, obj_hdr_type;
+
+ /* loosely derived from the traceroute-nanog.c
+ * decoding by Jorge Boncompte */
+ ext_ver = packet[offset]>>4;
+ ext_res = (packet[offset]&15)+ packet[offset+1];
+ ext_chk = ((unsigned int)packet[offset+2]<<8)+packet[offset+3];
+
+ /* Check for ICMP extension header */
+ if (ext_ver == 2 && ext_res == 0 && ext_chk != 0 && num >= (offset+6)) {
+ obj_hdr_len = ((int)packet[offset+4]<<8)+packet[offset+5];
+ obj_hdr_class = packet[offset+6];
+ obj_hdr_type = packet[offset+7];
+
+ /* make sure we have an MPLS extension */
+ if (obj_hdr_len >= 8 && obj_hdr_class == 1 && obj_hdr_type == 1) {
+ /* how many labels do we have? will be at least 1 */
+ mpls->labels = (obj_hdr_len-4)/4;
+
+ /* save all label objects */
+ for(i=0; (i<mpls->labels) && (i < MAXLABELS) && (num >= (offset+8)+(i*4)); i++) {
+
+ /* piece together the 20 byte label value */
+ mpls->label[i] = ((unsigned long) (packet[(offset+8)+(i*4)] << 12 & 0xff000) +
+ (unsigned int) (packet[(offset+9)+(i*4)] << 4 & 0xff0) +
+ (packet[(offset+10)+(i*4)] >> 4 & 0xf));
+ mpls->exp[i] = (packet[(offset+10)+(i*4)] >> 1) & 0x7;
+ mpls->s[i] = (packet[(offset+10)+(i*4)] & 0x1); /* should be 1 if only one label */
+ mpls->ttl[i] = packet[(offset+11)+(i*4)];
+ }
+ }
+ }
+}
int net_min(void);
int net_last(int at);
ip_t * net_addr(int at);
+void * net_mpls(int at);
+void * net_mplss(int, int);
int net_loss(int at);
int net_drop(int at);
int net_last(int at);
#define MAXPACKET 4470 /* largest test packet size */
#define MINPACKET 28 /* 20 bytes IP header and 8 bytes ICMP or UDP */
+#define MAXLABELS 8 /* http://kb.juniper.net/KB2190 (+ 3 just in case) */
/* stuff used by display such as report, curses... --Min */
#define MAXFLD 20 /* max stats fields to display */
extern char available_options[];
ip_t unspec_addr;
+
+/* MPLS label object */
+struct mplslen {
+ unsigned long label[MAXLABELS]; /* label value */
+ uint8 exp[MAXLABELS]; /* experimental bits */
+ uint8 ttl[MAXLABELS]; /* MPLS TTL */
+ char s[MAXLABELS]; /* bottom of stack */
+ char labels; /* how many labels did we get? */
+};
+
+void decodempls(int, char *, struct mplslen *, int);
void report_close(void)
{
- int i, j, at, max, z, w;
+ int i, j, k, at, max, z, w;
+ struct mplslen *mpls, *mplss;
ip_t *addr;
ip_t *addr2 = NULL;
char name[81];
at = net_min();
for(; at < max; at++) {
addr = net_addr(at);
+ mpls = net_mpls(at);
if( addrcmp( (void *) addr, (void *) &unspec_addr, af ) == 0 ) {
sprintf(name, "???");
} else {
* This feature show 'loadbalances' on routes
*/
- /* z is starting at 1 because addrs[0] is the same that addr */
+ /* z is starting at 1 because addrs[0] is the same that addr */
for (z = 1; z < MAXPATH ; z++) {
- addr2 = net_addrs(at, z);
- int found = 0;
- if ((addrcmp ((void *) &unspec_addr, (void *) addr2, af)) == 0)
- break;
+ addr2 = net_addrs(at, z);
+ mplss = net_mplss(at, z);
+ int found = 0;
+ if ((addrcmp ((void *) &unspec_addr, (void *) addr2, af)) == 0)
+ break;
for (w = 0; w < z; w++)
/* Thales -- Ok... checking if there are ips repeated on same hop */
- if ((addrcmp ((void *) addr2, (void *) net_addrs (at,w), af)) == 0) {
- found = 1;
- break;
- }
- if (!found) {
- if (z == 1)
+ if ((addrcmp ((void *) addr2, (void *) net_addrs (at,w), af)) == 0) {
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+
+ if (mpls->labels && z == 1 && enablempls) {
+ for (k=0; k < mpls->labels; k++) {
+ printf(" | |+-- [MPLS: Lbl %lu Exp %u S %u TTL %u]\n", mpls->label[k], mpls->exp[k], mpls->s[k], mpls->ttl[k]);
+ }
+ }
+
+ if (z == 1) {
printf (" | `|-- %s\n", strlongip(addr2));
- else
+ for (k=0; k < mplss->labels && enablempls; k++) {
+ printf(" | +-- [MPLS: Lbl %lu Exp %u S %u TTL %u]\n", mplss->label[k], mplss->exp[k], mplss->s[k], mplss->ttl[k]);
+ }
+ } else {
printf (" | |-- %s\n", strlongip(addr2));
- }
+ for (k=0; k < mplss->labels && enablempls; k++) {
+ printf(" | +-- [MPLS: Lbl %lu Exp %u S %u TTL %u]\n", mplss->label[k], mplss->exp[k], mplss->s[k], mplss->ttl[k]);
+ }
+ }
+ }
+ }
+
+ /* No multipath */
+ if(mpls->labels && z == 1 && enablempls) {
+ for (k=0; k < mpls->labels; k++) {
+ printf(" | +-- [MPLS: Lbl %lu Exp %u S %u TTL %u]\n", mpls->label[k], mpls->exp[k], mpls->s[k], mpls->ttl[k]);
+ }
}
}
}
int display_offset = 0;
-#define GRACETIME (5 * 1000*1000)
-
void select_loop(void) {
fd_set readfd;
int anyset = 0;
int NumPing = 0;
int paused = 0;
struct timeval lasttime, thistime, selecttime;
- struct timeval startgrace, stopgrace;
int dt;
int rv;
- int graceperiod = 0;
-
gettimeofday(&lasttime, NULL);
selecttime.tv_usec = 0;
rv = select(maxfd, (void *)&readfd, NULL, NULL, &selecttime);
+
} else {
if(Interactive) display_redraw();
(thistime.tv_sec == lasttime.tv_sec + intervaltime.tv_sec &&
thistime.tv_usec >= lasttime.tv_usec + intervaltime.tv_usec)) {
lasttime = thistime;
- if (!graceperiod) {
- if(NumPing >= MaxPing && (!Interactive || ForceMaxPing)) {
- graceperiod=1;
- startgrace=thistime;
- //gettimeofday (&startgrace, NULL);
- }
- if (net_send_batch())
- NumPing++;
- }
- }
- if (graceperiod) {
- // gettimeofday(&thistime, NULL);
- dt = (thistime.tv_usec - startgrace.tv_usec) +
- 1000000 * (thistime.tv_sec - startgrace.tv_sec);
- if (dt > GRACETIME) return;
+ if(NumPing >= MaxPing && (!Interactive || ForceMaxPing))
+ return;
+ if (net_send_batch())
+ NumPing++;
}
selecttime.tv_usec = (thistime.tv_usec - lasttime.tv_usec);
case ActionResume:
paused=0;
break;
+ case ActionMPLS:
+ enablempls = !enablempls;
+ display_clear();
+ break;
case ActionDNS:
if (dns) {
use_dns = !use_dns;