133675Sbostic /* 2*38644Sbostic * Copyright (c) 1989 The Regents of the University of California. 3*38644Sbostic * All rights reserved. 4*38644Sbostic * 5*38644Sbostic * This code is derived from software contributed to Berkeley by 6*38644Sbostic * Mike Muuss. 7*38644Sbostic * 8*38644Sbostic * Redistribution and use in source and binary forms are permitted 9*38644Sbostic * provided that the above copyright notice and this paragraph are 10*38644Sbostic * duplicated in all such forms and that any documentation, 11*38644Sbostic * advertising materials, and other materials related to such 12*38644Sbostic * distribution and use acknowledge that the software was developed 13*38644Sbostic * by the University of California, Berkeley. The name of the 14*38644Sbostic * University may not be used to endorse or promote products derived 15*38644Sbostic * from this software without specific prior written permission. 16*38644Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17*38644Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18*38644Sbostic * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19*38644Sbostic */ 20*38644Sbostic 21*38644Sbostic #ifndef lint 22*38644Sbostic char copyright[] = 23*38644Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 24*38644Sbostic All rights reserved.\n"; 25*38644Sbostic #endif /* not lint */ 26*38644Sbostic 27*38644Sbostic #ifndef lint 28*38644Sbostic static char sccsid[] = "@(#)ping.c 5.1 (Berkeley) 08/16/89"; 29*38644Sbostic #endif /* not lint */ 30*38644Sbostic 31*38644Sbostic /* 3219057Skarels * P I N G . C 3319057Skarels * 3419057Skarels * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, 3519057Skarels * measure round-trip-delays and packet loss across network paths. 3619057Skarels * 3719057Skarels * Author - 3819057Skarels * Mike Muuss 3919057Skarels * U. S. Army Ballistic Research Laboratory 4019057Skarels * December, 1983 4119057Skarels * 4219057Skarels * Status - 4319057Skarels * Public Domain. Distribution Unlimited. 4419057Skarels * Bugs - 4519057Skarels * More statistics could always be gathered. 4619057Skarels * This program has to run SUID to ROOT to access the ICMP socket. 4719057Skarels */ 4819057Skarels 4919057Skarels #include <sys/param.h> 5019057Skarels #include <sys/socket.h> 5119057Skarels #include <sys/file.h> 52*38644Sbostic #include <sys/time.h> 53*38644Sbostic #include <sys/signal.h> 5419057Skarels 5519057Skarels #include <netinet/in_systm.h> 5619057Skarels #include <netinet/in.h> 5719057Skarels #include <netinet/ip.h> 5819057Skarels #include <netinet/ip_icmp.h> 5938643Sbostic #include <netinet/ip_var.h> 60*38644Sbostic #include <netdb.h> 61*38644Sbostic #include <unistd.h> 62*38644Sbostic #include <stdio.h> 6338643Sbostic #include <ctype.h> 64*38644Sbostic #include <errno.h> 65*38644Sbostic #include <strings.h> 6619057Skarels 67*38644Sbostic #define DEFDATALEN (64 - 8) /* default data length */ 68*38644Sbostic #define MAXIPLEN 60 69*38644Sbostic #define MAXICMPLEN 76 70*38644Sbostic #define MAXPACKET (65536 - 60 - 8)/* max packet size */ 71*38644Sbostic #define MAXWAIT 10 /* max seconds to wait for response */ 72*38644Sbostic #define NROUTES 9 /* number of record route slots */ 7319057Skarels 74*38644Sbostic #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ 75*38644Sbostic #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ 76*38644Sbostic #define SET(bit) (A(bit) |= B(bit)) 77*38644Sbostic #define CLR(bit) (A(bit) &= (~B(bit))) 78*38644Sbostic #define TST(bit) (A(bit) & B(bit)) 7938643Sbostic 80*38644Sbostic /* various options */ 81*38644Sbostic int options; 82*38644Sbostic #define F_FLOOD 0x001 83*38644Sbostic #define F_INTERVAL 0x002 84*38644Sbostic #define F_NUMERIC 0x004 85*38644Sbostic #define F_PINGFILLED 0x008 86*38644Sbostic #define F_QUIET 0x010 87*38644Sbostic #define F_RROUTE 0x020 88*38644Sbostic #define F_SO_DEBUG 0x040 89*38644Sbostic #define F_SO_DONTROUTE 0x080 90*38644Sbostic #define F_VERBOSE 0x100 9138643Sbostic 92*38644Sbostic /* 93*38644Sbostic * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum 94*38644Sbostic * number of received sequence numbers we can keep track of. Change 128 95*38644Sbostic * to 8192 for complete accuracy... 96*38644Sbostic */ 97*38644Sbostic #define MAX_DUP_CHK (8 * 128) 98*38644Sbostic int mx_dup_ck = MAX_DUP_CHK; 99*38644Sbostic char rcvd_tbl[MAX_DUP_CHK / 8]; 10038643Sbostic 101*38644Sbostic struct sockaddr whereto; /* who to ping */ 102*38644Sbostic int datalen = DEFDATALEN; 103*38644Sbostic int s; /* socket file descriptor */ 104*38644Sbostic u_char outpack[MAXPACKET]; 105*38644Sbostic char BSPACE = '\b'; /* characters written for flood */ 106*38644Sbostic char DOT = '.'; 10719057Skarels char *hostname; 108*38644Sbostic int ident; /* process id to identify our packets */ 10919057Skarels 110*38644Sbostic /* counters */ 111*38644Sbostic long npackets; /* max packets to transmit */ 112*38644Sbostic long nreceived; /* # of packets we got back */ 113*38644Sbostic long nrepeats; /* number of duplicates */ 114*38644Sbostic long ntransmitted; /* sequence # for outbound packets = #sent */ 115*38644Sbostic int interval = 1; /* interval between packets */ 11638643Sbostic 117*38644Sbostic /* timing */ 118*38644Sbostic int timing; /* flag to do timing */ 119*38644Sbostic long tmin = LONG_MAX; /* minimum round trip time */ 120*38644Sbostic long tmax; /* maximum round trip time */ 121*38644Sbostic u_long tsum; /* sum of all times, for doing average */ 12219057Skarels 12338643Sbostic u_long inet_addr(); 124*38644Sbostic char *inet_ntoa(), *pr_addr(); 125*38644Sbostic int catcher(), finish(), prefinish(); 12619057Skarels 12719057Skarels main(argc, argv) 128*38644Sbostic int argc; 129*38644Sbostic char **argv; 13019057Skarels { 131*38644Sbostic extern int errno, optind; 132*38644Sbostic extern char *optarg; 133*38644Sbostic struct timeval timeout; 134*38644Sbostic struct hostent *hp; 135*38644Sbostic struct sockaddr_in *to; 13624199Skarels struct protoent *proto; 137*38644Sbostic register int i; 138*38644Sbostic int ch, fdmask, hold, packlen, preload; 139*38644Sbostic u_char *datap, *packet; 140*38644Sbostic char *target, hnamebuf[MAXHOSTNAMELEN], *malloc(); 141*38644Sbostic #ifdef IP_OPTIONS 142*38644Sbostic char rspace[3 + 4 * NROUTES + 1]; /* record route space */ 143*38644Sbostic #endif 14419057Skarels 145*38644Sbostic preload = 0; 146*38644Sbostic datap = &outpack[8 + sizeof(struct timeval)]; 147*38644Sbostic while ((ch = getopt(argc, argv, "Rc:dfh:i:l:np:qrs:v")) != EOF) 148*38644Sbostic switch(ch) { 149*38644Sbostic case 'c': 150*38644Sbostic npackets = atoi(optarg); 151*38644Sbostic if (npackets <= 0) { 152*38644Sbostic (void)fprintf(stderr, 153*38644Sbostic "ping: bad number of packets to transmit.\n"); 154*38644Sbostic exit(1); 155*38644Sbostic } 156*38644Sbostic break; 157*38644Sbostic case 'd': 158*38644Sbostic options |= F_SO_DEBUG; 159*38644Sbostic break; 160*38644Sbostic case 'f': 161*38644Sbostic if (getuid()) { 162*38644Sbostic (void)fprintf(stderr, 163*38644Sbostic "ping: you must be root to use the -f option.\n"); 164*38644Sbostic exit(1); 165*38644Sbostic } 166*38644Sbostic options |= F_FLOOD; 167*38644Sbostic setbuf(stdout, (char *)NULL); 168*38644Sbostic break; 169*38644Sbostic case 'i': /* wait between sending packets */ 170*38644Sbostic interval = atoi(optarg); 171*38644Sbostic if (interval <= 0) { 172*38644Sbostic (void)fprintf(stderr, 173*38644Sbostic "ping: bad timing interval.\n"); 174*38644Sbostic exit(1); 175*38644Sbostic } 176*38644Sbostic options |= F_INTERVAL; 177*38644Sbostic break; 178*38644Sbostic case 'l': 179*38644Sbostic preload = atoi(optarg); 180*38644Sbostic if (preload < 0) { 181*38644Sbostic (void)fprintf(stderr, 182*38644Sbostic "ping: bad preload value.\n"); 183*38644Sbostic exit(1); 184*38644Sbostic } 185*38644Sbostic break; 186*38644Sbostic case 'n': 187*38644Sbostic options |= F_NUMERIC; 188*38644Sbostic break; 189*38644Sbostic case 'p': /* fill buffer with user pattern */ 190*38644Sbostic options |= F_PINGFILLED; 191*38644Sbostic fill((char *)datap, optarg); 19238643Sbostic break; 193*38644Sbostic case 'q': 194*38644Sbostic options |= F_QUIET; 195*38644Sbostic break; 196*38644Sbostic case 'R': 197*38644Sbostic options |= F_RROUTE; 198*38644Sbostic break; 199*38644Sbostic case 'r': 200*38644Sbostic options |= F_SO_DONTROUTE; 201*38644Sbostic break; 202*38644Sbostic case 's': /* size of packet to send */ 203*38644Sbostic datalen = atoi(optarg); 204*38644Sbostic if (datalen > MAXPACKET) { 205*38644Sbostic (void)fprintf(stderr, 206*38644Sbostic "ping: packet size too large.\n"); 20738643Sbostic exit(1); 208*38644Sbostic } 209*38644Sbostic if (datalen <= 0) { 210*38644Sbostic (void)fprintf(stderr, 211*38644Sbostic "ping: illegal packet size.\n"); 212*38644Sbostic exit(1); 213*38644Sbostic } 214*38644Sbostic break; 215*38644Sbostic case 'v': 216*38644Sbostic options |= F_VERBOSE; 217*38644Sbostic break; 218*38644Sbostic default: 219*38644Sbostic usage(); 22019064Skarels } 221*38644Sbostic argc -= optind; 222*38644Sbostic argv += optind; 22338643Sbostic 224*38644Sbostic if (argc != 1) 225*38644Sbostic usage(); 226*38644Sbostic target = *argv; 22719057Skarels 228*38644Sbostic bzero((char *)&whereto, sizeof(struct sockaddr)); 229*38644Sbostic to = (struct sockaddr_in *)&whereto; 23024199Skarels to->sin_family = AF_INET; 231*38644Sbostic to->sin_addr.s_addr = inet_addr(target); 232*38644Sbostic if (to->sin_addr.s_addr != (u_int)-1) 233*38644Sbostic hostname = target; 234*38644Sbostic else { 235*38644Sbostic hp = gethostbyname(target); 236*38644Sbostic if (!hp) { 237*38644Sbostic (void)fprintf(stderr, 238*38644Sbostic "ping: unknown host %s\n", target); 23924199Skarels exit(1); 24019057Skarels } 241*38644Sbostic to->sin_family = hp->h_addrtype; 242*38644Sbostic bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length); 243*38644Sbostic (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1); 244*38644Sbostic hostname = hnamebuf; 24519057Skarels } 24619057Skarels 247*38644Sbostic if (options & F_FLOOD && options & F_INTERVAL) { 248*38644Sbostic (void)fprintf(stderr, 249*38644Sbostic "ping: -f and -i incompatible options.\n"); 25038643Sbostic exit(1); 25138643Sbostic } 25238643Sbostic 253*38644Sbostic if (datalen >= sizeof(struct timeval)) /* can we time transfer */ 25419064Skarels timing = 1; 255*38644Sbostic packlen = datalen + MAXIPLEN + MAXICMPLEN; 256*38644Sbostic if (!(packet = (u_char *)malloc((u_int)packlen))) { 257*38644Sbostic (void)fprintf(stderr, "ping: out of memory.\n"); 25838643Sbostic exit(1); 25938643Sbostic } 260*38644Sbostic if (!(options & F_PINGFILLED)) 261*38644Sbostic for (i = 8; i < datalen; ++i) 262*38644Sbostic *datap++ = i; 26319057Skarels 26419057Skarels ident = getpid() & 0xFFFF; 26519057Skarels 266*38644Sbostic if (!(proto = getprotobyname("icmp"))) { 267*38644Sbostic (void)fprintf(stderr, "ping: unknown protocol icmp.\n"); 268*38644Sbostic exit(1); 26924199Skarels } 27024199Skarels if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { 27119057Skarels perror("ping: socket"); 272*38644Sbostic exit(1); 27319057Skarels } 274*38644Sbostic hold = 1; 275*38644Sbostic if (options & F_SO_DEBUG) 276*38644Sbostic (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, 277*38644Sbostic sizeof(hold)); 278*38644Sbostic if (options & F_SO_DONTROUTE) 279*38644Sbostic (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, 280*38644Sbostic sizeof(hold)); 281*38644Sbostic 282*38644Sbostic /* record route option */ 283*38644Sbostic if (options & F_RROUTE) { 28438643Sbostic #ifdef IP_OPTIONS 28538643Sbostic rspace[IPOPT_OPTVAL] = IPOPT_RR; 28638643Sbostic rspace[IPOPT_OLEN] = sizeof(rspace)-1; 28738643Sbostic rspace[IPOPT_OFFSET] = IPOPT_MINOFF; 288*38644Sbostic if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, 289*38644Sbostic sizeof(rspace)) < 0) { 290*38644Sbostic perror("ping: record route"); 291*38644Sbostic exit(1); 29238643Sbostic } 29338643Sbostic #else 294*38644Sbostic (void)fprintf(stderr, 295*38644Sbostic "ping: record route not available in this implementation.\n"); 296*38644Sbostic exit(1); 297*38644Sbostic #endif /* IP_OPTIONS */ 29838643Sbostic } 29919057Skarels 300*38644Sbostic /* 301*38644Sbostic * When pinging the broadcast address, you can get a lot of answers. 302*38644Sbostic * Doing something so evil is useful if you are trying to stress the 303*38644Sbostic * ethernet, or just want to fill the arp cache to get some stuff for 304*38644Sbostic * /etc/ethers. 30538643Sbostic */ 306*38644Sbostic hold = 48 * 1024; 307*38644Sbostic (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, 308*38644Sbostic sizeof(hold)); 30919057Skarels 310*38644Sbostic if (to->sin_family == AF_INET) 311*38644Sbostic (void)printf("PING %s (%s): %d data bytes\n", hostname, 312*38644Sbostic inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr), 313*38644Sbostic datalen); 314*38644Sbostic else 315*38644Sbostic (void)printf("PING %s: %d data bytes\n", hostname, datalen); 31619057Skarels 317*38644Sbostic (void)signal(SIGINT, prefinish); 318*38644Sbostic (void)signal(SIGALRM, catcher); 319*38644Sbostic 320*38644Sbostic while (preload--) /* fire off them quickies */ 32138643Sbostic pinger(); 32219057Skarels 323*38644Sbostic if (options & F_FLOOD) { 324*38644Sbostic timeout.tv_sec = 0; 325*38644Sbostic timeout.tv_usec = 10000; 326*38644Sbostic fdmask = 1 << s; 327*38644Sbostic } else 328*38644Sbostic catcher(); /* start things going */ 32938643Sbostic 33019057Skarels for (;;) { 331*38644Sbostic struct sockaddr_in from; 332*38644Sbostic register int cc; 333*38644Sbostic int fromlen; 33419057Skarels 335*38644Sbostic if (options & F_FLOOD) { 33638643Sbostic pinger(); 337*38644Sbostic if (!select(32, (fd_set *)&fdmask, (fd_set *)NULL, 338*38644Sbostic (fd_set *)NULL, &timeout)) 33938643Sbostic continue; 34038643Sbostic } 341*38644Sbostic fromlen = sizeof(from); 342*38644Sbostic if ((cc = recvfrom(s, (char *)packet, packlen, 0, 343*38644Sbostic (struct sockaddr *)&from, &fromlen)) < 0) { 344*38644Sbostic if (errno == EINTR) 34519057Skarels continue; 34619057Skarels perror("ping: recvfrom"); 34719057Skarels continue; 34819057Skarels } 349*38644Sbostic pr_pack((char *)packet, cc, &from); 35019064Skarels if (npackets && nreceived >= npackets) 351*38644Sbostic break; 35219057Skarels } 353*38644Sbostic finish(); 354*38644Sbostic /* NOTREACHED */ 35519057Skarels } 35619057Skarels 35719057Skarels /* 358*38644Sbostic * catcher -- 359*38644Sbostic * This routine causes another PING to be transmitted, and then 36019057Skarels * schedules another SIGALRM for 1 second from now. 36119057Skarels * 362*38644Sbostic * bug -- 363*38644Sbostic * Our sense of time will slowly skew (i.e., packets will not be 364*38644Sbostic * launched exactly at 1-second intervals). This does not affect the 365*38644Sbostic * quality of the delay and loss statistics. 36619057Skarels */ 36719057Skarels catcher() 36819057Skarels { 36919064Skarels int waittime; 37019064Skarels 37119057Skarels pinger(); 372*38644Sbostic (void)signal(SIGALRM, catcher); 373*38644Sbostic if (!npackets || ntransmitted < npackets) 374*38644Sbostic alarm((u_int)interval); 37519064Skarels else { 37619064Skarels if (nreceived) { 37719064Skarels waittime = 2 * tmax / 1000; 378*38644Sbostic if (!waittime) 37919064Skarels waittime = 1; 38019064Skarels } else 38119064Skarels waittime = MAXWAIT; 382*38644Sbostic (void)signal(SIGALRM, finish); 383*38644Sbostic (void)alarm((u_int)waittime); 38419064Skarels } 38519057Skarels } 38619057Skarels 38719057Skarels /* 388*38644Sbostic * pinger -- 389*38644Sbostic * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet 39019057Skarels * will be added on by the kernel. The ID field is our UNIX process ID, 39119057Skarels * and the sequence number is an ascending integer. The first 8 bytes 39219057Skarels * of the data portion are used to hold a UNIX "timeval" struct in VAX 39319057Skarels * byte-order, to compute the round-trip time. 39419057Skarels */ 39519057Skarels pinger() 39619057Skarels { 397*38644Sbostic register struct icmp *icp; 398*38644Sbostic register int cc; 399*38644Sbostic int i; 40019057Skarels 401*38644Sbostic icp = (struct icmp *)outpack; 40219057Skarels icp->icmp_type = ICMP_ECHO; 40319057Skarels icp->icmp_code = 0; 40419057Skarels icp->icmp_cksum = 0; 405*38644Sbostic icp->icmp_seq = ++ntransmitted; 406*38644Sbostic icp->icmp_id = ident; /* ID */ 40719057Skarels 408*38644Sbostic CLR(icp->icmp_seq % mx_dup_ck); 40938643Sbostic 41019064Skarels if (timing) 411*38644Sbostic (void)gettimeofday((struct timeval *)&outpack[8], 412*38644Sbostic (struct timezone *)NULL); 41319057Skarels 414*38644Sbostic cc = datalen + 8; /* skips ICMP portion */ 41519057Skarels 416*38644Sbostic /* compute ICMP checksum here */ 417*38644Sbostic icp->icmp_cksum = in_cksum((u_short *)icp, cc); 41819057Skarels 419*38644Sbostic i = sendto(s, (char *)outpack, cc, 0, &whereto, 420*38644Sbostic sizeof(struct sockaddr)); 421*38644Sbostic 422*38644Sbostic if (i < 0 || i != cc) { 423*38644Sbostic if (i < 0) 424*38644Sbostic perror("ping: sendto"); 425*38644Sbostic (void)printf("ping: wrote %s %d chars, ret=%d\n", 426*38644Sbostic hostname, cc, i); 42719057Skarels } 428*38644Sbostic if (options & F_FLOOD) 429*38644Sbostic (void)write(STDOUT_FILENO, &DOT, 1); 43019057Skarels } 43119057Skarels 43219057Skarels /* 433*38644Sbostic * pr_pack -- 434*38644Sbostic * Print out the packet, if it came from us. This logic is necessary 43519057Skarels * because ALL readers of the ICMP socket get a copy of ALL ICMP packets 43619057Skarels * which arrive ('tis only fair). This permits multiple copies of this 43719057Skarels * program to be run without having intermingled output (or statistics!). 43819057Skarels */ 439*38644Sbostic pr_pack(buf, cc, from) 440*38644Sbostic char *buf; 441*38644Sbostic int cc; 442*38644Sbostic struct sockaddr_in *from; 44319057Skarels { 44427070Skarels register struct icmp *icp; 445*38644Sbostic register u_long l; 44638643Sbostic register int i, j; 44738643Sbostic register u_char *cp,*dp; 44838643Sbostic static int old_rrlen; 44938643Sbostic static char old_rr[MAX_IPOPTLEN]; 450*38644Sbostic struct ip *ip; 451*38644Sbostic struct timeval tv, *tp; 452*38644Sbostic long triptime; 453*38644Sbostic int hlen, dupflag; 45419057Skarels 455*38644Sbostic (void)gettimeofday(&tv, (struct timezone *)NULL); 45619057Skarels 45738643Sbostic /* Check the IP header */ 458*38644Sbostic ip = (struct ip *)buf; 45927070Skarels hlen = ip->ip_hl << 2; 460*38644Sbostic if (cc < hlen + ICMP_MINLEN) { 461*38644Sbostic if (options & F_VERBOSE) 462*38644Sbostic (void)fprintf(stderr, 463*38644Sbostic "ping: packet too short (%d bytes) from %s\n", cc, 464*38644Sbostic inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr)); 46527070Skarels return; 46627070Skarels } 46738643Sbostic 46838643Sbostic /* Now the ICMP part */ 46927070Skarels cc -= hlen; 47027070Skarels icp = (struct icmp *)(buf + hlen); 471*38644Sbostic if (icp->icmp_type == ICMP_ECHOREPLY) { 472*38644Sbostic if (icp->icmp_id != ident) 47338643Sbostic return; /* 'Twas not our ECHO */ 474*38644Sbostic ++nreceived; 47538643Sbostic if (timing) { 47638643Sbostic #ifndef icmp_data 47738643Sbostic tp = (struct timeval *)&icp->icmp_ip; 47838643Sbostic #else 479*38644Sbostic tp = (struct timeval *)icp->icmp_data; 48038643Sbostic #endif 481*38644Sbostic tvsub(&tv, tp); 482*38644Sbostic triptime = tv.tv_sec * 1000 + (tv.tv_usec / 1000); 48338643Sbostic tsum += triptime; 484*38644Sbostic if (triptime < tmin) 48538643Sbostic tmin = triptime; 486*38644Sbostic if (triptime > tmax) 48738643Sbostic tmax = triptime; 48819064Skarels } 48938643Sbostic 490*38644Sbostic if (TST(icp->icmp_seq % mx_dup_ck)) { 491*38644Sbostic ++nrepeats; 492*38644Sbostic --nreceived; 493*38644Sbostic dupflag = 1; 494*38644Sbostic } else { 495*38644Sbostic SET(icp->icmp_seq % mx_dup_ck); 496*38644Sbostic dupflag = 0; 49738643Sbostic } 49838643Sbostic 499*38644Sbostic if (options & F_QUIET) 50038643Sbostic return; 50138643Sbostic 502*38644Sbostic if (options & F_FLOOD) 503*38644Sbostic (void)write(STDOUT_FILENO, &BSPACE, 1); 504*38644Sbostic else { 505*38644Sbostic (void)printf("%d bytes from %s: icmp_seq=%u", cc, 506*38644Sbostic inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), 507*38644Sbostic icp->icmp_seq); 508*38644Sbostic (void)printf(" ttl=%d", ip->ip_ttl); 50938643Sbostic if (timing) 510*38644Sbostic (void)printf(" time=%ld ms", triptime); 511*38644Sbostic if (dupflag) 512*38644Sbostic (void)printf(" (DUP!)"); 51338643Sbostic /* check the data */ 51438643Sbostic cp = (u_char*)&icp->icmp_data[8]; 515*38644Sbostic dp = &outpack[8 + sizeof(struct timeval)]; 516*38644Sbostic for (i = 8; i < datalen; ++i, ++cp, ++dp) { 51738643Sbostic if (*cp != *dp) { 518*38644Sbostic (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", 519*38644Sbostic i, *dp, *cp); 52038643Sbostic cp = (u_char*)&icp->icmp_data[0]; 521*38644Sbostic for (i = 8; i < datalen; ++i, ++cp) { 522*38644Sbostic if ((i % 32) == 8) 523*38644Sbostic (void)printf("\n\t"); 524*38644Sbostic (void)printf("%x ", *cp); 52538643Sbostic } 52638643Sbostic break; 52738643Sbostic } 52838643Sbostic } 52938643Sbostic } 53038643Sbostic } else { 53138643Sbostic /* We've got something other than an ECHOREPLY */ 532*38644Sbostic if (!(options & F_VERBOSE)) 53338643Sbostic return; 534*38644Sbostic (void)printf("%d bytes from %s: ", cc, 535*38644Sbostic pr_addr(from->sin_addr.s_addr)); 536*38644Sbostic pr_icmph(icp); 53719057Skarels } 53819057Skarels 53938643Sbostic /* Display any IP options */ 54038643Sbostic cp = (u_char *)buf + sizeof(struct ip); 541*38644Sbostic 542*38644Sbostic /* ANSI C will force hlen to unsigned! */ 543*38644Sbostic for (; hlen > sizeof(struct ip); --hlen, ++cp) 544*38644Sbostic switch (*cp) { 54538643Sbostic case IPOPT_EOL: 54638643Sbostic hlen = 0; 54738643Sbostic break; 54838643Sbostic case IPOPT_LSRR: 549*38644Sbostic (void)printf("\nLSRR: "); 55038643Sbostic hlen -= 2; 55138643Sbostic j = *++cp; 55238643Sbostic ++cp; 553*38644Sbostic if (j > IPOPT_MINOFF) 554*38644Sbostic for (;;) { 555*38644Sbostic l = *++cp; 556*38644Sbostic l = (l<<8) + *++cp; 557*38644Sbostic l = (l<<8) + *++cp; 558*38644Sbostic l = (l<<8) + *++cp; 559*38644Sbostic if (l == 0) 560*38644Sbostic (void)printf("\t0.0.0.0"); 56138643Sbostic else 562*38644Sbostic (void)printf("\t%s", pr_addr(ntohl(l))); 56338643Sbostic hlen -= 4; 56438643Sbostic j -= 4; 56538643Sbostic if (j <= IPOPT_MINOFF) 56638643Sbostic break; 567*38644Sbostic (void)putchar('\n'); 56838643Sbostic } 56938643Sbostic break; 57038643Sbostic case IPOPT_RR: 571*38644Sbostic j = *++cp; /* get length */ 572*38644Sbostic i = *++cp; /* and pointer */ 57338643Sbostic hlen -= 2; 574*38644Sbostic if (i > j) 575*38644Sbostic i = j; 57638643Sbostic i -= IPOPT_MINOFF; 57738643Sbostic if (i <= 0) 57838643Sbostic continue; 57938643Sbostic if (i == old_rrlen 58038643Sbostic && cp == (u_char *)buf + sizeof(struct ip) + 2 58138643Sbostic && !bcmp((char *)cp, old_rr, i) 582*38644Sbostic && !(options & F_FLOOD)) { 583*38644Sbostic (void)printf("\t(same route)"); 584*38644Sbostic i = ((i + 3) / 4) * 4; 58538643Sbostic hlen -= i; 58638643Sbostic cp += i; 58738643Sbostic break; 58838643Sbostic } 58938643Sbostic old_rrlen = i; 59038643Sbostic bcopy((char *)cp, old_rr, i); 591*38644Sbostic (void)printf("\nRR: "); 59238643Sbostic for (;;) { 59338643Sbostic l = *++cp; 59438643Sbostic l = (l<<8) + *++cp; 59538643Sbostic l = (l<<8) + *++cp; 59638643Sbostic l = (l<<8) + *++cp; 59738643Sbostic if (l == 0) 598*38644Sbostic (void)printf("\t0.0.0.0"); 59938643Sbostic else 600*38644Sbostic (void)printf("\t%s", pr_addr(ntohl(l))); 60138643Sbostic hlen -= 4; 60238643Sbostic i -= 4; 60338643Sbostic if (i <= 0) 60438643Sbostic break; 605*38644Sbostic (void)putchar('\n'); 60638643Sbostic } 60738643Sbostic break; 60838643Sbostic case IPOPT_NOP: 609*38644Sbostic (void)printf("\nNOP"); 61038643Sbostic break; 61138643Sbostic default: 612*38644Sbostic (void)printf("\nunknown option %x", *cp); 61338643Sbostic break; 61438643Sbostic } 615*38644Sbostic if (!(options & F_FLOOD)) { 616*38644Sbostic (void)putchar('\n'); 617*38644Sbostic (void)fflush(stdout); 61838643Sbostic } 61919057Skarels } 62019057Skarels 62119057Skarels /* 622*38644Sbostic * in_cksum -- 623*38644Sbostic * Checksum routine for Internet Protocol family headers (C Version) 62419057Skarels */ 62519057Skarels in_cksum(addr, len) 626*38644Sbostic u_short *addr; 627*38644Sbostic int len; 62819057Skarels { 62924199Skarels register int nleft = len; 63024199Skarels register u_short *w = addr; 63124199Skarels register int sum = 0; 63238643Sbostic u_short answer = 0; 63319057Skarels 63419057Skarels /* 635*38644Sbostic * Our algorithm is simple, using a 32 bit accumulator (sum), we add 636*38644Sbostic * sequential 16 bit words to it, and at the end, fold back all the 637*38644Sbostic * carry bits from the top 16 bits into the lower 16 bits. 63819057Skarels */ 639*38644Sbostic while (nleft > 1) { 64019057Skarels sum += *w++; 64119057Skarels nleft -= 2; 64219057Skarels } 64324199Skarels 64424199Skarels /* mop up an odd byte, if necessary */ 645*38644Sbostic if (nleft == 1) { 64638643Sbostic *(u_char *)(&answer) = *(u_char *)w ; 64738643Sbostic sum += answer; 64832208Sbostic } 64919057Skarels 650*38644Sbostic /* add back carry outs from top 16 bits to low 16 bits */ 65125244Skarels sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 65225244Skarels sum += (sum >> 16); /* add carry */ 65325244Skarels answer = ~sum; /* truncate to 16 bits */ 654*38644Sbostic return(answer); 65519057Skarels } 65619057Skarels 65719057Skarels /* 658*38644Sbostic * tvsub -- 659*38644Sbostic * Subtract 2 timeval structs: out = out - in. Out is assumed to 660*38644Sbostic * be >= in. 66119057Skarels */ 662*38644Sbostic tvsub(out, in) 663*38644Sbostic register struct timeval *out, *in; 66419057Skarels { 665*38644Sbostic if ((out->tv_usec -= in->tv_usec) < 0) { 666*38644Sbostic --out->tv_sec; 66719057Skarels out->tv_usec += 1000000; 66819057Skarels } 66919057Skarels out->tv_sec -= in->tv_sec; 67019057Skarels } 67119057Skarels 672*38644Sbostic /* 673*38644Sbostic * prefinish -- 674*38644Sbostic * On the first SIGINT, allow any outstanding packets to dribble in. 675*38644Sbostic */ 67638643Sbostic prefinish() 67738643Sbostic { 678*38644Sbostic /* quit now if caught up or if remote is dead */ 679*38644Sbostic if (!nreceived || nreceived >= ntransmitted) 68038643Sbostic finish(); 681*38644Sbostic 682*38644Sbostic /* do this only the 1st time, let the normal limit work */ 683*38644Sbostic (void)signal(SIGINT, finish); 684*38644Sbostic npackets = ntransmitted + 1; 68538643Sbostic } 68619057Skarels /* 687*38644Sbostic * finish -- 688*38644Sbostic * Print out statistics, and give up. 68919057Skarels */ 69019057Skarels finish() 69119057Skarels { 692*38644Sbostic (void)putchar('\n'); 693*38644Sbostic (void)fflush(stdout); 694*38644Sbostic (void)printf("--- %s ping statistics ---\n", hostname); 695*38644Sbostic (void)printf("%ld packets transmitted, ", ntransmitted); 696*38644Sbostic (void)printf("%ld packets received, ", nreceived); 697*38644Sbostic if (nrepeats) 698*38644Sbostic (void)printf("+%ld duplicates, ", nrepeats); 69938643Sbostic if (ntransmitted) 700*38644Sbostic if (nreceived > ntransmitted) 701*38644Sbostic (void)printf("-- somebody's printing up packets!"); 70238029Skarels else 703*38644Sbostic (void)printf("%d%% packet loss", 704*38644Sbostic (int) (((ntransmitted - nreceived) * 100) / 705*38644Sbostic ntransmitted)); 706*38644Sbostic (void)putchar('\n'); 70719064Skarels if (nreceived && timing) 708*38644Sbostic (void)printf("round-trip min/avg/max = %ld/%lu/%ld ms\n", 709*38644Sbostic tmin, tsum / (nreceived + nrepeats), tmax); 71019057Skarels exit(0); 71119057Skarels } 71238643Sbostic 713*38644Sbostic #ifdef notdef 71438643Sbostic static char *ttab[] = { 71538643Sbostic "Echo Reply", /* ip + seq + udata */ 71638643Sbostic "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ 71738643Sbostic "Source Quench", /* IP */ 71838643Sbostic "Redirect", /* redirect type, gateway, + IP */ 71938643Sbostic "Echo", 72038643Sbostic "Time Exceeded", /* transit, frag reassem + IP */ 72138643Sbostic "Parameter Problem", /* pointer + IP */ 72238643Sbostic "Timestamp", /* id + seq + three timestamps */ 72338643Sbostic "Timestamp Reply", /* " */ 72438643Sbostic "Info Request", /* id + sq */ 72538643Sbostic "Info Reply" /* " */ 72638643Sbostic }; 727*38644Sbostic #endif 72838643Sbostic 72938643Sbostic /* 730*38644Sbostic * pr_icmph -- 731*38644Sbostic * Print a descriptive string about an ICMP header. 73238643Sbostic */ 733*38644Sbostic pr_icmph(icp) 734*38644Sbostic struct icmp *icp; 73538643Sbostic { 736*38644Sbostic switch(icp->icmp_type) { 73738643Sbostic case ICMP_ECHOREPLY: 738*38644Sbostic (void)printf("Echo Reply\n"); 73938643Sbostic /* XXX ID + Seq + Data */ 74038643Sbostic break; 74138643Sbostic case ICMP_UNREACH: 742*38644Sbostic switch(icp->icmp_code) { 74338643Sbostic case ICMP_UNREACH_NET: 744*38644Sbostic (void)printf("Destination Net Unreachable\n"); 74538643Sbostic break; 74638643Sbostic case ICMP_UNREACH_HOST: 747*38644Sbostic (void)printf("Destination Host Unreachable\n"); 74838643Sbostic break; 74938643Sbostic case ICMP_UNREACH_PROTOCOL: 750*38644Sbostic (void)printf("Destination Protocol Unreachable\n"); 75138643Sbostic break; 75238643Sbostic case ICMP_UNREACH_PORT: 753*38644Sbostic (void)printf("Destination Port Unreachable\n"); 75438643Sbostic break; 75538643Sbostic case ICMP_UNREACH_NEEDFRAG: 756*38644Sbostic (void)printf("frag needed and DF set\n"); 75738643Sbostic break; 75838643Sbostic case ICMP_UNREACH_SRCFAIL: 759*38644Sbostic (void)printf("Source Route Failed\n"); 76038643Sbostic break; 76138643Sbostic default: 762*38644Sbostic (void)printf("Dest Unreachable, Bad Code: %d\n", 763*38644Sbostic icp->icmp_code); 76438643Sbostic break; 76538643Sbostic } 76638643Sbostic /* Print returned IP header information */ 76738643Sbostic #ifndef icmp_data 768*38644Sbostic pr_retip(&icp->icmp_ip); 76938643Sbostic #else 770*38644Sbostic pr_retip((struct ip *)icp->icmp_data); 77138643Sbostic #endif 77238643Sbostic break; 77338643Sbostic case ICMP_SOURCEQUENCH: 774*38644Sbostic (void)printf("Source Quench\n"); 77538643Sbostic #ifndef icmp_data 776*38644Sbostic pr_retip(&icp->icmp_ip); 77738643Sbostic #else 778*38644Sbostic pr_retip((struct ip *)icp->icmp_data); 77938643Sbostic #endif 78038643Sbostic break; 78138643Sbostic case ICMP_REDIRECT: 782*38644Sbostic switch(icp->icmp_code) { 78338643Sbostic case ICMP_REDIRECT_NET: 784*38644Sbostic (void)printf("Redirect Network"); 78538643Sbostic break; 78638643Sbostic case ICMP_REDIRECT_HOST: 787*38644Sbostic (void)printf("Redirect Host"); 78838643Sbostic break; 78938643Sbostic case ICMP_REDIRECT_TOSNET: 790*38644Sbostic (void)printf("Redirect Type of Service and Network"); 79138643Sbostic break; 79238643Sbostic case ICMP_REDIRECT_TOSHOST: 793*38644Sbostic (void)printf("Redirect Type of Service and Host"); 79438643Sbostic break; 79538643Sbostic default: 796*38644Sbostic (void)printf("Redirect, Bad Code: %d", icp->icmp_code); 79738643Sbostic break; 79838643Sbostic } 799*38644Sbostic (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr); 80038643Sbostic #ifndef icmp_data 801*38644Sbostic pr_retip(&icp->icmp_ip); 80238643Sbostic #else 803*38644Sbostic pr_retip((struct ip *)icp->icmp_data); 80438643Sbostic #endif 80538643Sbostic break; 80638643Sbostic case ICMP_ECHO: 807*38644Sbostic (void)printf("Echo Request\n"); 80838643Sbostic /* XXX ID + Seq + Data */ 80938643Sbostic break; 81038643Sbostic case ICMP_TIMXCEED: 811*38644Sbostic switch(icp->icmp_code) { 81238643Sbostic case ICMP_TIMXCEED_INTRANS: 813*38644Sbostic (void)printf("Time to live exceeded\n"); 81438643Sbostic break; 81538643Sbostic case ICMP_TIMXCEED_REASS: 816*38644Sbostic (void)printf("Frag reassembly time exceeded\n"); 81738643Sbostic break; 81838643Sbostic default: 819*38644Sbostic (void)printf("Time exceeded, Bad Code: %d\n", 820*38644Sbostic icp->icmp_code); 82138643Sbostic break; 82238643Sbostic } 82338643Sbostic #ifndef icmp_data 824*38644Sbostic pr_retip(&icp->icmp_ip); 82538643Sbostic #else 826*38644Sbostic pr_retip((struct ip *)icp->icmp_data); 82738643Sbostic #endif 82838643Sbostic break; 82938643Sbostic case ICMP_PARAMPROB: 830*38644Sbostic (void)printf("Parameter problem: pointer = 0x%02x\n", 831*38644Sbostic icp->icmp_hun.ih_pptr); 83238643Sbostic #ifndef icmp_data 833*38644Sbostic pr_retip(&icp->icmp_ip); 83438643Sbostic #else 835*38644Sbostic pr_retip((struct ip *)icp->icmp_data); 83638643Sbostic #endif 83738643Sbostic break; 83838643Sbostic case ICMP_TSTAMP: 839*38644Sbostic (void)printf("Timestamp\n"); 84038643Sbostic /* XXX ID + Seq + 3 timestamps */ 84138643Sbostic break; 84238643Sbostic case ICMP_TSTAMPREPLY: 843*38644Sbostic (void)printf("Timestamp Reply\n"); 84438643Sbostic /* XXX ID + Seq + 3 timestamps */ 84538643Sbostic break; 84638643Sbostic case ICMP_IREQ: 847*38644Sbostic (void)printf("Information Request\n"); 84838643Sbostic /* XXX ID + Seq */ 84938643Sbostic break; 85038643Sbostic case ICMP_IREQREPLY: 851*38644Sbostic (void)printf("Information Reply\n"); 85238643Sbostic /* XXX ID + Seq */ 85338643Sbostic break; 85438643Sbostic #ifdef ICMP_MASKREQ 85538643Sbostic case ICMP_MASKREQ: 856*38644Sbostic (void)printf("Address Mask Request\n"); 85738643Sbostic break; 85838643Sbostic #endif 85938643Sbostic #ifdef ICMP_MASKREPLY 86038643Sbostic case ICMP_MASKREPLY: 861*38644Sbostic (void)printf("Address Mask Reply\n"); 86238643Sbostic break; 86338643Sbostic #endif 86438643Sbostic default: 865*38644Sbostic (void)printf("Bad ICMP type: %d\n", icp->icmp_type); 86638643Sbostic } 86738643Sbostic } 86838643Sbostic 86938643Sbostic /* 870*38644Sbostic * pr_iph -- 871*38644Sbostic * Print an IP header with options. 87238643Sbostic */ 873*38644Sbostic pr_iph(ip) 874*38644Sbostic struct ip *ip; 87538643Sbostic { 876*38644Sbostic int hlen; 877*38644Sbostic u_char *cp; 87838643Sbostic 87938643Sbostic hlen = ip->ip_hl << 2; 880*38644Sbostic cp = (u_char *)ip + 20; /* point to options */ 88138643Sbostic 882*38644Sbostic (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); 883*38644Sbostic (void)printf(" %1x %1x %02x %04x %04x", 884*38644Sbostic ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); 885*38644Sbostic (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13, 886*38644Sbostic (ip->ip_off) & 0x1fff); 887*38644Sbostic (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum); 888*38644Sbostic (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); 889*38644Sbostic (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); 89038643Sbostic /* dump and option bytes */ 891*38644Sbostic while (hlen-- > 20) { 892*38644Sbostic (void)printf("%02x", *cp++); 89338643Sbostic } 894*38644Sbostic (void)putchar('\n'); 89538643Sbostic } 89638643Sbostic 89738643Sbostic /* 898*38644Sbostic * pr_addr -- 899*38644Sbostic * Return an ascii host address as a dotted quad and optionally with 900*38644Sbostic * a hostname. 90138643Sbostic */ 90238643Sbostic char * 903*38644Sbostic pr_addr(l) 904*38644Sbostic u_long l; 90538643Sbostic { 906*38644Sbostic struct hostent *hp; 907*38644Sbostic static char buf[80]; 90838643Sbostic 909*38644Sbostic if ((options & F_NUMERIC) || 910*38644Sbostic !(hp = gethostbyaddr((char *)&l, 4, AF_INET))) 911*38644Sbostic (void)sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&l)); 91238643Sbostic else 913*38644Sbostic (void)sprintf(buf, "%s (%s)", hp->h_name, 914*38644Sbostic inet_ntoa(*(struct in_addr *)&l)); 915*38644Sbostic return(buf); 91638643Sbostic } 91738643Sbostic 91838643Sbostic /* 919*38644Sbostic * pr_retip -- 920*38644Sbostic * Dump some info on a returned (via ICMP) IP packet. 92138643Sbostic */ 922*38644Sbostic pr_retip(ip) 923*38644Sbostic struct ip *ip; 92438643Sbostic { 925*38644Sbostic int hlen; 926*38644Sbostic u_char *cp; 92738643Sbostic 928*38644Sbostic pr_iph(ip); 92938643Sbostic hlen = ip->ip_hl << 2; 930*38644Sbostic cp = (u_char *)ip + hlen; 93138643Sbostic 932*38644Sbostic if (ip->ip_p == 6) 933*38644Sbostic (void)printf("TCP: from port %u, to port %u (decimal)\n", 934*38644Sbostic (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); 935*38644Sbostic else if (ip->ip_p == 17) 936*38644Sbostic (void)printf("UDP: from port %u, to port %u (decimal)\n", 937*38644Sbostic (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); 93838643Sbostic } 93938643Sbostic 94038643Sbostic fill(bp, patp) 941*38644Sbostic char *bp, *patp; 94238643Sbostic { 943*38644Sbostic register int ii, jj, kk; 944*38644Sbostic int pat[16]; 945*38644Sbostic char *cp; 94638643Sbostic 947*38644Sbostic for (cp = patp; *cp; cp++) 948*38644Sbostic if (!isxdigit(*cp)) { 949*38644Sbostic (void)fprintf(stderr, 950*38644Sbostic "ping: patterns must be specified as hex digits.\n"); 951*38644Sbostic exit(1); 952*38644Sbostic } 953*38644Sbostic ii = sscanf(patp, 954*38644Sbostic "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", 955*38644Sbostic &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], 956*38644Sbostic &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], 957*38644Sbostic &pat[13], &pat[14], &pat[15]); 95838643Sbostic 959*38644Sbostic if (ii > 0) 960*38644Sbostic for (kk = 0; kk <= MAXPACKET - (8 + ii); kk += ii) 961*38644Sbostic for (jj = 0; jj < ii; ++jj) 962*38644Sbostic bp[jj + kk] = pat[jj]; 963*38644Sbostic if (!(options & F_QUIET)) { 964*38644Sbostic (void)printf("PATTERN: 0x"); 965*38644Sbostic for (jj = 0; jj < ii; ++jj) 966*38644Sbostic (void)printf("%02x", bp[jj] & 0xFF); 967*38644Sbostic (void)printf("\n"); 968*38644Sbostic } 969*38644Sbostic } 97038643Sbostic 971*38644Sbostic usage() 972*38644Sbostic { 973*38644Sbostic (void)fprintf(stderr, 974*38644Sbostic "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n"); 975*38644Sbostic exit(1); 97638643Sbostic } 977