mirror of
https://github.com/traviscross/mtr.git
synced 2024-09-21 18:37:10 +00:00
5d26cb0c05
The mtr-packet tool isolates the raw socket sending/receiving of packets from the mtr user interface. By isolating the socket interactions to a separate process, we can be sure that any security flaws in the user-interface code doesn't expose a raw socket interface to an attacker attempting to escalate privileges. This is a bare-bones implementation, only support ICMP, only support IP version 4, and missing many of the probe customization features available in mtr. It will require some more work to reach feature parity with the current mtr implementation. But it's a start. The include mtr-packet man page explains the protocol format used to communicate with this new process. Included is an automated test for mtr-packet, implemented using Python's unittest module. Though the code actually being tested is implemented in C, Python make it easy to write test cases. 'make check' will test the current build. An alternate code-path for Windows is included in the mtr-packet tool. The mechanism for sending and receiving network probes is significantly different for Windows, as compared to Unix-like operating systems, but the interface provided by mtr-packet is the same. 'make dist-windows-bin' will make a Windows binary distribution. A Cygwin build environment is required, but the resulting binary distribution doesn't require that Cygwin be already installed. Tested on: Ubuntu 16.10, FreeBSD 11.0, MacOS 10.12.1 (Sierra), Windows 7 Since the code changes are significant, more esoteric operating systems may require changes.
261 lines
6.1 KiB
C
261 lines
6.1 KiB
C
/*
|
|
mtr -- a network diagnostic tool
|
|
Copyright (C) 1997,1998 Matt Kimball
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License version 2 as
|
|
published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <sys/select.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <errno.h>
|
|
#ifdef HAVE_ERROR_H
|
|
# include <error.h>
|
|
#else
|
|
# include "portability/error.h"
|
|
#endif
|
|
|
|
#include "mtr.h"
|
|
#include "dns.h"
|
|
#include "net.h"
|
|
#include "asn.h"
|
|
#include "display.h"
|
|
#include "select.h"
|
|
|
|
extern void select_loop(struct mtr_ctl *ctl){
|
|
fd_set readfd;
|
|
fd_set writefd;
|
|
int anyset = 0;
|
|
int maxfd = 0;
|
|
int dnsfd, netfd;
|
|
#ifdef ENABLE_IPV6
|
|
int dnsfd6;
|
|
#endif
|
|
int NumPing = 0;
|
|
int paused = 0;
|
|
struct timeval lasttime, thistime, selecttime;
|
|
struct timeval startgrace;
|
|
int dt;
|
|
int rv;
|
|
int graceperiod = 0;
|
|
struct timeval intervaltime;
|
|
static double dnsinterval = 0;
|
|
|
|
memset(&startgrace, 0, sizeof(startgrace));
|
|
|
|
gettimeofday(&lasttime, NULL);
|
|
|
|
while(1) {
|
|
dt = calc_deltatime (ctl->WaitTime);
|
|
intervaltime.tv_sec = dt / 1000000;
|
|
intervaltime.tv_usec = dt % 1000000;
|
|
|
|
FD_ZERO(&readfd);
|
|
FD_ZERO(&writefd);
|
|
|
|
maxfd = 0;
|
|
|
|
if(ctl->Interactive) {
|
|
FD_SET(0, &readfd);
|
|
maxfd = 1;
|
|
}
|
|
|
|
#ifdef ENABLE_IPV6
|
|
if (ctl->dns) {
|
|
dnsfd6 = dns_waitfd6();
|
|
if (dnsfd6 >= 0) {
|
|
FD_SET(dnsfd6, &readfd);
|
|
if(dnsfd6 >= maxfd) maxfd = dnsfd6 + 1;
|
|
} else {
|
|
dnsfd6 = 0;
|
|
}
|
|
} else
|
|
dnsfd6 = 0;
|
|
#endif
|
|
if (ctl->dns) {
|
|
dnsfd = dns_waitfd();
|
|
FD_SET(dnsfd, &readfd);
|
|
if(dnsfd >= maxfd) maxfd = dnsfd + 1;
|
|
} else
|
|
dnsfd = 0;
|
|
|
|
netfd = net_waitfd();
|
|
FD_SET(netfd, &readfd);
|
|
if(netfd >= maxfd) maxfd = netfd + 1;
|
|
|
|
if (ctl->mtrtype == IPPROTO_TCP)
|
|
net_add_fds(&writefd, &maxfd);
|
|
|
|
do {
|
|
if(anyset || paused) {
|
|
/* Set timeout to 0.1s.
|
|
* While this is almost instantaneous for human operators,
|
|
* it's slow enough for computers to go do something else;
|
|
* this prevents mtr from hogging 100% CPU time on one core.
|
|
*/
|
|
selecttime.tv_sec = 0;
|
|
selecttime.tv_usec = paused?100000:0;
|
|
|
|
rv = select(maxfd, (void *)&readfd, &writefd, NULL, &selecttime);
|
|
|
|
} else {
|
|
if(ctl->Interactive) display_redraw(ctl);
|
|
|
|
gettimeofday(&thistime, NULL);
|
|
|
|
if(thistime.tv_sec > lasttime.tv_sec + intervaltime.tv_sec ||
|
|
(thistime.tv_sec == lasttime.tv_sec + intervaltime.tv_sec &&
|
|
thistime.tv_usec >= lasttime.tv_usec + intervaltime.tv_usec)) {
|
|
lasttime = thistime;
|
|
|
|
if (!graceperiod) {
|
|
if (NumPing >= ctl->MaxPing && (!ctl->Interactive || ctl->ForceMaxPing)) {
|
|
graceperiod = 1;
|
|
startgrace = thistime;
|
|
}
|
|
|
|
/* do not send out batch when we've already initiated grace period */
|
|
if (!graceperiod && net_send_batch(ctl))
|
|
NumPing++;
|
|
}
|
|
}
|
|
|
|
if (graceperiod) {
|
|
dt = (thistime.tv_usec - startgrace.tv_usec) +
|
|
1000000 * (thistime.tv_sec - startgrace.tv_sec);
|
|
if ((ctl->GraceTime * 1000 * 1000) < dt)
|
|
return;
|
|
}
|
|
|
|
selecttime.tv_usec = (thistime.tv_usec - lasttime.tv_usec);
|
|
selecttime.tv_sec = (thistime.tv_sec - lasttime.tv_sec);
|
|
if (selecttime.tv_usec < 0) {
|
|
--selecttime.tv_sec;
|
|
selecttime.tv_usec += 1000000;
|
|
}
|
|
selecttime.tv_usec = intervaltime.tv_usec - selecttime.tv_usec;
|
|
selecttime.tv_sec = intervaltime.tv_sec - selecttime.tv_sec;
|
|
if (selecttime.tv_usec < 0) {
|
|
--selecttime.tv_sec;
|
|
selecttime.tv_usec += 1000000;
|
|
}
|
|
|
|
if (ctl->dns) {
|
|
if ((selecttime.tv_sec > (time_t)dnsinterval) ||
|
|
((selecttime.tv_sec == (time_t)dnsinterval) &&
|
|
(selecttime.tv_usec > ((time_t)(dnsinterval * 1000000) % 1000000)))) {
|
|
selecttime.tv_sec = (time_t)dnsinterval;
|
|
selecttime.tv_usec = (time_t)(dnsinterval * 1000000) % 1000000;
|
|
}
|
|
}
|
|
|
|
rv = select(maxfd, (void *)&readfd, NULL, NULL, &selecttime);
|
|
}
|
|
} while ((rv < 0) && (errno == EINTR));
|
|
|
|
if (rv < 0) {
|
|
error(EXIT_FAILURE, errno, "Select failed");
|
|
}
|
|
anyset = 0;
|
|
|
|
/* Have we got new packets back? */
|
|
if(FD_ISSET(netfd, &readfd)) {
|
|
net_process_return(ctl);
|
|
anyset = 1;
|
|
}
|
|
|
|
if (ctl->dns) {
|
|
/* Handle any pending resolver events */
|
|
dnsinterval = ctl->WaitTime;
|
|
}
|
|
|
|
/* Have we finished a nameservice lookup? */
|
|
#ifdef ENABLE_IPV6
|
|
if(ctl->dns && dnsfd6 && FD_ISSET(dnsfd6, &readfd)) {
|
|
dns_ack6();
|
|
anyset = 1;
|
|
}
|
|
#endif
|
|
if(ctl->dns && dnsfd && FD_ISSET(dnsfd, &readfd)) {
|
|
dns_ack(ctl);
|
|
anyset = 1;
|
|
}
|
|
|
|
/* Has a key been pressed? */
|
|
if(FD_ISSET(0, &readfd)) {
|
|
switch (display_keyaction(ctl)) {
|
|
case ActionQuit:
|
|
return;
|
|
break;
|
|
case ActionReset:
|
|
net_reset(ctl);
|
|
break;
|
|
case ActionDisplay:
|
|
ctl->display_mode = (ctl->display_mode + 1) % DisplayModeMAX;
|
|
break;
|
|
case ActionClear:
|
|
display_clear(ctl);
|
|
break;
|
|
case ActionPause:
|
|
paused=1;
|
|
break;
|
|
case ActionResume:
|
|
paused=0;
|
|
break;
|
|
case ActionMPLS:
|
|
ctl->enablempls = !ctl->enablempls;
|
|
display_clear(ctl);
|
|
break;
|
|
case ActionDNS:
|
|
if (ctl->dns) {
|
|
ctl->use_dns = !ctl->use_dns;
|
|
display_clear(ctl);
|
|
}
|
|
break;
|
|
#ifdef HAVE_IPINFO
|
|
case ActionII:
|
|
ctl->ipinfo_no++;
|
|
if (ctl->ipinfo_no > ctl->ipinfo_max)
|
|
ctl->ipinfo_no = 0;
|
|
break;
|
|
case ActionAS:
|
|
ctl->ipinfo_no = ctl->ipinfo_no ? 0 : ctl->ipinfo_max;
|
|
break;
|
|
#endif
|
|
|
|
case ActionScrollDown:
|
|
ctl->display_offset += 5;
|
|
break;
|
|
case ActionScrollUp:
|
|
ctl->display_offset -= 5;
|
|
if (ctl->display_offset < 0) {
|
|
ctl->display_offset = 0;
|
|
}
|
|
break;
|
|
}
|
|
anyset = 1;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|