/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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]; } }