/*
* M P I N G . C V.2.0
*
* Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
* measure round-trip-delays and packet loss across network paths.
*
* - Median and percentile support added by Einar Fløystad Dørum, for Uninett A/S
*
* - Multihost support added by Vegard Engen, for Uninett A/S
*
* - IPv6 support added by:
* Frank Aune - Frank.Aune@uninett.no
* Stig Venaas - Stig.Venaas@uninett.no ,for Uninett A/S 2003.
*
* - Source revision, version 2.0 release manager and current maintainer:
* Frank Aune - Frank.Aune@uninett.no
*
*
* IPv4 support based partly on ping.c from Mike Muus, U. S. Army Ballistic Research Lab,
* with various modifications.
*
* IPv6 support based partly on ping6.c from the UNIX iputils package.
*
*
*
* Status -
* Production quality. Source is released under the "GPL license version 2"
* or later, at your discretion.
*
* Notes -
* This program has to be setuid or run as ROOT to access ICMP sockets.
*
* Security related issues -
* None currently known
* (Fixed) Possible DOS exploit discovered in icmp echo request in pr_pack4/6
*
* Bugs -
* None currently known
*
* TODO -
* More statistics could always be gathered.
*
*
*
*/
/* DEVELOPER TODO:
FIXED BUGS:
- FIXED: invoke mping with all flags and check behaviour!
- FIXED: IPv6 loopback pinging causes bad icmp type. possible checksum error?
- FIXED: ipv6 doesnt print out host-ip on statistics print
- FIXED: namefield variable length, makes it hard to read
- FIXED: Sends only 1 packet every second
- FIXED: sec/msec/usec handling for pr_pack and pr_pack6. Now using float triptime instead of int triptime
This makes mping produce _much_ more accurate statistics!
- FIXED: DNS lookup based on getaddrinfo, to replace the old implementation
- FIXED: both pingflags and options used in the code. That doesnt seem nice...
- FIXED: statistics print time in microsecs, while text says millisecs.
- FIXED: overflow in statistics sometime
- FIXED: Removed all compile warnings
- FIXED: Select "hangs" before printing finish when specifying packet count for ipv6 adresses
- FIXED: square-sum is zero for localhost
- FIXED: packet_time produce segfault after X pings. malloc(0) made mping segfault, since you basically kept record of every
packet stats for infinite pings, which theoretically led to infinite memory usage.
- FIXED: ping for localhost produce funny results, both 4 and 6
CURRENT BUGS:
- The routing option produce funny results, present since mping 1.0
CLEAN-UPS:
- Sjekk opp syntaks for interval/ualarm
- hops=0 : not implemented yet
- IMPORTANT: Implement checks for input arguments to avoide misuse
OTHER:
- LIMITATION: after 65.535 packets, the icmp_seq counter resets to zero and start counting again. (normal ping does the same btw)
- FEATURE, NOT BUG: Sometimes when ctrl-c is pressed, packet loss is reported due to select only finishing
its current ping.
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/signal.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <sys/time.h>
#include <sys/select.h>
#define MAXWAIT 10 /* max time to wait for response, sec. */
#define MAXPACKET (65536-60-8) /* max packet size */
/* Various options */
int options;
#define F_INTERVAL 0x001
#define F_VERBOSE 0x002 /* verbose flag */
#define F_QUIET 0x004 /* quiet flag */
#define F_PACKED 0x008 /* floodping flag */
#define F_SO_DONTROUTE 0x010 /* record route flag */
#define F_NOLOOKUP 0x020 /* don't look up ip-numbers */
#define F_PERCENTILE 0x040 /* use percentile and not min/avg/max */
#define F_MEDIAN 0x080 /* use median instead of avg */
#define F_NROUTES 0x100 /* number of record route slots */
#define F_IPV4 0x200 /* Prefer IPv4 adress on DNS resolve */
#define F_IPV6 0x400 /* Prefer IPv6 adress on DNS resolve */
#define F_AUTOSCALETIME 0x800 /* Automaticallly select sec/msec/usec */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
#define MAXHOSTS 500
#define MAXCOUNT 65535
static void finish(int dummy);
static void catcher(int dummy);
static void pinger(int hostnum);
static void pinger6(int hostnum);
static void pr_pack(char *buf, int cc, struct sockaddr_in *from);
static void pr_pack6(char *buf, int cc, struct sockaddr_in6 *from);
static void pr_icmph(struct icmp *icp);
static void pr_icmph6(struct icmp6_hdr *icp6);
static void pr_retip(struct ip *ip);
static void pr_iph(struct ip *ip);
static u_short in_cksum(const u_short *addr, int len);
static void tvsub(register struct timeval *out, register struct timeval *in);
u_char *packet;
int packlen;
int i;
extern int errno;
/* We choose to set the socket descriptors to (-1), and then later,
based on the input arguement(s), create the needed socket(s) */
int s = -1; /* Socket IPv4 file descriptor */
int s6 = -1; /* Socket IPv6 file descriptor */
int af = AF_UNSPEC; /* Adress family to be determined */
struct hostent *hp; /* Pointer to host info */
struct timezone tz; /* leftover */
struct sockaddr_storage whereto[MAXHOSTS]; /* Who to ping */
struct sockaddr_in *to;
struct sockaddr_in6 *to6;
struct addrinfo hints, *res;
int error;
int datalen; /* How much data */
/* Usage string printed at program call with no argument specified */
char usage[] =
"\n"
"Usage: mping [-rqvpmfn46a] [-c count] [-i interval] [-s packetsize] [-w waittime] [hosts...]\n"
"Packet options: \n"
" -r\t\t No routing\n"
" -q\t\t Quiet\n"
" -v\t\t Verbose\n"
" -p\t\t Show data in shorter style\n"
" -m\t\t Print median instead of max\n"
" -f\t\t Print 10/50/90 percentile instead of min/avg/max\n"
" -F hostlist\t Get list of hosts from file\n"
" -c count\t Send count packets\n"
" -i\t\t interval in ms between packets(default is 100ms)\n"
" -s packetsize\t Size of icmp packet(s)\n"
" -w waittime\t Time in secs to wait after last packet(2s)\n"
"\n"
"DNS options:\n"
" -n\t\t No reverse DNS lookups\n"
" -4\t\t Prefer IPv4 adress on multiple DNS hits\n"
" -6\t\t Prefer IPv6 adress on multiple DNS hits\n"
"\n"
"Timing options:\n"
" -a\t\t Automatically scale SI time units (sec/msec/usec)\n\n"
"\t\t Note: This has no effect if used together with -p,\n"
"\t\t as this will make mping default to using msec\n"
"\n"
"About mping:\n"
" -V\t\t Show version info\n"
"\n";
/* Display version info when invoked with the -V switch */
char version[] =
"\n"
"Mping v2.01 build 2004-03-12\n"
"Developed by Uninett (http://www.uninett.no) 1996-2004\n"
"All rights reserved\n"
"\n"
"See 'man mping' for more information\n"
"\n";
/* Definition of variables */
char hostname[MAXHOSTS][MAXHOSTNAMELEN];
char hnamebuf[MAXHOSTNAMELEN];
int nhosts = 0; /* number of hosts on command line */
int npackets; /* number of packets to send to each host. */
int wtime = 2; /* time (ms) to wait after last packet sent */
int preload = 0; /* number of packets to "preload" */
int ntransmitted=0; /* sequence # for outbound packets = #sent */
int interval = 100; /* interval between packets (msec) Default: 10 packets/sec */
int uid;
int ident;
long nreceived[MAXHOSTS]; /* # of packets we got back */
int received_any = 0; /* Flag */
int nactive = 0; /* Number of hosts that we're still pinging. */
int active[MAXHOSTS]; /* Array of flags */
int timing = 0;
int chnum = 0;
long tmin[MAXHOSTS];
long tmax[MAXHOSTS];
unsigned long tsum[MAXHOSTS]; /* sum of all times, for doing average */
double sqsum[MAXHOSTS]; /* square sum of all times, */
int *packet_time[MAXHOSTS];
long tmax_tot = 0;
int finishing = 0; /* bool finish alarm is set - false each time new pkt alarm is set */
static int read_nodefile (char *nodefile, char **nodebuf);
char *inet_ntoa();
char *pr_addr();
int compare(const void*, const void*);
int calculate_n_percentile(int *arr, int arr_len, int n);
int numeric = 0; /* -n flag: numeric addresses only */
char rspace[3 + 4 * F_NROUTES + 1]; /* record route space */
int alloc_count; /* Number of packets we allocate space for
* for recording statistics */
/*
* M A I N
*/
int main(int argc, char **argv)
{
struct sockaddr_storage from;
char **av = argv;
extern char *optarg;
char *nodefile;
int nodecount = 0;
static char *nodebuf[MAXHOSTS];
int on = 1;
int test = 0;
struct protoent *proto;
int i, result, sqsum_offset, sz_opt,ch;
argc--, av++;
datalen = 64 - 8;
/* Check if program is run as root or with setuid permissions */
if (geteuid()) {
fprintf(stderr, "To access ICMP sockets, this program must be run as root or setuid root.\n");
exit(3);
}
/* Determine which options are set at command line, and set neccesary flags */
while (argc > 0 && *av[0] == '-') {
while (*++av[0])
/*
while ((ch = getopt(argc, argv, "I:LRc:dfh:i:l:np:qrs:t:vF:6:4:w:UVM:")) != EOF) {
switch(ch) {
*/
switch (*av[0]) {
case 'i':
if (!(*++av[0])) {
av++;
if (--argc == 0) {
break;
}
}
interval = atoi(av[0]);
if (interval <= 0) {
fprintf(stderr, "Mping: bad timing interval.\n");
exit(2);
}
options |= F_INTERVAL;
break;
case 'r':
options |= F_SO_DONTROUTE;
break;
case 'v':
options |= F_VERBOSE;
break;
case 'q':
options |= F_QUIET;
break;
case 'p':
options |= F_PACKED;
break;
case 'n':
options |= F_NOLOOKUP;
break;
case 'm':
options |= F_MEDIAN;
break;
case 'f':
options |= F_PERCENTILE;
break;
case 'c':
if (!(*++av[0])) {
av++;
if (--argc == 0) {
break;
}
}
npackets = atoi(av[0]);
break;
case 'w':
if (!(*++av[0])) {
av++;
if (--argc == 0) {
break;
}
}
wtime = atoi(av[0]);
break;
case 's':
if (!(*++av[0])) {
av++;
if (--argc == 0) {
break;
}
}
datalen = atoi(av[0]);
if (datalen > MAXPACKET) {
fprintf(stderr, "Mping: Packet size too large. Check arguements. \n");
exit(1);
}
if (datalen < sizeof(int)) {
fprintf(stderr, "mping: need at least %d data bytes\n",
sizeof(int));
exit(1);
}
break;
case 'V':
printf(version);
exit(1);
break;
case '4':
af = AF_INET;
break;
case '6':
af = AF_INET6;
break;
case 'a':
options |= F_AUTOSCALETIME;
break;
case 'F':
if (!(*++av[0])) {
av++;
if (--argc == 0) {
break;
}
}
nodefile = av[0];
nodecount = read_nodefile(nodefile, nodebuf);
break;
}
argc--, av++;
}
if (datalen >= (sizeof(struct timeval) + sizeof(int))) {
timing = 1;
}
/* Check memory allocation */
packlen = datalen + 60 + 76;
if (!(packet = (u_char *)malloc((u_int)packlen))) {
fprintf(stderr, "Mping: Memory allocation failed. Check arguements\n");
exit(2);
}
/* Display Usage for program if no arguments are given */
if (argc < 1 && nodecount == 0) {
printf(usage);
exit(1);
}
/* ident holds process id */
ident = getpid() & 0xFFFF;
/*
* We check each arguement at the command line, and test if they are an IPv4 or IPv6
* adress. And here's the "magic": We only create the appropriate sockets according
* to these arguements. This solution solves a number of problems and even decrease
* memory usage! How nice...
*
* Note however, that if mping is invoked with an IPv4 or IPv6 address, and is unable
* to create the the corresponding raw socket - we make it fail on purpose, since
* mping then will be unable to do what its told.
*
*/
if (nodecount > 0) {
av = nodebuf;
argc = nodecount;
}
i = 0;
while ((i < argc) && (nhosts < (MAXHOSTS - 1))) {
char hostid[255];
if (strncmp(av[i], "#", 1) == 0 || ! sscanf(av[i], "%254s", hostid)) {
i++;
continue;
}
active[nhosts] = 1;
tmin[nhosts] = 999999999;
tmax[nhosts] = 0;
tsum[nhosts] = 0;
sqsum[nhosts] = 0.0;
alloc_count = (npackets > 0) ? npackets : MAXCOUNT;
//packet_time[nhosts] = (int*)malloc(npackets*sizeof(int));
if (!(packet_time[nhosts] = (int*)malloc(alloc_count*sizeof(int)))) {
fprintf(stderr, "Mping: Memory allocation failed\n");
exit(1);
}
/* Remember to free up this memory afterwards */
//memset(packet_time[nhosts],-1,npackets*sizeof(int));
memset(packet_time[nhosts],-1,alloc_count*sizeof(int));
bzero((char *) &whereto[nhosts], sizeof(struct sockaddr_storage));
//#if 0
to = (struct sockaddr_in *) &whereto[nhosts];
to6 = (struct sockaddr_in6 *) &whereto[nhosts];
//#endif
/* Remember to free up this memory afterwards */
memset(&hints, 0, sizeof(hints));
hints.ai_family = af;
hints.ai_socktype = SOCK_RAW;
/*hints.ai_protocol = IPPROTO_???;*/
error = getaddrinfo(hostid, NULL, &hints, &res);
if (error) {
fprintf(stderr, "host %s : %s\n", hostid, gai_strerror(error));
active[nhosts] = 0;
i++;
continue;
}
if (res->ai_family == AF_INET6) {
/*
Redesign the tests, since they seem to make eachother redundant.
Also; how do I get socket(res->ai_family, res->ai_socktype, res->ai_protocol) to work?
*/
if ((proto = getprotobyname("ipv6-icmp")) == NULL) {
fprintf(stderr, "IPv6-ICMP: Protocol not supported by kernel.\n");
exit(1);
}
if (s6 < 0) {
s6 = socket(res->ai_family, res->ai_socktype, IPPROTO_ICMPV6 /*res->ai_protocol*/);
if (s6 < 0) {
perror("Mping: IPv6 adress specified (%s), but raw IPv6 socket not supported by kernel");
exit(1);
}
if (options & F_SO_DONTROUTE) {
setsockopt(s6, SOL_SOCKET, SO_DONTROUTE, &on, sizeof(on));
}
}
}
else if (res->ai_family == AF_INET) {
if ((proto = getprotobyname("icmp")) == NULL) {
fprintf(stderr, "IPv4-ICMP: Protocol not supported by kernel.\n");
exit(1);
}
if (s < 0) {
s = socket(res->ai_family, res->ai_socktype, IPPROTO_ICMP /*res->ai_protocol*/);
if (s < 0) {
perror("Mping: IPv4 adress specified, but raw IPv4 socket not supported by kernel.\n");
exit(1);
}
}
if (options & F_SO_DONTROUTE) {
setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &on, sizeof(on));
}
}
else {
printf("FATAL");
}
memcpy(&whereto[nhosts], res->ai_addr, res->ai_addrlen);
/* Copy hostname for further use */
strcpy(hnamebuf, hostid);
strcpy(hostname[nhosts], hnamebuf);
if (!(options & F_PACKED))
printf("PING %-44s: %d data bytes\n", hostname[nhosts], datalen);
#if 0
if (options & F_PACKED) {
printf(hostid);
//prinft(hnamebuf);
//prinft(hostname[nhosts]);
}
#endif
if (options & F_NOLOOKUP) {
strcpy(hnamebuf, hostid);
strcpy(hostname[nhosts], hnamebuf);
}
if (active[nhosts]) {
nhosts++;
nactive++;
}
i++;
if (nhosts >= MAXHOSTS) {
fprintf(stderr, "Max hosts reached. Skipping rest.\n");
}
}
if (!options & F_PACKED)
putchar('\n');
/* setlinebuf( stdout ); */
signal(SIGINT, finish);
signal(SIGALRM, catcher);
if (nhosts > 0)
catcher(-1 /* dummy */);
else
exit(-1);
/* ------------------------------------------------------ */
/* SELECT TEST
*
*
Konsept for select() implementasjon i mping
Problemet enkelt forklart:
To sockets blir opprettet:
s - for IPv4
s6 - for IPv6
En send() request på socket s gjør at receive() blir stående og vente på svar, og derfor
blokkere for send/receive på socket s6. For å kunne bruke socket s og s6 "samtidig" må
derfor I/O Multipleksing implementeres i form av select().
Returns:
positiv count for ready descriptor
0 for timeout
-1 on error
int maxfdp1 : Number of max descriptors to be tested + 1
(...)
Eksempelkode fra Stig:
int s4, s6, smax;
fd_set readfds;
smax = s6 > s4 ? s6 : s4;
for (;;) {
FD_ZERO(&readfds);
FD_SET(s4, &readfds);
FD_SET(s6, &readfds);
ndesc = select(smax + 1, &readfds, (fd_set *)0, (fd_set *)0, NULL);
if (FD_ISSET(s4, &readfds)) {
...
cnt = recvfrom(s4, buf, count, 0, (struct sockaddr *)&from,
&len);
,,,
}
if (FD_ISSET(s6, &readfds)) {
...
cnt = recvmsg(s, &msgh, MSG_WAITALL);
...
}
**------------------------------------------------------------ */
for (;;) {
int fromlen = sizeof(from);
int cc;
int smax;
struct timeval timeout;
fd_set read_fdset;
static const time_t SELECT_TIMEOUT_SECS = 0;
static const time_t SELECT_TIMEOUT_USECS = 10000;
timeout.tv_sec = SELECT_TIMEOUT_SECS;
timeout.tv_usec = SELECT_TIMEOUT_USECS;
/* Initialize the select loop */
smax = s6 > s ? s6 : s;
if (smax >= FD_SETSIZE) {
perror("mping: smax > FD_SETSIZE");
exit(1);
}
FD_ZERO(&read_fdset);
if (s >= 0)
FD_SET(s, &read_fdset);
if (s6 >= 0)
FD_SET(s6, &read_fdset);
/* int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, count struct timeval *timeout); {} */
/* select_descriptor returns:
* - positive count for ready descriptor'
- 0 for timeout
- (-1) for error
*/
if (select(smax + 1, &read_fdset, (fd_set *)0, (fd_set *)0, NULL) < 0) {
if (errno != EINTR) {
perror("mping: select error");
exit(1);
}
continue;
}
if (s >= 0) {
if (FD_ISSET(s, &read_fdset)) {
if ((cc = recvfrom(s, (char *)packet, packlen, 0,
(struct sockaddr *)&from, &fromlen)) < 0) {
if (errno != EINTR) {
perror("mping: recvfrom");
(void)fflush(stderr);
}
continue;
}
if (cc > 0) {
pr_pack(packet, cc, (struct sockaddr_in *)&from);
}
}
}
if (s6 >= 0) {
if (FD_ISSET(s6, &read_fdset)) {
if ((cc = recvfrom(s6, (char *)packet, packlen, 0,
(struct sockaddr *)&from, &fromlen)) < 0) {
if (errno != EINTR) {
perror("mping: recvfrom");
(void)fflush(stderr);
}
continue;
}
if (cc > 0) {
pr_pack6(packet, cc, (struct sockaddr_in6 *)&from);
}
}
}
/* Lets finish up and print out statistics */
/* pinger should set finish-alarm, but if lost this is a safequard */
if ( npackets && !finishing && ( nactive <= 0 || ntransmitted > npackets)){
signal(SIGALRM, finish);
alarm(wtime);
finishing=1;
}
}
/*WE REALLY SHOULDNT BE HERE :-| */
return 0; /* Dummy return */
}
static int
read_nodefile (char *nodefile, char **nodebuf)
{
char linebuf[256];
int nodecount = 0;
FILE *fh = fopen(nodefile, "r");
while(fgets(linebuf,sizeof linebuf, fh) && nodecount < MAXHOSTS) {
int llen = strlen(linebuf);
if (linebuf[llen - 1] == '\n')
linebuf[llen - 1] = '\0';
nodebuf[nodecount] = strdup(linebuf);
nodecount++;
}
if (!feof(fh))
nodecount = 0;
fclose(fh);
return nodecount;
}
/*
* C A T C H E R
*
* This routine causes another PING to be transmitted, and then
* schedules another SIGALRM for 'interval' seconds from now.
*
* Bug -
* Our sense of time will slowly skew (ie, packets will not be launched
* exactly at 'interval' intervals). This does not affect the quality
* of the delay and loss statistics.
*/
static void
catcher(int dummy)
{
int waittime;
int err;
if (chnum == 0) {
ntransmitted++;
}
/* Call pinger or pinger6 depending on what sa_family whereto[chnum] belongs to */
if (((struct sockaddr *) &whereto[chnum]) -> sa_family == AF_INET) {
pinger(chnum++);
}
else if (((struct sockaddr *) &whereto[chnum]) -> sa_family == AF_INET6) {
pinger6(chnum++);
}
else {
/* We should never reach this state */
fprintf(stderr, "!!Adress or hostname is neither IPv4 or IPv6!!\n");
exit(5);
}
if (chnum >= nhosts) {
chnum = 0;
}
if ((npackets == 0) || ((ntransmitted < npackets) || (chnum != 0))) {
#ifdef NO_UALARM
alarm($waittime);
signal(SIGALRM, catcher);
#else
signal(SIGALRM, catcher);
// ualarm(wtime * interval, 0);
ualarm(interval * 1000, 0);
finishing=0;
#endif
} else {
/* ok trenger bare vente waittime etter siste pakke er sendt
if (received_any) {
waittime = 2 * tmax_tot / 1000;
if (waittime == 0)
waittime = 1;
} else
waittime = MAXWAIT;
*/
waittime=wtime;
signal(SIGALRM, finish);
if ( alarm(waittime) < 0 ){
perror("mping: alarm error");
exit(1);
}
}
}
/*
* P I N G E R
*
* Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
* will be added on by the kernel. The ID field is our UNIX process ID,
* and the sequence number is an ascending integer. The first 8 bytes
* of the data portion are used to hold a UNIX "timeval" struct in VAX
* byte-order, to compute the round-trip time.
*
* The pinger entitry consists of pinger and pinger6, for IPv4 and IPv6.
*
*/
static void
pinger(int hostnum)
{
static u_char outpack[MAXPACKET];
register struct icmp *icp = (struct icmp *) outpack;
register int cc;
int i;
register struct timeval *tp = (struct timeval *) &outpack[8 + sizeof(int)];
register int *hnum = (int *) &outpack[8];
register u_char *datap = &outpack[8 + sizeof(int) + sizeof(struct timeval)];
icp->icmp_type = ICMP_ECHO;
icp->icmp_code = 0;
icp->icmp_cksum = 0;
icp->icmp_seq = ntransmitted;
icp->icmp_id = ident; /* ID */
cc = datalen + 8; /* skips ICMP portion */
for (i = 8; i < datalen; i++) {/* skip 8 for time */
*datap++ = i;
}
/* cc = sendto(s, msg, len, flags, to, tolen) */
if ((npackets == 0) || (ntransmitted <= npackets)) {
*hnum = hostnum;
if (timing)
gettimeofday(tp, &tz);
/* Compute ICMP checksum here */
icp->icmp_cksum = 0;
icp->icmp_cksum = in_cksum((u_short *)icp, cc); /* */
i = sendto(s, (char *)outpack, cc, 0,
(struct sockaddr *) &whereto[hostnum],
sizeof(struct sockaddr_in));
if (i < 0 || i != cc) {
if (i < 0)
perror("sendto");
printf("mping: wrote %s %d chars, ret=%d\n",
hostname[hostnum], cc, i);
fflush(stdout);
}
}
}
/*
* P I N G E R 6
*
* Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
* will be added on by the kernel. The ID field is our UNIX process ID,
* and the sequence number is an ascending integer. The first 8 bytes
* of the data portion are used to hold a UNIX "timeval" struct in VAX
* byte-order, to compute the round-trip time.
*/
static void
pinger6(int hostnum)
{
static u_char outpack[MAXPACKET];
struct icmp6_hdr *icmph;
register int cc;
int i;
icmph = (struct icmp6_hdr *)outpack;
icmph->icmp6_type = ICMP6_ECHO_REQUEST;
icmph->icmp6_code = 0;
icmph->icmp6_cksum = 0;
icmph->icmp6_seq = ntransmitted;
icmph->icmp6_id = ident;
icmph->icmp6_data32[1] = hostnum;
/* CLR(icmph->icmp6_sequence % mx_dup_ck); */
if (timing) {
gettimeofday((struct timeval *)&icmph->icmp6_data32[1],
(struct timezone *)NULL);
icmph->icmp6_data32[3] = hostnum;
}
else {
icmph->icmp6_data32[1] = hostnum;
}
/* Should also fill in data, and we need to skip hnum value */
cc = datalen + 8 + 4; /* skips ICMP portion */
i = sendto(s6, (char *)outpack, cc, 0 /*confirm*/,
(struct sockaddr *) &whereto[hostnum],
sizeof(struct sockaddr_in6));
if (i < 0 || i != cc) {
if (i < 0) {
if (errno == EAGAIN) {
ntransmitted--;
} else {
perror("ping: sendto");
}
} else {
printf("mping: wrote %s %d chars, ret=%d\n",
hostname[hostnum], cc, i);
}
}
}
/*
* P R _ P A C K
*
* Print out the IPv4 packet, if it came from us. This logic is necessary
* because ALL readers of the ICMP socket get a copy of ALL ICMP packets
* which arrive ('tis only fair). This permits multiple copies of this
* program to be run without having intermingled output (or statistics!).
*
* pr_pack for IPv4 and pr_pack6 for IPv6
*
*/
static void
pr_pack(char *buf, int cc, struct sockaddr_in *from)
{
struct ip *ip;
register struct icmp *icp;
register int i;
struct timeval tv;
struct timeval *tp;
int *hnum;
int hlen;
long triptime = 0;
/*
from->sin_addr.s_addr = ntohl(from->sin_addr.s_addr);
*/
gettimeofday(&tv, &tz);
/* Check the IP header */
ip = (struct ip *) buf;
hlen = ip->ip_hl << 2;
if (cc < hlen + ICMP_MINLEN) {
/* if( options & VERBOSE )
printf("packet too short (%d bytes) from %s\n", cc,
inet_ntoa(ntohl(from->sin_addr)));
*/
return;
}
/* Now the ICMP part */
/* Possible ICMP echo DOS exploit !!!!!!!!!!!!!!!
godtar echo reply fra hvilken som helst adresse (pga ruter)
Fikset nå.
*/
cc -= hlen;
icp = (struct icmp *)(buf + hlen);
if (icp->icmp_type == ICMP_ECHOREPLY) {
if (icp->icmp_id != ident) {
if (options & F_VERBOSE)
fprintf(stderr, "Mping: received another hosts ICMP_ECHO_REPLY - dropping packet.\n");
return; /* This was not our ICMP ECHO reply */
}
hnum = (int *) &buf[hlen + 8];
if (*hnum >= nhosts) {
printf("hnum=%d, larger than nhosts=%d!\n", *hnum,
nhosts);
return;
}
if ((++nreceived[*hnum] >= npackets) && npackets) {
nactive--;
active[*hnum] = 0;
}
if (timing) {
/* These ifndef's needs to be changed */
#ifndef icmp_data
tp = (struct timeval *) &icp->icmp_ip;
#else
tp = (struct timeval *) &icp->icmp_data[sizeof(int)];
#endif
tvsub(&tv, tp);
triptime = tv.tv_sec * 1000000 + tv.tv_usec;
if (triptime < 0) {
fprintf(stderr, "Warning: time of day goes back, taking countermeasures.\n");
triptime = 0;
}
tsum[*hnum] += triptime;
sqsum[*hnum] += (double)triptime * (double)triptime;
if (nreceived[*hnum] <= alloc_count)
packet_time[*hnum][nreceived[*hnum]-1] = triptime;
if (triptime < tmin[*hnum])
tmin[*hnum] = triptime;
if (triptime > tmax[*hnum])
tmax[*hnum] = triptime;
}
if (triptime > tmax_tot)
tmax_tot = triptime;
if (options & F_QUIET)
return;
if (!(options & F_PACKED))
printf("%d bytes from %-35s: icmp_seq=%d", cc,
hostname[*hnum],
icp->icmp_seq); /* */
else
printf("%s %d %d", hostname[*hnum], cc, icp->icmp_seq);
if (timing) {
if ((options & F_AUTOSCALETIME) && !(options & F_PACKED)) {
if (triptime >= 1000000) {
if (!(options & F_PACKED))
printf(" time=%ld.%03ld sec\n", triptime/1000000,
(triptime%1000000)/1000);
else
printf(" %ld.%03ld sec\n", triptime/1000000,
(triptime%1000000)/1000);
} else if (triptime >= 1000) {
if (!(options & F_PACKED))
printf(" time=%ld.%03ld msec\n", triptime/1000,
triptime%1000);
else
printf(" %ld.%03ld msec\n", triptime/1000,
triptime%1000);
} else {
if (!(options & F_PACKED))
printf(" time=%ld usec\n", triptime);
else
printf(" %ld usec\n", triptime);
}
}
else {
if (!(options & F_PACKED))
printf(" time=%ld.%03ld msec\n", triptime/1000, triptime%1000);
else
printf(" %ld.%03ld\n", triptime/1000, triptime%1000);
}
}
else
putchar('\n');
} else {
/* We've got something other than an ECHOREPLY */
if (!(options & F_PACKED) || !(options & F_QUIET)) {
if (icp->icmp_type != ICMP_ECHO) { /* Filter out localhost */
printf("%d bytes from %-35s: ", cc, pr_addr(from));
pr_icmph(icp);
}
}
}
/* Display any IP options */
/* XXX - we should eventually do this for all packets with options */
if (hlen > 20 && icp->icmp_type == ICMP_ECHOREPLY) {
unsigned char *cp;
/*printf("%d byte IP header:\n", hlen); */
cp = (unsigned char *) buf + sizeof(struct ip) + 3;
for (i = 0; i < F_NROUTES; i++) {
unsigned long l;
l = (*cp << 24) | (*(cp + 1) << 16) | (*(cp + 2) << 8) | *(cp + 3);
/* give the nameserver a break! */
if (l == 0)
printf("0.0.0.0\n");
else
printf("%s\n", pr_addr(ntohl(l)));
cp += 4;
}
}
}
/*
* P R _ P A C K 6
*
* Print out the packet, if it came from us. This logic is necessary
* because ALL readers of the ICMP socket get a copy of ALL ICMP packets
* which arrive (this is only fair). This permits multiple copies of this
* program to be run without having intermingled output (or statistics!).
*
*
*/
static void
pr_pack6(char *buf, int cc, struct sockaddr_in6 *from /*,int hops, struct timeval *tv */)
{
struct icmp6_hdr *icmph;
u_char *cp,*dp;
struct timeval tv;
struct timeval *tp;
long triptime = 0;
int dupflag = 0;
int hnum;
int hops = 0;
icmph = (struct icmp6_hdr *) buf;
if (icmph->icmp6_type == ICMP6_ECHO_REPLY) {
if (icmph->icmp6_id != ident) {
if (options & F_VERBOSE)
fprintf(stderr, "Mping: received another hosts ICMP_ECHO_REPLY - dropping packet.\n");
return; /* This was not our ICMP ECHO reply */
}
if (cc < 8+4 || timing && cc < (8 + sizeof(struct timeval) + 4) ) {
if (options & F_VERBOSE)
fprintf(stderr, "mping: packet too short (%d bytes)\n", cc);
return;
}
hnum = icmph->icmp6_data32[timing ? 3 :1];
if (hnum < 0 || hnum > nhosts) {
printf("hnum=%d, outside [0..%d]\n", hnum, nhosts);
return;
}
/* TODO: Check that the address (whereto[hnum] == source of packet) */
/* Changed to prevent ipv6 "hang" on count */
if ((++nreceived[hnum] >= npackets) && npackets) {
nactive--;
active[hnum] = 0;
}
if (timing && cc >= 8+sizeof(struct timeval)) {
gettimeofday(&tv, NULL);
tp = (struct timeval *)(icmph + 1);
restamp:
tvsub(&tv, tp);
triptime = tv.tv_sec * 1000000 + tv.tv_usec;
if (triptime < 0) {
fprintf(stderr, "Warning: time of day goes back, taking countermeasures.\n");
triptime = 0;
/* F_LATENCY NOT ADDED TO OPTIONS YET
if (!(options & F_LATENCY)) {
gettimeofday(&tv, NULL);
options |= F_LATENCY;
goto restamp;
}
*/
}
tsum[hnum] += triptime;
sqsum[hnum] += (double)triptime * (double)triptime;
if (nreceived[hnum] <= alloc_count)
packet_time[hnum][nreceived[hnum]-1] = triptime;
if (triptime < tmin[hnum])
tmin[hnum] = triptime;
if (triptime > tmax[hnum])
tmax[hnum] = triptime;
}
if (triptime > tmax_tot)
tmax_tot = triptime;
if (options & F_QUIET)
return;
if (!(options & F_PACKED)) {
printf("%d bytes from %-35s: icmp_seq=%u", cc, pr_addr(from), icmph->icmp6_seq);
if (hops > 0)
printf(" hops=%d", hops);
if (cc < datalen+8) {
printf(" (truncated)");
return;
}
}
else {
printf("%s %d %u", pr_addr(from), cc, icmph->icmp6_seq);
}
if (timing) {
if ((options & F_AUTOSCALETIME) && !(options & F_PACKED)) {
if (triptime >= 1000000) {
if (!(options & F_PACKED))
printf(" time=%ld.%03ld sec\n", triptime/1000000,
(triptime%1000000)/1000);
else
printf(" %ld.%03ld sec\n", triptime/1000000,
(triptime%1000000)/1000);
} else if (triptime >= 1000) {
if (!(options & F_PACKED))
printf(" time=%ld.%03ld msec\n", triptime/1000,
triptime%1000);
else
printf(" %ld.%03ld msec\n", triptime/1000,
triptime%1000);
} else {
if (!(options & F_PACKED))
printf(" time=%ld usec\n", triptime);
else
printf(" %ld usec\n", triptime);
}
}
else {
if (!(options & F_PACKED))
printf(" time=%ld.%03ld msec\n", triptime/1000, triptime%1000);
else
printf(" %ld.%03ld\n", triptime/1000, triptime%1000);
}
}
else
putchar('\n');
}else {
/* We got something other than an ICMP6_ECHO_REPLY */
if (!(options & F_PACKED) || !(options & F_QUIET)) {
if (icmph->icmp6_type != ICMP6_ECHO_REQUEST) { /* Filter out localhost */
printf("%d bytes from %-35s: ", cc, pr_addr(from));
pr_icmph6(icmph);
}
}
}
}
/*
* I N _ C K S U M
*
* Checksum routine for Internet Protocol V4 family headers (C Version)
*
*/
static u_short
in_cksum(const u_short *addr, register int len)
{
register int nleft = len;
const u_short *w = addr;
register int sum = 0;
register u_short answer = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold
* back all the carry bits from the top 16 bits into the lower
* 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1) {
sum += htons(*(u_char *)w << 8);
}
/*
* add back carry outs from top 16 bits to low 16 bits
*/
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}
/*
* I N _ C K S U M 6
*
* Checksum routine for Internet Protocol V6 family headers (C Version)
*
* NOTE: This check is obsolete, due to the kernel managing the ipv6 checksum
* but the code-structure is kept, because it might be handy sometime :-)
*/
#if 0
static int
in_cksum6(void *pkg, int len)
{
int tmplen = len + 36;
uint16_t shortlen = htons ((uint16_t) len);
/* TODO: Reuse old buffer if big enough */
char *tmppkg = malloc (tmplen);
int ret;
/* Fill in pseudo header, starting at tmppkg */
/*
memcpy (tmppkg + 0, source address, 16);
memcpy (tmppkg + 0, dest address, 16);
*/
/* len in network byte order at offset 32 */
memcpy (tmppkg + 32, &shortlen, 2);
/* Pad */
tmppkg[34] = '\0';
/* "Next header" 58 */
tmppkg[35] = 58;
/* Origianl package at offset 36 */
memcpy (tmppkg + 36, pkg, len);
ret = in_cksum (tmppkg, tmplen);
free (tmppkg);
return ret;
}
#endif
/*
* T V S U B
*
* Subtract 2 timeval structs: out = out - in.
*
* Out is assumed to be >= in.
*/
static void
tvsub(out, in)
register struct timeval *out, *in;
{
if ((out->tv_usec -= in->tv_usec) < 0) {
out->tv_sec--;
out->tv_usec += 1000000;
}
out->tv_sec -= in->tv_sec;
}
/*
* F I N I S H
*
* Print out statistics, and give up.
* Heavily buffered STDIO is used here, so that all the statistics
* will be written with 1 sys-write call. This is nice when more
* than one copy of the program is running on a terminal; it prevents
* the statistics output from becomming intermingled.
*/
static void
finish(int dummy)
{
int i;
fflush(stdout);
if (!(options & F_PACKED)){
printf("\n\n---- MPING Statistics----\n");
}
if (ntransmitted == 1)
if (!(options & F_PACKED))
printf("%ld packet transmitted to host\n\n", ntransmitted);
else
printf("%ld packet transmitted to host\n", ntransmitted);
else
if (!(options & F_PACKED))
printf("%ld packets transmitted to each host\n\n", ntransmitted);
else
printf("%ld packets transmitted to each host\n", ntransmitted);
for (i = 0; i < nhosts; i++) {
int nrecorded = MIN (nreceived[i], MAXCOUNT);
if((options & F_MEDIAN) || (options & F_PERCENTILE)){
qsort(packet_time[i], nrecorded, sizeof(int), compare);
}
if (!(options & F_PACKED)) {
if (nreceived[i] == 1)
printf("%s: %ld packet received, ", hostname[i], nreceived[i]);
else
printf("%s: %ld packets received, ", hostname[i], nreceived[i]);
}else{
printf("%s %ld ", hostname[i], nreceived[i]);
}
if (ntransmitted){
if (nreceived[i] > ntransmitted){
printf("-- somebody's printing up packets!");
}else{
if (!(options & F_PACKED)) {
printf("%d%% packet loss",
(int) (((ntransmitted - nreceived[i]) * 100) / ntransmitted));
}
}
}
if (!(options & F_PACKED)){
printf("\n");
}
if (nreceived[i] && timing && !((options & F_MEDIAN) || (options & F_PERCENTILE))) {
if (!(options & F_PACKED)) {
if (options & F_AUTOSCALETIME) {
if ((tsum[i] / nreceived[i]) >= 1000000) {
printf("round-trip (sec) min/avg/max/square-sum = %ld.%03ld/%lu.%03ld/%ld.%03ld/%.3f\n",
tmin[i] / 1000000, tmin[i]%1000,
tsum[i] / (nreceived[i] * 1000000), (tsum[i] / nreceived[i])%1000,
tmax[i] / 1000000, tmax[i]%1000,
sqsum[i] / 1.0e12);
}
else if ((tsum[i] / nreceived[i]) >= 1000) {
printf("round-trip (msec) min/avg/max/square-sum = %ld.%03ld/%lu.%03ld/%ld.%03ld/%.3f\n",
tmin[i] / 1000, tmin[i]%1000,
tsum[i] / (nreceived[i] * 1000), (tsum[i] / nreceived[i])%1000,
tmax[i] / 1000, tmax[i]%1000,
sqsum[i] / 1.0e6);
}
else {
printf("round-trip (usec) min/avg/max/square-sum = %ld/%lu/%ld/%.3f\n",
tmin[i],
tsum[i] / nreceived[i],
tmax[i],
sqsum[i]);
}
}
else {
printf("round-trip (msec) min/avg/max/square-sum = %ld.%03ld/%lu.%03ld/%ld.%03ld/%.3f\n",
tmin[i] / 1000, tmin[i]%1000,
tsum[i] / (nreceived[i] * 1000), (tsum[i] / nreceived[i])%1000,
tmax[i] / 1000, tmax[i]%1000,
sqsum[i] / 1.0e6);
}
}
else {
printf("%ld.%03ld/%lu.%03ld/%ld.%03ld/%.3f\n",
tmin[i] / 1000, tmin[i]%1000,
tsum[i] / (nreceived[i] * 1000), (tsum[i] / nreceived[i])%1000,
tmax[i] / 1000, tmax[i]%1000,
sqsum[i] / 1.0e6);
}
}
else if (nreceived[i] && timing && (options & F_MEDIAN) && !(options & F_PERCENTILE)) {
if (!(options & F_PACKED)) {
if (options & F_AUTOSCALETIME) {
if ((tsum[i] / nreceived[i]) >= 1000000) {
printf("round-trip (sec) min/avg/max/square-sum/median = %ld.%03ld/%lu.%03ld/%ld.%03ld/%.3f/%ld.%03ld\n",
tmin[i] / 1000000, tmin[i]%1000,
tsum[i] / (nreceived[i] * 1000000), (tsum[i] / nreceived[i])%1000,
tmax[i] / 1000000, tmax[i]%1000,
sqsum[i] / 1.0e12,
(calculate_n_percentile(packet_time[i], nrecorded, 50))/1000000, (calculate_n_percentile(packet_time[i], nrecorded, 50))%1000);
}
else if ((tsum[i] / nreceived[i]) >= 1000) {
printf("round-trip (msec) min/avg/max/square-sum/median = %ld.%03ld/%lu.%03ld/%ld.%03ld/%.3f/%ld.%03ld\n",
tmin[i] / 1000, tmin[i]%1000,
tsum[i] / (nreceived[i] * 1000), (tsum[i] / nreceived[i])%1000,
tmax[i] / 1000, tmax[i]%1000,
sqsum[i] / 1.0e6,
(calculate_n_percentile(packet_time[i], nrecorded, 50))/1000, (calculate_n_percentile(packet_time[i], nrecorded, 50))%1000);
}
else {
printf("round-trip (usec) min/avg/max/square-sum/median = %ld/%lu/%ld/%.3f/%ld\n",
tmin[i],
tsum[i] / nreceived[i],
tmax[i],
sqsum[i],
(calculate_n_percentile(packet_time[i], nrecorded, 50)));
}
}
else {
printf("round-trip (msec) min/avg/max/square-sum/median = %ld.%03ld/%lu.%03ld/%ld.%03ld/%.3f/%ld.%03ld\n",
tmin[i] / 1000, tmin[i]%1000,
tsum[i] / (nreceived[i] * 1000), (tsum[i] / nreceived[i])%1000,
tmax[i] / 1000, tmax[i]%1000,
sqsum[i] / 1.0e6,
(calculate_n_percentile(packet_time[i], nrecorded, 50))/1000, (calculate_n_percentile(packet_time[i], nrecorded, 50))%1000);
}
}else{
printf("%ld.%03ld/%lu.%03ld/%ld.%03ld/%.3f/%ld.%03ld\n",
tmin[i] / 1000, tmin[i]%1000,
tsum[i] / (nreceived[i] * 1000), (tsum[i] / nreceived[i])%1000,
tmax[i] / 1000, tmax[i]%1000,
sqsum[i] / 1.0e6,
(calculate_n_percentile(packet_time[i], nrecorded, 50))/1000, (calculate_n_percentile(packet_time[i], nrecorded, 50))%1000);
}
}else if (nreceived[i] && timing) {
if (!(options & F_PACKED)) {
if (options & F_AUTOSCALETIME) {
if ((tsum[i] / nreceived[i]) >= 1000000) {
printf("round-trip (sec) 10-percentile/median/90-percentile/square-sum = %ld.%03ld/%ld.%03ld/%ld.%03ld/%.3f\n",
(calculate_n_percentile(packet_time[i], nrecorded, 10)) / 1000000,
(calculate_n_percentile(packet_time[i], nrecorded, 10))%1000,
(calculate_n_percentile(packet_time[i], nrecorded, 50)) / 1000000,
(calculate_n_percentile(packet_time[i], nrecorded, 50))%1000,
(calculate_n_percentile(packet_time[i], nrecorded, 90)) / 1000000,
(calculate_n_percentile(packet_time[i], nrecorded, 90))%1000,
sqsum[i] / 1.0e12);
}
else if ((tsum[i] / nreceived[i]) >= 1000) { /* msec responsetime */
printf("round-trip (msec) 10-percentile/median/90-percentile/square-sum = %ld.%03ld/%ld.%03ld/%ld.%03ld/%.3f\n",
(calculate_n_percentile(packet_time[i], nrecorded, 10)) / 1000,
(calculate_n_percentile(packet_time[i], nrecorded, 10))%1000,
(calculate_n_percentile(packet_time[i], nrecorded, 50)) / 1000,
(calculate_n_percentile(packet_time[i], nrecorded, 50))%1000,
(calculate_n_percentile(packet_time[i], nrecorded, 90)) / 1000,
(calculate_n_percentile(packet_time[i], nrecorded, 90))%1000,
sqsum[i] / 1.0e6);
}
else { /* usec responsetime */
printf("round-trip (usec) 10-percentile/median/90-percentile/square-sum = %ld/%ld/%ld/%.3f\n",
(calculate_n_percentile(packet_time[i], nrecorded, 10)),
(calculate_n_percentile(packet_time[i], nrecorded, 50)),
(calculate_n_percentile(packet_time[i], nrecorded, 90)),
sqsum[i]);
}
}
else {
printf("round-trip (msec) 10-percentile/median/90-percentile/square-sum = %ld.%03ld/%ld.%03ld/%ld.%03ld/%.3f\n",
(calculate_n_percentile(packet_time[i], nrecorded, 10)) / 1000,
(calculate_n_percentile(packet_time[i], nrecorded, 10))%1000,
(calculate_n_percentile(packet_time[i], nrecorded, 50)) / 1000,
(calculate_n_percentile(packet_time[i], nrecorded, 50))%1000,
(calculate_n_percentile(packet_time[i], nrecorded, 90)) / 1000,
(calculate_n_percentile(packet_time[i], nrecorded, 90))%1000,
sqsum[i] / 1.0e6);
}
}else{
printf("%ld.%03ld/%ld.%03ld/%ld.%03ld/%.3f\n",
(calculate_n_percentile(packet_time[i], nrecorded, 10)) / 1000,
(calculate_n_percentile(packet_time[i], nrecorded, 10))%1000,
(calculate_n_percentile(packet_time[i], nrecorded, 50)) / 1000,
(calculate_n_percentile(packet_time[i], nrecorded, 50))%1000,
(calculate_n_percentile(packet_time[i], nrecorded, 90)) / 1000,
(calculate_n_percentile(packet_time[i], nrecorded, 90))%1000,
sqsum[i] / 1.0e6);
}
}
if (!(options & F_PACKED) || !nreceived[i])
printf("\n");
}
printf("----\n");
/* Free up allocated memory */
/* Something here makes mping segfault from time to time */
// free(packet_time[nhosts]);
// free(&hints);
freeaddrinfo(res);
fflush(stdout);
exit(0);
}
static char *ttab[] =
{
"Echo Reply", /* ip + seq + udata */
"Dest Unreachable", /* net, host, proto, port, frag, sr + IP */
"Source Quench", /* IP */
"Redirect", /* redirect type, gateway, + IP */
"Echo",
"Time Exceeded", /* transit, frag reassem + IP */
"Parameter Problem", /* pointer + IP */
"Timestamp", /* id + seq + three timestamps */
"Timestamp Reply", /* " */
"Info Request", /* id + sq */
"Info Reply" /* " */
};
/*
* Print a descriptive string about an ICMP header.
*/
static void
pr_icmph(struct icmp *icp)
{
switch(icp->icmp_type) {
case ICMP_ECHOREPLY:
printf("Echo Reply\n");
/* XXX ID + Seq + Data */
break;
case ICMP_UNREACH:
switch(icp->icmp_code) {
case ICMP_UNREACH_NET:
printf("Destination Net Unreachable\n");
break;
case ICMP_UNREACH_HOST:
printf("Destination Host Unreachable\n");
break;
case ICMP_UNREACH_PROTOCOL:
printf("Destination Protocol Unreachable\n");
break;
case ICMP_UNREACH_PORT:
printf("Destination Port Unreachable\n");
break;
case ICMP_UNREACH_NEEDFRAG:
/* Not implemented yet */
/* printf("Frag needed and DF set (mtu = %u)\n",
(unsigned int)ntohl(icp->un.gateway));*/
printf("Frag needed and DF set: Sorry, not implemented yet\n");
break;
case ICMP_UNREACH_SRCFAIL:
printf("Source Route Failed\n");
break;
default:
printf("Dest Unreachable, Unknown Code: %d\n",
icp->icmp_code);
break;
}
if (!(options & F_VERBOSE))
break;
/* Print returned IP header information */
pr_retip((struct ip*)(icp + 1));
break;
case ICMP_SOURCEQUENCH:
printf("Source Quench\n");
if (!(options & F_VERBOSE))
break;
pr_retip((struct ip*)(icp + 1));
break;
case ICMP_REDIRECT:
switch(icp->icmp_code) {
case ICMP_REDIRECT_NET:
printf("Redirect Network");
break;
case ICMP_REDIRECT_HOST:
printf("Redirect Host");
break;
case ICMP_REDIRECT_TOSNET:
printf("Redirect Type of Service and Network");
break;
case ICMP_REDIRECT_TOSHOST:
printf("Redirect Type of Service and Host");
break;
default:
printf("Redirect, Unknown Code: %d", icp->icmp_code);
break;
}
/* Not implemented yet */
/* printf("(New nexthop: %s)\n", pr_addr(icp->un.gateway)); */
printf("(New nexthop: Sorry, not implemented yet)\n");
if (!(options & F_VERBOSE))
break;
pr_retip((struct ip*)(icp + 1));
break;
case ICMP_ECHO:
printf("Echo Request\n");
/* XXX ID + Seq + Data */
break;
case ICMP_TIMXCEED:
switch(icp->icmp_code) {
case ICMP_TIMXCEED_INTRANS:
printf("Time to live exceeded\n");
break;
case ICMP_TIMXCEED_REASS:
printf("Frag reassembly time exceeded\n");
break;
default:
printf("Time exceeded, Bad Code: %d\n", icp->icmp_code);
break;
}
if (!(options & F_VERBOSE))
break;
pr_retip((struct ip*)(icp + 1));
break;
case ICMP_PARAMPROB:
/* Not implemented yet */
/* printf("Parameter problem: pointer = %d\n", (int)(ntohl(icp->un.gateway)>>24)); */
printf("Parameter problem: pointer = Sorry, not implemented yet \n");
if (!(options & F_VERBOSE))
break;
pr_retip((struct ip*)(icp + 1));
break;
case ICMP_TSTAMP:
printf("Timestamp\n");
/* XXX ID + Seq + 3 timestamps */
break;
case ICMP_TSTAMPREPLY:
printf("Timestamp Reply\n");
/* XXX ID + Seq + 3 timestamps */
break;
case ICMP_IREQ:
printf("Information Request\n");
/* XXX ID + Seq */
break;
case ICMP_IREQREPLY:
printf("Information Reply\n");
/* XXX ID + Seq */
break;
#ifdef ICMP_MASKREQ
case ICMP_MASKREQ:
printf("Address Mask Request\n");
break;
#endif
#ifdef ICMP_MASKREPLY
case ICMP_MASKREPLY:
printf("Address Mask Reply\n");
break;
#endif
default:
printf("Unknown ICMP type: %d\n", icp->icmp_type);
}
}
/*
* Print a descriptive string about an ICMP6 header.
*/
static void
pr_icmph6(struct icmp6_hdr *icp6)
{
switch(icp6->icmp6_type) {
case ICMP6_DST_UNREACH:
printf("Destination unreachable: ");
switch (icp6->icmp6_code) {
case ICMP6_DST_UNREACH_NOROUTE:
printf("No Route to Destination\n");
break;
case ICMP6_DST_UNREACH_ADMIN:
printf("Administratively Prohibited\n");
break;
case ICMP6_DST_UNREACH_NOTNEIGHBOR:
printf("Not a Neighbour\n");
break;
case ICMP6_DST_UNREACH_ADDR:
printf("Destination Host Unreachable\n");
break;
case ICMP6_DST_UNREACH_NOPORT:
printf("Bad port\n");
break;
default:
printf("Unknown code %d\n", icp6->icmp6_code);
break;
}
break;
case ICMP6_PACKET_TOO_BIG:
/* Not implemented yet */
/* printf("Packet too big: mtu=%u",
(unsigned int)ntohl(icp6->icmp6_hop_limit)); */
printf("Packet too big: mtu=Sorry, not implemented yet\n");
if (icp6->icmp6_code)
printf(", code=%d", icp6->icmp6_code);
break;
case ICMP6_TIME_EXCEEDED:
printf("Time exceeded: ");
if (icp6->icmp6_code == ICMP6_TIME_EXCEED_TRANSIT)
printf("Hop limit == 0 in transit\n");
else if (icp6->icmp6_code == ICMP6_TIME_EXCEED_REASSEMBLY)
printf("Reassembly time out\n");
else
printf("code %d\n", icp6->icmp6_code);
break;
case ICMP6_PARAM_PROB:
printf("Parameter problem: ");
if (icp6->icmp6_code == ICMP6_PARAMPROB_HEADER)
printf("Wrong header field\n");
else if (icp6->icmp6_code == ICMP6_PARAMPROB_NEXTHEADER)
printf("Unknown header\n");
else if (icp6->icmp6_code == ICMP6_PARAMPROB_OPTION)
printf("Unknown option\n");
else
printf("code %d\n", icp6->icmp6_code);
/* Not implemented yet */
/* printf ("at %u", (unsigned int)ntohl(icp6->icmp6_pointer));*/
printf("at: Sorry, not implemented yet\n");
break;
case ICMP6_ECHO_REQUEST:
printf("Echo request\n");
break;
case ICMP6_ECHO_REPLY:
printf("Echo reply\n");
break;
case ICMP6_MEMBERSHIP_QUERY:
printf("Membership Query\n");
break;
case ICMP6_MEMBERSHIP_REPORT:
printf("MLD Report\n");
break;
case ICMP6_MEMBERSHIP_REDUCTION:
printf("MLD Reduction\n");
break;
case ND_ROUTER_ADVERT:
printf("Router advertisment\n");
break;
case ND_NEIGHBOR_SOLICIT:
printf("Neighbor solicitation\n");
break;
default:
printf("Unknown ICMP type: %d\n", icp6->icmp6_type);
}
return;
}
/*
* Print an IP header with options.
*/
static void
pr_iph(struct ip *ip)
{
int hlen;
u_char *cp;
hlen = ip->ip_hl << 2;
cp = (u_char *)ip + 20; /* point to options */
printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n");
printf(" %1x %1x %02x %04x %04x",
ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id);
printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13,
(ip->ip_off) & 0x1fff);
printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum);
printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr));
printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr));
/* pr_options not implemented yet */
/* pr_options(cp, hlen);*/
while (hlen-- > 20) {
printf("%02x", *cp++);
}
printf("\n");
}
/*
* Return an ascii host address
* as a dotted quad and optionally with a hostname
*/
/* This one is obsolete and replaced below */
#if 0
char *
pr_addr(l)
unsigned long l;
{
struct hostent *hp;
static char buf[80];
if (numeric || (hp = gethostbyaddr(&l, 4, AF_INET)) == NULL)
sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&l));
else
sprintf(buf, "%s", hp->h_name);
return (buf);
}
#endif
char *pr_addr(struct sockaddr *sa) {
switch (sa->sa_family) {
case AF_INET: {
static char addr[INET_ADDRSTRLEN];
struct sockaddr_in *sain = (struct sockaddr_in *) sa;
inet_ntop(AF_INET, &sain->sin_addr, addr, sizeof(addr));
return addr;
}
case AF_INET6: {
static char addr[INET6_ADDRSTRLEN];
struct sockaddr_in6 *sain6 = (struct sockaddr_in6 *) sa;
inet_ntop(AF_INET6, &sain6->sin6_addr, addr, sizeof(addr));
return addr;
}
}
return NULL;
}
/*
* Dump some info on a returned (via ICMP) IP packet.
*/
static void
pr_retip(struct ip *ip)
{
int hlen;
unsigned char *cp;
pr_iph(ip);
hlen = ip->ip_hl << 2;
cp = (unsigned char *) ip + hlen;
if (ip->ip_p == IPPROTO_TCP) {
printf("TCP: from port %d, to port %d (decimal)\n",
(*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
} else if (ip->ip_p == IPPROTO_UDP) {
printf("UDP: from port %d, to port %d (decimal)\n",
(*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
}
}
/*
* Compare to integers. Used for sorting the packet array.
*/
int compare(const void* a, const void* b)
{
return *(int *)a - *(int *)b;
}
/*
* Calculates the n percentile. arr has to be sorted.
*/
int calculate_n_percentile(int *arr, int arr_len, int n)
{
int mid;
mid = arr_len*n/100;
if(arr_len*n%100 == 0){
if(mid==0)mid++;
return (arr[mid]+arr[mid-1])/2;
}else{
return arr[mid];
}
}
syntax highlighted by Code2HTML, v. 0.9.1