From: Matt Kimball Date: Tue, 11 Jul 2017 21:55:40 +0000 (-0700) Subject: ui: display "no route to host" error as host entry rather than abort X-Git-Tag: v0.93~45^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F209%2Fhead;p=thirdparty%2Fmtr.git ui: display "no route to host" error as host entry rather than abort When a host is reported as unreachable in response to a particular network probe, the Windows version of the mtr UI had the unfortuante behavior of exiting and and printing a terse message. This is surprising to a user, who would expect mtr probes and reporting to continue. With this change, probing continues and a status reading "no route to host" is displayed in the probe report. This change will also improve the reporting of the Unix version of mtr, now specifying "no route to host" instead of misleadingly indicating that no reply has been received. This fixes issue #179. --- diff --git a/ui/cmdpipe.c b/ui/cmdpipe.c index a90fa4f..a3e5783 100644 --- a/ui/cmdpipe.c +++ b/ui/cmdpipe.c @@ -621,16 +621,6 @@ void handle_reply_errors( reply_name = reply->command_name; - if (!strcmp(reply_name, "no-route")) { - display_close(ctl); - error(EXIT_FAILURE, 0, "No route to host"); - } - - if (!strcmp(reply_name, "network-down")) { - display_close(ctl); - error(EXIT_FAILURE, 0, "Network down"); - } - if (!strcmp(reply_name, "probes-exhausted")) { display_close(ctl); error(EXIT_FAILURE, 0, "Probes exhausted"); @@ -677,6 +667,7 @@ void handle_command_reply( struct command_t reply; ip_t fromaddress; int seq_num; + int err; int round_trip_time; char *reply_name; struct mplslen mpls; @@ -698,8 +689,16 @@ void handle_command_reply( seq_num = reply.token; reply_name = reply.command_name; - /* If the reply type is unknown, ignore it for future compatibility */ - if (strcmp(reply_name, "reply") && strcmp(reply_name, "ttl-expired")) { + /* Check for known reply types. */ + if (!strcmp(reply_name, "reply") + || !strcmp(reply_name, "ttl-expired")) { + err = 0; + } else if (!strcmp(reply_name, "no-route")) { + err = ENETUNREACH; + } else if (!strcmp(reply_name, "network-down")) { + err = ENETDOWN; + } else { + /* If the reply type is unknown, ignore it */ return; } @@ -710,7 +709,7 @@ void handle_command_reply( if (parse_reply_arguments (ctl, &reply, &fromaddress, &round_trip_time, &mpls)) { - reply_func(ctl, seq_num, &mpls, (void *) &fromaddress, + reply_func(ctl, seq_num, err, &mpls, (void *) &fromaddress, round_trip_time); } } diff --git a/ui/cmdpipe.h b/ui/cmdpipe.h index 7a5b687..93d4439 100644 --- a/ui/cmdpipe.h +++ b/ui/cmdpipe.h @@ -47,6 +47,7 @@ void ( *probe_reply_func_t) ( struct mtr_ctl * ctl, int sequence, + int err, struct mplslen * mpls, ip_t * addr, int round_trip_time); diff --git a/ui/curses.c b/ui/curses.c index a7588ca..7566ad7 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -404,6 +404,8 @@ static void mtr_curses_hosts( int at; struct mplslen *mpls, *mplss; ip_t *addr, *addrs; + int addrcmp_result; + int err; int y; char *name; @@ -416,11 +418,14 @@ static void mtr_curses_hosts( for (at = net_min(ctl) + ctl->display_offset; at < max; at++) { printw("%2d. ", at + 1); + err = net_err(at); addr = net_addr(at); mpls = net_mpls(at); - if (addrcmp((void *) addr, (void *) &ctl->unspec_addr, ctl->af) != - 0) { + addrcmp_result = addrcmp( + (void *) addr, (void *) &ctl->unspec_addr, ctl->af); + + if (err == 0 && addrcmp_result != 0) { name = dns_lookup(ctl, addr); if (!net_up(at)) attron(A_BOLD); @@ -498,9 +503,10 @@ static void mtr_curses_hosts( } attroff(A_BOLD); } - } else { - printw("???"); + attron(A_BOLD); + printw("(%s)", host_error_to_string(err)); + attroff(A_BOLD); } printw("\n"); @@ -618,7 +624,7 @@ static void mtr_curses_graph( int startstat, int cols) { - int max, at, y; + int max, at, y, err; ip_t *addr; char *name; int __unused_int ATTRIBUTE_UNUSED; @@ -629,22 +635,31 @@ static void mtr_curses_graph( printw("%2d. ", at + 1); addr = net_addr(at); + err = net_err(at); + if (!addr) { - printw("???\n"); + printw("(%s)", host_error_to_string(err)); continue; } - if (!net_up(at)) - attron(A_BOLD); - if (addrcmp((void *) addr, (void *) &ctl->unspec_addr, ctl->af)) { + if (err == 0 + && addrcmp((void *) addr, (void *) &ctl->unspec_addr, ctl->af)) { + + if (!net_up(at)) { + attron(A_BOLD); + } + #ifdef HAVE_IPINFO if (is_printii(ctl)) printw(fmt_ipinfo(ctl, addr)); #endif name = dns_lookup(ctl, addr); printw("%s", name ? name : strlongip(ctl, addr)); - } else - printw("???"); + } else { + attron(A_BOLD); + printw("(%s)", host_error_to_string(err)); + } + attroff(A_BOLD); getyx(stdscr, y, __unused_int); diff --git a/ui/display.c b/ui/display.c index 4c08889..95db140 100644 --- a/ui/display.c +++ b/ui/display.c @@ -18,8 +18,10 @@ #include "config.h" +#include #include #include +#include #include #include @@ -250,3 +252,26 @@ void display_clear( mtr_curses_clear(ctl); #endif } + + +/* + Given an errno error code corresponding to a host entry, return a + user readable error string. +*/ +char *host_error_to_string( + int err) +{ + if (err == ENETUNREACH) { + return "no route to host"; + } + + if (err == ENETDOWN) { + return "network down"; + } + + if (err == 0) { + return "waiting for reply"; + } + + return strerror(err); +} diff --git a/ui/display.h b/ui/display.h index d827528..e3561af 100644 --- a/ui/display.h +++ b/ui/display.h @@ -81,3 +81,5 @@ extern void display_loop( struct mtr_ctl *ctl); extern void display_clear( struct mtr_ctl *ctl); +extern char *host_error_to_string( + int err); diff --git a/ui/net.c b/ui/net.c index 3a7abc8..6396ab6 100644 --- a/ui/net.c +++ b/ui/net.c @@ -51,6 +51,7 @@ static void sockaddrtop( struct nethost { ip_t addr; ip_t addrs[MAXPATH]; /* for multi paths byMin */ + int err; int xmit; int returned; int sent; @@ -185,11 +186,37 @@ static void net_send_query( } -/* We got a return on something we sent out. Record the address and - time. */ +/* + Mark a sequence entry as completed and return the host index + being probed. + + Returns -1 in the case of an invalid sequence number. +*/ +static int mark_sequence_complete( + int seq) +{ + if ((seq < 0) || (seq >= MaxSequence)) { + return -1; + } + + if (!sequence[seq].transit) { + return -1; + } + sequence[seq].transit = 0; + + return sequence[seq].index; +} + + +/* + A probe has successfully completed. + + Record the round trip time and address of the responding host. +*/ static void net_process_ping( struct mtr_ctl *ctl, int seq, + int err, struct mplslen *mpls, ip_t * addr, int totusec) @@ -206,16 +233,12 @@ static void net_process_ping( addrcpy((void *) &addrcopy, (char *) addr, ctl->af); - if ((seq < 0) || (seq >= MaxSequence)) { - return; - } - - if (!sequence[seq].transit) { + index = mark_sequence_complete(seq); + if (index < 0) { return; } - sequence[seq].transit = 0; - index = sequence[seq].index; + host[index].err = err; if (addrcmp((void *) &(host[index].addr), (void *) &ctl->unspec_addr, ctl->af) == 0) { @@ -325,6 +348,15 @@ ip_t *net_addrs( return (ip_t *) & (host[at].addrs[i]); } +/* + Get the error code corresponding to a host entry. +*/ +int net_err( + int at) +{ + return host[at].err; +} + void *net_mpls( int at) { @@ -444,6 +476,13 @@ int net_max( if (addrcmp((void *) &(host[at].addr), (void *) remoteaddress, ctl->af) == 0) { return at + 1; + } else if (host[at].err != 0) { + /* + If a hop has returned an ICMP error + (such as "no route to host") then we'll consider that the + final hop. + */ + return at + 1; } else if (addrcmp((void *) &(host[at].addr), (void *) &ctl->unspec_addr, ctl->af) != 0) { max = at + 2; diff --git a/ui/net.h b/ui/net.h index 6b95a00..c713914 100644 --- a/ui/net.h +++ b/ui/net.h @@ -56,6 +56,8 @@ extern int net_last( int at); extern ip_t *net_addr( int at); +extern int net_err( + int at); extern void *net_mpls( int at); extern void *net_mplss(