- Lots of stuff.
- Neato overview display by David Sward.
- FreeBSD does wrong in the kernel the same that Solaris/x86 (see
note for 0.27 does right. It forces mtr to send bad packets....
- Adjusted "not too much at once" algorithm. Now probing continues as
long as not more than 5 hosts are unknown. Returning packets
usually allow us to do the first sweep in one go.
source: ftp://ftp.bitwizard.nl/mtr/mtr-0.29.tar.gz
Thanks to everyone who has provided feedback on mtr.
Thanks especially to those of you who have sent code:
+ (Reverse alphabetical order)
+
+ Juha Takala,
+ David Sward (sward@clark.net),
+ Andrew Stesin,
+ Aaron Scarisbrick,
+ Russell Nelson,
+ Alexander V. Lukyanov,
+ Charles Levert,
+ Bertrand Leconte,
+ Anand Kumria,
+ Adam Kramer (l3zqc@qcunix1.acc.qc.edu),
+ Simon Kirby,
+ Christophe Kalt,
+ Mircea Damian,
+ Brian Casey,
- Brian Casey, Mircea Damian, Christophe Kalt, Simon Kirby,
- Anand Kumria, Bertrand Leconte, Charles Levert, Alexander
- V. Lukyanov, Russell Nelson, Aaron Scarisbrick, Andrew
- Stesin, Juha Takala.
and anyone who has slipped through the cracks of my mail file.
+
+Authors: If you want your Email mentioned here, send it to me.
+ If you don't want your Email mentioned here, tell me.
+
+ -- REW
WHAT'S NEW?
+ v0.29 Lots of stuff.
+ Neato overview display by David Sward.
+ FreeBSD does wrong in the kernel the same that Solaris/x86 (see
+ note for 0.27 does right. It forces mtr to send bad packets....
+ Adjusted "not too much at once" algorithm. Now probing
+ continues as long as not more than 5 hosts are unknown.
+ Returning packets usually allow us to do the first sweep
+ in one go.
+
+
+ v0.28 DNS lookups are now suppressed if you don't want them.
+
v0.27
Fixed bug that showed up on Solaris/x86.
Gimp mainloop now runs as it's supposed to.
INSTALLING
- To begin mtr, first use the included configure script:
+ Slightly more hints are in the file INSTALL. If this doesn't work
+ you could try reading that too....
+
+ To begin compiling mtr, first use the included configure script:
./configure
Note that mtr must be suid-root because it requires access to raw IP
sockets. See SECURITY for security information.
+ If you want to build a version that doesn't use GTK, but if you
+ DO have GTK installed, you can use
+
+ ./configure --with-gtk-prefix=/we_dont_want_gtk_so_we_use_a_name_that_certainly_doesnt_exist
+
+ It seems the --no_gtk (whatever) doesn't work. Feel free to submit
+ a patch for this, I'm not that familiar with autoconf (it works for
+ me :-).
+
+
+
+
WHERE CAN I GET THE LATEST VERSION OR MORE INFORMATION?
See the mtr web page at
Bug reports and feature requests should be sent to the mtr
mailing list.
-
- Bugs to fix?
- - ?
+
+ - Do something useful if host couldn't be resolved.
+
+- Nice to have:
+
+ - stop sending packets when a new host is getting entered.
+
+ - Show state ("looking up host") while doing the DNS lookup for a new
+ host.
+
+ - Check why it still tries to go for X11 display if DISPLAY is unset.
+ (I saw code to handle this)
+
+ - Revert to curses mode even if DISPLAY is set, but a problem
+ prevents us from running in X11 mode.
+ --> The problem is that gtk_init simply calls exit for us if
+ it finds a problem. Tricky!
+
+ - Read environment variable "MTR_DEFAULTS" as a commandline before
+ parsing the commandline.
AC_INIT(mtr.c)
-AM_INIT_AUTOMAKE(mtr, 0.28)
+AM_INIT_AUTOMAKE(mtr, 0.29)
AC_SUBST(GTK_OBJ)
AC_SUBST(CURSES_OBJ)
AC_CHECK_FUNC(gethostbyname, ,
AC_CHECK_LIB(nsl, gethostbyname, , AC_MSG_ERROR(No nameservice library found)))
+AC_CHECK_FUNC(res_init, ,
+ AC_CHECK_LIB(bind, res_init, , AC_MSG_ERROR(No resolver library found)))
+
AC_CHECK_FUNC(res_mkquery, ,
AC_CHECK_LIB(resolv, res_mkquery, , AC_MSG_ERROR(No resolver library found)))
return ActionQuit;
if(c==3)
return ActionQuit;
+ if (c==12)
+ return ActionClear;
if(tolower(c) == 'r')
return ActionReset;
+ if (tolower(c) == 'd')
+ return ActionDisplay;
return 0;
}
getyx(stdscr, y, x);
move(y, startstat);
- printw(" %3d%% %3d %3d %4d %4d %6d",
+ printw(" %3d%% %4d %4d %4d %4d %6d",
net_percent(at),
net_returned(at), net_xmit(at),
net_best(at), net_avg(at), net_worst(at));
}
}
+static double factors[] = { 0.02, 0.05, 0.08, 0.15, 0.33, 0.50, 0.80, 1.00 };
+static int scale[8];
+static int low_ms, high_ms;
+
+void mtr_gen_scale(void) {
+ int *saved, i, max, at;
+ int range;
+
+ low_ms = 1000000;
+ high_ms = -1;
+
+ for (i = 0; i < 8; i++) {
+ scale[i] = 0;
+ }
+ max = net_max();
+ for (at = 0; at < max; at++) {
+ saved = net_saved_pings(at);
+ for (i = 0; i < SAVED_PINGS; i++) {
+ if (saved[i] < 0) continue;
+ if (saved[i] < low_ms) {
+ low_ms = saved[i];
+ }
+ if (saved[i] > high_ms) {
+ high_ms = saved[i];
+ }
+ }
+ }
+ range = high_ms - low_ms;
+ for (i = 0; i < 8; i++) {
+ scale[i] = low_ms + ((double)range * factors[i]);
+ }
+}
+
+static const char* block_map = ".123abc>";
+
+void mtr_print_scaled(int ms) {
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (ms <= scale[i]) {
+ printw("%c", block_map[i]);
+ return;
+ }
+ }
+ printw(">");
+}
+
+void mtr_fill_graph(int at) {
+ int* saved;
+ int i;
+ int val;
+
+ saved = net_saved_pings(at);
+ for (i = 0; i < SAVED_PINGS; i++) {
+ if (saved[i] == -2) {
+ printw(" ");
+ } else if (saved[i] == -1) {
+ attron(A_BOLD);
+ printw("?");
+ attroff(A_BOLD);
+ } else {
+ if (display_mode == 1) {
+ if (saved[i] > scale[6]) {
+ printw("%c", block_map[7]);
+ } else {
+ printw(".");
+ }
+ } else {
+ mtr_print_scaled(saved[i]);
+ }
+ }
+ }
+}
+
+void mtr_curses_graph(int startstat) {
+ int max, at, addr, y, x;
+ char* name;
+ char blocks[50];
+
+ max = net_max();
+ for (at = 0; at < max; at++) {
+ printw("%2d. ", at+1);
+
+ addr = net_addr(at);
+ if (!addr) {
+ printw("???\n");
+ continue;
+ }
+
+ name = dns_lookup(addr);
+ if (name) {
+ printw("%s", name);
+ } else {
+ printw("%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) && 0xff, (addr >> 8) & 0xff, addr & 0xff);
+ }
+
+ getyx(stdscr, y, x);
+ move(y, startstat);
+
+ printw(" ");
+ mtr_fill_graph(at);
+ printw("\n");
+ }
+}
+
void mtr_curses_redraw() {
int maxx, maxy;
int startstat;
int rowstat;
+ int i;
erase();
getmaxyx(stdscr, maxy, maxx);
- /* Modified by Brian Casey December 1997 bcasey@imagiware.com */
- startstat = maxx - 40;
-
rowstat = 5;
attron(A_BOLD);
attroff(A_BOLD);
printw("Keys: ");
+ attron(A_BOLD); printw("D"); attroff(A_BOLD);
+ printw(" - Display mode ");
attron(A_BOLD); printw("R"); attroff(A_BOLD);
printw(" - Restart statistics ");
attron(A_BOLD); printw("Q"); attroff(A_BOLD);
attron(A_BOLD);
mvprintw(rowstat - 1, 0, "Hostname");
- /* Modified by Brian Casey December 1997 bcasey@imagiware.com */
- mvprintw(rowstat - 2, startstat, " Packets Pings");
- mvprintw(rowstat - 1, startstat, " %%Loss Rcv Snt Best Avg Worst");
+ if (display_mode == 0) {
+ startstat = maxx - 40;
- attroff(A_BOLD);
- move(rowstat, 0);
+ /* Modified by Brian Casey December 1997 bcasey@imagiware.com */
+ mvprintw(rowstat - 2, startstat, " Packets Pings");
+ mvprintw(rowstat - 1, startstat, " %%Loss Rcv Snt Best Avg Worst");
+
+ attroff(A_BOLD);
+ move(rowstat, 0);
+
+ mtr_curses_hosts(startstat);
+ } else {
+ /* David Sward, Jan 1999 */
+ startstat = maxx - 52;
- mtr_curses_hosts(startstat);
+ mvprintw(rowstat - 1, startstat, " Last 50 pings");
+
+ attroff(A_BOLD);
+ move(rowstat, 0);
+
+ mtr_gen_scale();
+ mtr_curses_graph(startstat);
+
+ printw("\n");
+ attron(A_BOLD);
+ printw("Scale:");
+ attroff(A_BOLD);
+
+ for (i = 0; i < 7; i++) {
+ printw(" %c:%d ms", block_map[i], scale[i]);
+ }
+ }
refresh();
}
printw("\n");
endwin();
}
+
+void mtr_curses_clear() {
+ mtr_curses_close();
+ mtr_curses_open();
+}
}
}
+
+void display_clear() {
+ switch(DisplayMode) {
+ case DisplayCurses:
+ mtr_curses_clear();
+ break;
+ case DisplayReport:
+ case DisplaySplit: /* BL */
+ case DisplayRaw:
+ break;
+
+ case DisplayGTK:
+ break;
+ }
+}
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-enum { ActionNone, ActionQuit, ActionReset };
+enum { ActionNone, ActionQuit, ActionReset, ActionDisplay, ActionClear };
enum { DisplayReport, DisplayCurses, DisplayGTK, DisplaySplit, DisplayRaw };
/* Prototypes for display.c */
void display_rawhost(int hostnum, int ip_addr);
int display_keyaction();
void display_loop();
+void display_clear();
+
+int display_mode;
GtkWidget *Scrollarea_create() {
GtkWidget *List;
+ GtkWidget *scroll;
int count;
for(count = 0; Report_Positions[count]; count++);
List = GTK_WIDGET(gtk_clist_new_with_titles(count, Report_Text));
- gtk_clist_set_policy(GTK_CLIST(List), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ scroll = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
for(count = 0; Report_Positions[count + 1]; count++) {
gtk_clist_set_column_width(GTK_CLIST(List), count,
Report_Positions[count + 1] -
for(count = 1; Report_Positions[count]; count++) {
gtk_clist_set_column_justification(GTK_CLIST(List), count, GTK_JUSTIFY_RIGHT);
}
+ gtk_container_add(GTK_CONTAINER(scroll), List);
+ gtk_widget_show(List);
ReportBody = List;
- return List;
+ return scroll;
}
void gtk_add_row(GtkWidget *List) {
void mtr_curses_close();
void mtr_curses_redraw();
int mtr_curses_keyaction();
+void mtr_curses_clear();
display_open();
dns_open();
+ display_mode = 0;
display_loop();
net_end_transit();
#include <unistd.h>
#include <stdio.h>
#include <math.h>
+#include <errno.h>
#include "net.h"
extern float WaitTime, DeltaTime;
+int timestamp;
#define MaxTransit 4
#define ICMP_ECHO 8
#define ICMP_ECHOREPLY 0
+
+#define ICMP_TSTAMP 13
+#define ICMP_TSTAMPREPLY 14
+
#define ICMP_TIME_EXCEEDED 11
-
+
#ifndef SOL_IP
#define SOL_IP 0
#endif
int ttl;
int sec;
int msec;
+ int seq;
};
struct nethost {
int best;
int worst;
int transit;
+ int saved[SAVED_PINGS];
};
static struct nethost host[MaxHost];
return (~sum & 0xffff);
}
+
+static int BSDfix = 0;
+
void net_send_ping(int index) {
char packet[sizeof(struct IPHeader) + sizeof(struct ICMPHeader)
+ sizeof(struct packetdata)];
addr.sin_addr.s_addr = host[index].addr;
host[index].xmit++;
host[index].transit = 1;
+ net_save_xmit(index);
memset(packet, 0, packetsize);
ip->version = 0x45;
ip->tos = 0;
- ip->len = htons (packetsize);
+ ip->len = BSDfix? packetsize: htons (packetsize);
ip->id = 0;
ip->frag = 0;
ip->ttl = 127;
ip->saddr = 0;
ip->daddr = host[index].addr;
- icmp->type = ICMP_ECHO;
+ icmp->type = timestamp?ICMP_TSTAMP:ICMP_ECHO;
icmp->id = getpid();
icmp->sequence = 0;
data->ttl = 0;
data->index = index;
+ data->seq = host[index].xmit;
gettimeofday(&now, NULL);
data->sec = now.tv_sec;
struct ICMPHeader *icmp;
struct packetdata *data;
int packetsize = sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + sizeof(struct packetdata);
+ int rv;
+ static int first=1;
memset(packet, 0, packetsize);
ip->version = 0x45;
ip->tos = 0;
- ip->len = htons (packetsize);
+ ip->len = BSDfix ? packetsize: htons (packetsize);
ip->id = 0;
ip->frag = 0;
ip->ttl = hops;
icmp->checksum = checksum(icmp, packetsize - sizeof(struct IPHeader));
ip->check = checksum(ip, packetsize);
- sendto(sendsock, packet, packetsize, 0,
+
+ rv = sendto(sendsock, packet, packetsize, 0,
(struct sockaddr *)&remoteaddress, sizeof(remoteaddress));
+ if (first && (rv == EINVAL)) {
+ first = 0;
+ ip->len = packetsize;
+ rv = sendto(sendsock, packet, packetsize, 0,
+ (struct sockaddr *)&remoteaddress, sizeof(remoteaddress));
+ if (rv >= 0) {
+ fprintf (stderr, "You've got a broken (FreeBSD?) system\n");
+ BSDfix = 1;
+ }
+ }
}
void net_process_ping(struct packetdata *data, struct sockaddr_in *addr) {
|| (data->sec == reset.tv_sec && (1000*data->msec) < reset.tv_usec))
/* discard this data point, stats were reset after it was generated */
return;
+
+ if (net_duplicate(data->index, data->seq)) {
+ return;
+ }
totmsec = (now.tv_sec - data->sec) * 1000 +
((now.tv_usec/1000) - data->msec);
host[data->index].total += totmsec;
host[data->index].returned++;
host[data->index].transit = 0;
+ net_save_return(data->index, data->seq, totmsec);
} else {
at = data->ttl - 1;
if(at < 0 || at > MaxHost)
}
int net_percent(int at) {
- if(host[at].xmit == 0)
+ if((host[at].xmit - host[at].transit) == 0)
return 0;
- return 100 - (100 * (host[at].returned + host[at].transit) / host[at].xmit);
+ return 100 - (100 * host[at].returned / (host[at].xmit - host[at].transit));
}
int net_best(int at) {
int net_send_batch() {
- static int n_unknown = 10;
static int at;
+ int n_unknown, i;
if(host[at].addr == 0) {
net_send_query(at + 1);
- n_unknown--;
} else {
net_send_ping(at);
}
-
+
+ n_unknown = 0;
+
+ for (i=0;i<at;i++) {
+ if (host[i].addr == 0)
+ n_unknown++;
+ if (host[i].addr == remoteaddress.sin_addr.s_addr)
+ n_unknown = 100; /* Make sure we drop into "we should restart" */
+ }
+
if ((host[at].addr == remoteaddress.sin_addr.s_addr) ||
- (n_unknown == 0)) {
+ (n_unknown > 5)) {
DeltaTime = WaitTime / (float) (at+1);
at = 0;
- n_unknown = 10;
return 1;
}
}
int net_open(int addr) {
+ net_reset();
+
remoteaddress.sin_family = AF_INET;
remoteaddress.sin_addr.s_addr = addr;
void net_reset() {
int at;
+ int i;
for(at = 0; at < MaxHost; at++) {
host[at].xmit = host[at].transit;
host[at].total = 0;
host[at].best = 0;
host[at].worst = 0;
+ for (i=0; i<SAVED_PINGS; i++) {
+ host[at].saved[i] = -2; /* unsent */
+ }
}
gettimeofday(&reset, NULL);
}
return recvsock;
}
+int* net_saved_pings(int at) {
+ return host[at].saved;
+}
+
+void net_save_xmit(int at) {
+ int tmp[SAVED_PINGS];
+ memcpy(tmp, &host[at].saved[1], (SAVED_PINGS-1)*sizeof(int));
+ memcpy(host[at].saved, tmp, (SAVED_PINGS-1)*sizeof(int));
+ host[at].saved[SAVED_PINGS-1] = -1;
+}
+int net_duplicate(int at, int seq) {
+ int idx;
+ idx = SAVED_PINGS - (host[at].xmit - seq) - 1;
+ if (idx < 0) {
+ /* long in the past - assume received */
+ return 2;
+ }
+ if (idx >= SAVED_PINGS) {
+ /* ehhhh - back to the future? */
+ return 3;
+ }
+ if (host[at].saved[idx] == -2) {
+ /* say what? must be an old ping */
+ return 4;
+ }
+ if (host[at].saved[idx] != -1) {
+ /* it's a dup */
+ return 5;
+ }
+ return 0;
+}
+
+void net_save_return(int at, int seq, int ms) {
+ int idx;
+ idx = SAVED_PINGS - (host[at].xmit - seq) - 1;
+ if (idx < 0) {
+ return;
+ }
+ host[at].saved[idx] = ms;
+}
int net_xmit(int at);
int net_transit(int at);
+#define SAVED_PINGS 50
+int* net_saved_pings(int at);
+void net_save_xmit(int at);
+void net_save_return(int at, int seq, int ms);
+int net_duplicate(int at, int seq);
#define MaxHost 256
if(action == ActionReset)
net_reset();
+ if (action == ActionDisplay)
+ display_mode = (display_mode+1) % 3;
+
+ if (action == ActionClear)
+ display_clear();
+
anyset = 1;
}
#include "split.h"
*/
+#include <config.h>
+
+#if defined(HAVE_NCURSES_H)
+# include <ncurses.h>
+#elif defined(HAVE_NCURSES_CURSES_H)
+# include <ncurses/curses.h>
+#elif defined(HAVE_CURSES_H)
+# include <curses.h>
+#else
+# error No curses header file available
+#endif
+
+
extern char *Hostname;
extern int WaitTime;