/*
 *                    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