133675Sbostic /* 238644Sbostic * Copyright (c) 1989 The Regents of the University of California. 338644Sbostic * All rights reserved. 438644Sbostic * 538644Sbostic * This code is derived from software contributed to Berkeley by 638644Sbostic * Mike Muuss. 738644Sbostic * 838644Sbostic * Redistribution and use in source and binary forms are permitted 938644Sbostic * provided that the above copyright notice and this paragraph are 1038644Sbostic * duplicated in all such forms and that any documentation, 1138644Sbostic * advertising materials, and other materials related to such 1238644Sbostic * distribution and use acknowledge that the software was developed 1338644Sbostic * by the University of California, Berkeley. The name of the 1438644Sbostic * University may not be used to endorse or promote products derived 1538644Sbostic * from this software without specific prior written permission. 1638644Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1738644Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1838644Sbostic * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1938644Sbostic */ 2038644Sbostic 2138644Sbostic #ifndef lint 2238644Sbostic char copyright[] = 2338644Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 2438644Sbostic All rights reserved.\n"; 2538644Sbostic #endif /* not lint */ 2638644Sbostic 2738644Sbostic #ifndef lint 28*38646Sbostic static char sccsid[] = "@(#)ping.c 5.2 (Berkeley) 08/16/89"; 2938644Sbostic #endif /* not lint */ 3038644Sbostic 3138644Sbostic /* 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> 5238644Sbostic #include <sys/time.h> 5338644Sbostic #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> 6038644Sbostic #include <netdb.h> 6138644Sbostic #include <unistd.h> 6238644Sbostic #include <stdio.h> 6338643Sbostic #include <ctype.h> 6438644Sbostic #include <errno.h> 6538644Sbostic #include <strings.h> 6619057Skarels 6738644Sbostic #define DEFDATALEN (64 - 8) /* default data length */ 6838644Sbostic #define MAXIPLEN 60 6938644Sbostic #define MAXICMPLEN 76 7038644Sbostic #define MAXPACKET (65536 - 60 - 8)/* max packet size */ 7138644Sbostic #define MAXWAIT 10 /* max seconds to wait for response */ 7238644Sbostic #define NROUTES 9 /* number of record route slots */ 7319057Skarels 7438644Sbostic #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ 7538644Sbostic #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ 7638644Sbostic #define SET(bit) (A(bit) |= B(bit)) 7738644Sbostic #define CLR(bit) (A(bit) &= (~B(bit))) 7838644Sbostic #define TST(bit) (A(bit) & B(bit)) 7938643Sbostic 8038644Sbostic /* various options */ 8138644Sbostic int options; 8238644Sbostic #define F_FLOOD 0x001 8338644Sbostic #define F_INTERVAL 0x002 8438644Sbostic #define F_NUMERIC 0x004 8538644Sbostic #define F_PINGFILLED 0x008 8638644Sbostic #define F_QUIET 0x010 8738644Sbostic #define F_RROUTE 0x020 8838644Sbostic #define F_SO_DEBUG 0x040 8938644Sbostic #define F_SO_DONTROUTE 0x080 9038644Sbostic #define F_VERBOSE 0x100 9138643Sbostic 9238644Sbostic /* 9338644Sbostic * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum 9438644Sbostic * number of received sequence numbers we can keep track of. Change 128 9538644Sbostic * to 8192 for complete accuracy... 9638644Sbostic */ 9738644Sbostic #define MAX_DUP_CHK (8 * 128) 9838644Sbostic int mx_dup_ck = MAX_DUP_CHK; 9938644Sbostic char rcvd_tbl[MAX_DUP_CHK / 8]; 10038643Sbostic 10138644Sbostic struct sockaddr whereto; /* who to ping */ 10238644Sbostic int datalen = DEFDATALEN; 10338644Sbostic int s; /* socket file descriptor */ 10438644Sbostic u_char outpack[MAXPACKET]; 10538644Sbostic char BSPACE = '\b'; /* characters written for flood */ 10638644Sbostic char DOT = '.'; 10719057Skarels char *hostname; 10838644Sbostic int ident; /* process id to identify our packets */ 10919057Skarels 11038644Sbostic /* counters */ 11138644Sbostic long npackets; /* max packets to transmit */ 11238644Sbostic long nreceived; /* # of packets we got back */ 11338644Sbostic long nrepeats; /* number of duplicates */ 11438644Sbostic long ntransmitted; /* sequence # for outbound packets = #sent */ 11538644Sbostic int interval = 1; /* interval between packets */ 11638643Sbostic 11738644Sbostic /* timing */ 11838644Sbostic int timing; /* flag to do timing */ 11938644Sbostic long tmin = LONG_MAX; /* minimum round trip time */ 12038644Sbostic long tmax; /* maximum round trip time */ 12138644Sbostic u_long tsum; /* sum of all times, for doing average */ 12219057Skarels 12338643Sbostic u_long inet_addr(); 12438644Sbostic char *inet_ntoa(), *pr_addr(); 125*38646Sbostic int catcher(), finish(); 12619057Skarels 12719057Skarels main(argc, argv) 12838644Sbostic int argc; 12938644Sbostic char **argv; 13019057Skarels { 13138644Sbostic extern int errno, optind; 13238644Sbostic extern char *optarg; 13338644Sbostic struct timeval timeout; 13438644Sbostic struct hostent *hp; 13538644Sbostic struct sockaddr_in *to; 13624199Skarels struct protoent *proto; 13738644Sbostic register int i; 13838644Sbostic int ch, fdmask, hold, packlen, preload; 13938644Sbostic u_char *datap, *packet; 14038644Sbostic char *target, hnamebuf[MAXHOSTNAMELEN], *malloc(); 14138644Sbostic #ifdef IP_OPTIONS 14238644Sbostic char rspace[3 + 4 * NROUTES + 1]; /* record route space */ 14338644Sbostic #endif 14419057Skarels 14538644Sbostic preload = 0; 14638644Sbostic datap = &outpack[8 + sizeof(struct timeval)]; 14738644Sbostic while ((ch = getopt(argc, argv, "Rc:dfh:i:l:np:qrs:v")) != EOF) 14838644Sbostic switch(ch) { 14938644Sbostic case 'c': 15038644Sbostic npackets = atoi(optarg); 15138644Sbostic if (npackets <= 0) { 15238644Sbostic (void)fprintf(stderr, 15338644Sbostic "ping: bad number of packets to transmit.\n"); 15438644Sbostic exit(1); 15538644Sbostic } 15638644Sbostic break; 15738644Sbostic case 'd': 15838644Sbostic options |= F_SO_DEBUG; 15938644Sbostic break; 16038644Sbostic case 'f': 16138644Sbostic if (getuid()) { 16238644Sbostic (void)fprintf(stderr, 16338644Sbostic "ping: you must be root to use the -f option.\n"); 16438644Sbostic exit(1); 16538644Sbostic } 16638644Sbostic options |= F_FLOOD; 16738644Sbostic setbuf(stdout, (char *)NULL); 16838644Sbostic break; 16938644Sbostic case 'i': /* wait between sending packets */ 17038644Sbostic interval = atoi(optarg); 17138644Sbostic if (interval <= 0) { 17238644Sbostic (void)fprintf(stderr, 17338644Sbostic "ping: bad timing interval.\n"); 17438644Sbostic exit(1); 17538644Sbostic } 17638644Sbostic options |= F_INTERVAL; 17738644Sbostic break; 17838644Sbostic case 'l': 17938644Sbostic preload = atoi(optarg); 18038644Sbostic if (preload < 0) { 18138644Sbostic (void)fprintf(stderr, 18238644Sbostic "ping: bad preload value.\n"); 18338644Sbostic exit(1); 18438644Sbostic } 18538644Sbostic break; 18638644Sbostic case 'n': 18738644Sbostic options |= F_NUMERIC; 18838644Sbostic break; 18938644Sbostic case 'p': /* fill buffer with user pattern */ 19038644Sbostic options |= F_PINGFILLED; 19138644Sbostic fill((char *)datap, optarg); 19238643Sbostic break; 19338644Sbostic case 'q': 19438644Sbostic options |= F_QUIET; 19538644Sbostic break; 19638644Sbostic case 'R': 19738644Sbostic options |= F_RROUTE; 19838644Sbostic break; 19938644Sbostic case 'r': 20038644Sbostic options |= F_SO_DONTROUTE; 20138644Sbostic break; 20238644Sbostic case 's': /* size of packet to send */ 20338644Sbostic datalen = atoi(optarg); 20438644Sbostic if (datalen > MAXPACKET) { 20538644Sbostic (void)fprintf(stderr, 20638644Sbostic "ping: packet size too large.\n"); 20738643Sbostic exit(1); 20838644Sbostic } 20938644Sbostic if (datalen <= 0) { 21038644Sbostic (void)fprintf(stderr, 21138644Sbostic "ping: illegal packet size.\n"); 21238644Sbostic exit(1); 21338644Sbostic } 21438644Sbostic break; 21538644Sbostic case 'v': 21638644Sbostic options |= F_VERBOSE; 21738644Sbostic break; 21838644Sbostic default: 21938644Sbostic usage(); 22019064Skarels } 22138644Sbostic argc -= optind; 22238644Sbostic argv += optind; 22338643Sbostic 22438644Sbostic if (argc != 1) 22538644Sbostic usage(); 22638644Sbostic target = *argv; 22719057Skarels 22838644Sbostic bzero((char *)&whereto, sizeof(struct sockaddr)); 22938644Sbostic to = (struct sockaddr_in *)&whereto; 23024199Skarels to->sin_family = AF_INET; 23138644Sbostic to->sin_addr.s_addr = inet_addr(target); 23238644Sbostic if (to->sin_addr.s_addr != (u_int)-1) 23338644Sbostic hostname = target; 23438644Sbostic else { 23538644Sbostic hp = gethostbyname(target); 23638644Sbostic if (!hp) { 23738644Sbostic (void)fprintf(stderr, 23838644Sbostic "ping: unknown host %s\n", target); 23924199Skarels exit(1); 24019057Skarels } 24138644Sbostic to->sin_family = hp->h_addrtype; 24238644Sbostic bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length); 24338644Sbostic (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1); 24438644Sbostic hostname = hnamebuf; 24519057Skarels } 24619057Skarels 24738644Sbostic if (options & F_FLOOD && options & F_INTERVAL) { 24838644Sbostic (void)fprintf(stderr, 24938644Sbostic "ping: -f and -i incompatible options.\n"); 25038643Sbostic exit(1); 25138643Sbostic } 25238643Sbostic 25338644Sbostic if (datalen >= sizeof(struct timeval)) /* can we time transfer */ 25419064Skarels timing = 1; 25538644Sbostic packlen = datalen + MAXIPLEN + MAXICMPLEN; 25638644Sbostic if (!(packet = (u_char *)malloc((u_int)packlen))) { 25738644Sbostic (void)fprintf(stderr, "ping: out of memory.\n"); 25838643Sbostic exit(1); 25938643Sbostic } 26038644Sbostic if (!(options & F_PINGFILLED)) 26138644Sbostic for (i = 8; i < datalen; ++i) 26238644Sbostic *datap++ = i; 26319057Skarels 26419057Skarels ident = getpid() & 0xFFFF; 26519057Skarels 26638644Sbostic if (!(proto = getprotobyname("icmp"))) { 26738644Sbostic (void)fprintf(stderr, "ping: unknown protocol icmp.\n"); 26838644Sbostic exit(1); 26924199Skarels } 27024199Skarels if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { 27119057Skarels perror("ping: socket"); 27238644Sbostic exit(1); 27319057Skarels } 27438644Sbostic hold = 1; 27538644Sbostic if (options & F_SO_DEBUG) 27638644Sbostic (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, 27738644Sbostic sizeof(hold)); 27838644Sbostic if (options & F_SO_DONTROUTE) 27938644Sbostic (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, 28038644Sbostic sizeof(hold)); 28138644Sbostic 28238644Sbostic /* record route option */ 28338644Sbostic 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; 28838644Sbostic if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, 28938644Sbostic sizeof(rspace)) < 0) { 29038644Sbostic perror("ping: record route"); 29138644Sbostic exit(1); 29238643Sbostic } 29338643Sbostic #else 29438644Sbostic (void)fprintf(stderr, 29538644Sbostic "ping: record route not available in this implementation.\n"); 29638644Sbostic exit(1); 29738644Sbostic #endif /* IP_OPTIONS */ 29838643Sbostic } 29919057Skarels 30038644Sbostic /* 30138644Sbostic * When pinging the broadcast address, you can get a lot of answers. 30238644Sbostic * Doing something so evil is useful if you are trying to stress the 30338644Sbostic * ethernet, or just want to fill the arp cache to get some stuff for 30438644Sbostic * /etc/ethers. 30538643Sbostic */ 30638644Sbostic hold = 48 * 1024; 30738644Sbostic (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, 30838644Sbostic sizeof(hold)); 30919057Skarels 31038644Sbostic if (to->sin_family == AF_INET) 31138644Sbostic (void)printf("PING %s (%s): %d data bytes\n", hostname, 31238644Sbostic inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr), 31338644Sbostic datalen); 31438644Sbostic else 31538644Sbostic (void)printf("PING %s: %d data bytes\n", hostname, datalen); 31619057Skarels 317*38646Sbostic (void)signal(SIGINT, finish); 31838644Sbostic (void)signal(SIGALRM, catcher); 31938644Sbostic 32038644Sbostic while (preload--) /* fire off them quickies */ 32138643Sbostic pinger(); 32219057Skarels 32338644Sbostic if (options & F_FLOOD) { 32438644Sbostic timeout.tv_sec = 0; 32538644Sbostic timeout.tv_usec = 10000; 32638644Sbostic fdmask = 1 << s; 32738644Sbostic } else 32838644Sbostic catcher(); /* start things going */ 32938643Sbostic 33019057Skarels for (;;) { 33138644Sbostic struct sockaddr_in from; 33238644Sbostic register int cc; 33338644Sbostic int fromlen; 33419057Skarels 33538644Sbostic if (options & F_FLOOD) { 33638643Sbostic pinger(); 33738644Sbostic if (!select(32, (fd_set *)&fdmask, (fd_set *)NULL, 33838644Sbostic (fd_set *)NULL, &timeout)) 33938643Sbostic continue; 34038643Sbostic } 34138644Sbostic fromlen = sizeof(from); 34238644Sbostic if ((cc = recvfrom(s, (char *)packet, packlen, 0, 34338644Sbostic (struct sockaddr *)&from, &fromlen)) < 0) { 34438644Sbostic if (errno == EINTR) 34519057Skarels continue; 34619057Skarels perror("ping: recvfrom"); 34719057Skarels continue; 34819057Skarels } 34938644Sbostic pr_pack((char *)packet, cc, &from); 35019064Skarels if (npackets && nreceived >= npackets) 35138644Sbostic break; 35219057Skarels } 35338644Sbostic finish(); 35438644Sbostic /* NOTREACHED */ 35519057Skarels } 35619057Skarels 35719057Skarels /* 35838644Sbostic * catcher -- 35938644Sbostic * This routine causes another PING to be transmitted, and then 36019057Skarels * schedules another SIGALRM for 1 second from now. 36119057Skarels * 36238644Sbostic * bug -- 36338644Sbostic * Our sense of time will slowly skew (i.e., packets will not be 36438644Sbostic * launched exactly at 1-second intervals). This does not affect the 36538644Sbostic * quality of the delay and loss statistics. 36619057Skarels */ 36719057Skarels catcher() 36819057Skarels { 36919064Skarels int waittime; 37019064Skarels 37119057Skarels pinger(); 37238644Sbostic (void)signal(SIGALRM, catcher); 37338644Sbostic if (!npackets || ntransmitted < npackets) 37438644Sbostic alarm((u_int)interval); 37519064Skarels else { 37619064Skarels if (nreceived) { 37719064Skarels waittime = 2 * tmax / 1000; 37838644Sbostic if (!waittime) 37919064Skarels waittime = 1; 38019064Skarels } else 38119064Skarels waittime = MAXWAIT; 38238644Sbostic (void)signal(SIGALRM, finish); 38338644Sbostic (void)alarm((u_int)waittime); 38419064Skarels } 38519057Skarels } 38619057Skarels 38719057Skarels /* 38838644Sbostic * pinger -- 38938644Sbostic * 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 { 39738644Sbostic register struct icmp *icp; 39838644Sbostic register int cc; 39938644Sbostic int i; 40019057Skarels 40138644Sbostic icp = (struct icmp *)outpack; 40219057Skarels icp->icmp_type = ICMP_ECHO; 40319057Skarels icp->icmp_code = 0; 40419057Skarels icp->icmp_cksum = 0; 40538644Sbostic icp->icmp_seq = ++ntransmitted; 40638644Sbostic icp->icmp_id = ident; /* ID */ 40719057Skarels 40838644Sbostic CLR(icp->icmp_seq % mx_dup_ck); 40938643Sbostic 41019064Skarels if (timing) 41138644Sbostic (void)gettimeofday((struct timeval *)&outpack[8], 41238644Sbostic (struct timezone *)NULL); 41319057Skarels 41438644Sbostic cc = datalen + 8; /* skips ICMP portion */ 41519057Skarels 41638644Sbostic /* compute ICMP checksum here */ 41738644Sbostic icp->icmp_cksum = in_cksum((u_short *)icp, cc); 41819057Skarels 41938644Sbostic i = sendto(s, (char *)outpack, cc, 0, &whereto, 42038644Sbostic sizeof(struct sockaddr)); 42138644Sbostic 42238644Sbostic if (i < 0 || i != cc) { 42338644Sbostic if (i < 0) 42438644Sbostic perror("ping: sendto"); 42538644Sbostic (void)printf("ping: wrote %s %d chars, ret=%d\n", 42638644Sbostic hostname, cc, i); 42719057Skarels } 42838644Sbostic if (options & F_FLOOD) 42938644Sbostic (void)write(STDOUT_FILENO, &DOT, 1); 43019057Skarels } 43119057Skarels 43219057Skarels /* 43338644Sbostic * pr_pack -- 43438644Sbostic * 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 */ 43938644Sbostic pr_pack(buf, cc, from) 44038644Sbostic char *buf; 44138644Sbostic int cc; 44238644Sbostic struct sockaddr_in *from; 44319057Skarels { 44427070Skarels register struct icmp *icp; 44538644Sbostic 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]; 45038644Sbostic struct ip *ip; 45138644Sbostic struct timeval tv, *tp; 45238644Sbostic long triptime; 45338644Sbostic int hlen, dupflag; 45419057Skarels 45538644Sbostic (void)gettimeofday(&tv, (struct timezone *)NULL); 45619057Skarels 45738643Sbostic /* Check the IP header */ 45838644Sbostic ip = (struct ip *)buf; 45927070Skarels hlen = ip->ip_hl << 2; 46038644Sbostic if (cc < hlen + ICMP_MINLEN) { 46138644Sbostic if (options & F_VERBOSE) 46238644Sbostic (void)fprintf(stderr, 46338644Sbostic "ping: packet too short (%d bytes) from %s\n", cc, 46438644Sbostic 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); 47138644Sbostic if (icp->icmp_type == ICMP_ECHOREPLY) { 47238644Sbostic if (icp->icmp_id != ident) 47338643Sbostic return; /* 'Twas not our ECHO */ 47438644Sbostic ++nreceived; 47538643Sbostic if (timing) { 47638643Sbostic #ifndef icmp_data 47738643Sbostic tp = (struct timeval *)&icp->icmp_ip; 47838643Sbostic #else 47938644Sbostic tp = (struct timeval *)icp->icmp_data; 48038643Sbostic #endif 48138644Sbostic tvsub(&tv, tp); 48238644Sbostic triptime = tv.tv_sec * 1000 + (tv.tv_usec / 1000); 48338643Sbostic tsum += triptime; 48438644Sbostic if (triptime < tmin) 48538643Sbostic tmin = triptime; 48638644Sbostic if (triptime > tmax) 48738643Sbostic tmax = triptime; 48819064Skarels } 48938643Sbostic 49038644Sbostic if (TST(icp->icmp_seq % mx_dup_ck)) { 49138644Sbostic ++nrepeats; 49238644Sbostic --nreceived; 49338644Sbostic dupflag = 1; 49438644Sbostic } else { 49538644Sbostic SET(icp->icmp_seq % mx_dup_ck); 49638644Sbostic dupflag = 0; 49738643Sbostic } 49838643Sbostic 49938644Sbostic if (options & F_QUIET) 50038643Sbostic return; 50138643Sbostic 50238644Sbostic if (options & F_FLOOD) 50338644Sbostic (void)write(STDOUT_FILENO, &BSPACE, 1); 50438644Sbostic else { 50538644Sbostic (void)printf("%d bytes from %s: icmp_seq=%u", cc, 50638644Sbostic inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), 50738644Sbostic icp->icmp_seq); 50838644Sbostic (void)printf(" ttl=%d", ip->ip_ttl); 50938643Sbostic if (timing) 51038644Sbostic (void)printf(" time=%ld ms", triptime); 51138644Sbostic if (dupflag) 51238644Sbostic (void)printf(" (DUP!)"); 51338643Sbostic /* check the data */ 51438643Sbostic cp = (u_char*)&icp->icmp_data[8]; 51538644Sbostic dp = &outpack[8 + sizeof(struct timeval)]; 51638644Sbostic for (i = 8; i < datalen; ++i, ++cp, ++dp) { 51738643Sbostic if (*cp != *dp) { 51838644Sbostic (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", 51938644Sbostic i, *dp, *cp); 52038643Sbostic cp = (u_char*)&icp->icmp_data[0]; 52138644Sbostic for (i = 8; i < datalen; ++i, ++cp) { 52238644Sbostic if ((i % 32) == 8) 52338644Sbostic (void)printf("\n\t"); 52438644Sbostic (void)printf("%x ", *cp); 52538643Sbostic } 52638643Sbostic break; 52738643Sbostic } 52838643Sbostic } 52938643Sbostic } 53038643Sbostic } else { 53138643Sbostic /* We've got something other than an ECHOREPLY */ 53238644Sbostic if (!(options & F_VERBOSE)) 53338643Sbostic return; 53438644Sbostic (void)printf("%d bytes from %s: ", cc, 53538644Sbostic pr_addr(from->sin_addr.s_addr)); 53638644Sbostic pr_icmph(icp); 53719057Skarels } 53819057Skarels 53938643Sbostic /* Display any IP options */ 54038643Sbostic cp = (u_char *)buf + sizeof(struct ip); 54138644Sbostic 54238644Sbostic /* ANSI C will force hlen to unsigned! */ 54338644Sbostic for (; hlen > sizeof(struct ip); --hlen, ++cp) 54438644Sbostic switch (*cp) { 54538643Sbostic case IPOPT_EOL: 54638643Sbostic hlen = 0; 54738643Sbostic break; 54838643Sbostic case IPOPT_LSRR: 54938644Sbostic (void)printf("\nLSRR: "); 55038643Sbostic hlen -= 2; 55138643Sbostic j = *++cp; 55238643Sbostic ++cp; 55338644Sbostic if (j > IPOPT_MINOFF) 55438644Sbostic for (;;) { 55538644Sbostic l = *++cp; 55638644Sbostic l = (l<<8) + *++cp; 55738644Sbostic l = (l<<8) + *++cp; 55838644Sbostic l = (l<<8) + *++cp; 55938644Sbostic if (l == 0) 56038644Sbostic (void)printf("\t0.0.0.0"); 56138643Sbostic else 56238644Sbostic (void)printf("\t%s", pr_addr(ntohl(l))); 56338643Sbostic hlen -= 4; 56438643Sbostic j -= 4; 56538643Sbostic if (j <= IPOPT_MINOFF) 56638643Sbostic break; 56738644Sbostic (void)putchar('\n'); 56838643Sbostic } 56938643Sbostic break; 57038643Sbostic case IPOPT_RR: 57138644Sbostic j = *++cp; /* get length */ 57238644Sbostic i = *++cp; /* and pointer */ 57338643Sbostic hlen -= 2; 57438644Sbostic if (i > j) 57538644Sbostic 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) 58238644Sbostic && !(options & F_FLOOD)) { 58338644Sbostic (void)printf("\t(same route)"); 58438644Sbostic 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); 59138644Sbostic (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) 59838644Sbostic (void)printf("\t0.0.0.0"); 59938643Sbostic else 60038644Sbostic (void)printf("\t%s", pr_addr(ntohl(l))); 60138643Sbostic hlen -= 4; 60238643Sbostic i -= 4; 60338643Sbostic if (i <= 0) 60438643Sbostic break; 60538644Sbostic (void)putchar('\n'); 60638643Sbostic } 60738643Sbostic break; 60838643Sbostic case IPOPT_NOP: 60938644Sbostic (void)printf("\nNOP"); 61038643Sbostic break; 61138643Sbostic default: 61238644Sbostic (void)printf("\nunknown option %x", *cp); 61338643Sbostic break; 61438643Sbostic } 61538644Sbostic if (!(options & F_FLOOD)) { 61638644Sbostic (void)putchar('\n'); 61738644Sbostic (void)fflush(stdout); 61838643Sbostic } 61919057Skarels } 62019057Skarels 62119057Skarels /* 62238644Sbostic * in_cksum -- 62338644Sbostic * Checksum routine for Internet Protocol family headers (C Version) 62419057Skarels */ 62519057Skarels in_cksum(addr, len) 62638644Sbostic u_short *addr; 62738644Sbostic 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 /* 63538644Sbostic * Our algorithm is simple, using a 32 bit accumulator (sum), we add 63638644Sbostic * sequential 16 bit words to it, and at the end, fold back all the 63738644Sbostic * carry bits from the top 16 bits into the lower 16 bits. 63819057Skarels */ 63938644Sbostic while (nleft > 1) { 64019057Skarels sum += *w++; 64119057Skarels nleft -= 2; 64219057Skarels } 64324199Skarels 64424199Skarels /* mop up an odd byte, if necessary */ 64538644Sbostic if (nleft == 1) { 64638643Sbostic *(u_char *)(&answer) = *(u_char *)w ; 64738643Sbostic sum += answer; 64832208Sbostic } 64919057Skarels 65038644Sbostic /* 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 */ 65438644Sbostic return(answer); 65519057Skarels } 65619057Skarels 65719057Skarels /* 65838644Sbostic * tvsub -- 65938644Sbostic * Subtract 2 timeval structs: out = out - in. Out is assumed to 66038644Sbostic * be >= in. 66119057Skarels */ 66238644Sbostic tvsub(out, in) 66338644Sbostic register struct timeval *out, *in; 66419057Skarels { 66538644Sbostic if ((out->tv_usec -= in->tv_usec) < 0) { 66638644Sbostic --out->tv_sec; 66719057Skarels out->tv_usec += 1000000; 66819057Skarels } 66919057Skarels out->tv_sec -= in->tv_sec; 67019057Skarels } 67119057Skarels 67238644Sbostic /* 67338644Sbostic * finish -- 67438644Sbostic * Print out statistics, and give up. 67519057Skarels */ 67619057Skarels finish() 67719057Skarels { 678*38646Sbostic (void)signal(SIGINT, SIG_IGN); 67938644Sbostic (void)putchar('\n'); 68038644Sbostic (void)fflush(stdout); 68138644Sbostic (void)printf("--- %s ping statistics ---\n", hostname); 68238644Sbostic (void)printf("%ld packets transmitted, ", ntransmitted); 68338644Sbostic (void)printf("%ld packets received, ", nreceived); 68438644Sbostic if (nrepeats) 68538644Sbostic (void)printf("+%ld duplicates, ", nrepeats); 68638643Sbostic if (ntransmitted) 68738644Sbostic if (nreceived > ntransmitted) 68838644Sbostic (void)printf("-- somebody's printing up packets!"); 68938029Skarels else 69038644Sbostic (void)printf("%d%% packet loss", 69138644Sbostic (int) (((ntransmitted - nreceived) * 100) / 69238644Sbostic ntransmitted)); 69338644Sbostic (void)putchar('\n'); 69419064Skarels if (nreceived && timing) 69538644Sbostic (void)printf("round-trip min/avg/max = %ld/%lu/%ld ms\n", 69638644Sbostic tmin, tsum / (nreceived + nrepeats), tmax); 69719057Skarels exit(0); 69819057Skarels } 69938643Sbostic 70038644Sbostic #ifdef notdef 70138643Sbostic static char *ttab[] = { 70238643Sbostic "Echo Reply", /* ip + seq + udata */ 70338643Sbostic "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ 70438643Sbostic "Source Quench", /* IP */ 70538643Sbostic "Redirect", /* redirect type, gateway, + IP */ 70638643Sbostic "Echo", 70738643Sbostic "Time Exceeded", /* transit, frag reassem + IP */ 70838643Sbostic "Parameter Problem", /* pointer + IP */ 70938643Sbostic "Timestamp", /* id + seq + three timestamps */ 71038643Sbostic "Timestamp Reply", /* " */ 71138643Sbostic "Info Request", /* id + sq */ 71238643Sbostic "Info Reply" /* " */ 71338643Sbostic }; 71438644Sbostic #endif 71538643Sbostic 71638643Sbostic /* 71738644Sbostic * pr_icmph -- 71838644Sbostic * Print a descriptive string about an ICMP header. 71938643Sbostic */ 72038644Sbostic pr_icmph(icp) 72138644Sbostic struct icmp *icp; 72238643Sbostic { 72338644Sbostic switch(icp->icmp_type) { 72438643Sbostic case ICMP_ECHOREPLY: 72538644Sbostic (void)printf("Echo Reply\n"); 72638643Sbostic /* XXX ID + Seq + Data */ 72738643Sbostic break; 72838643Sbostic case ICMP_UNREACH: 72938644Sbostic switch(icp->icmp_code) { 73038643Sbostic case ICMP_UNREACH_NET: 73138644Sbostic (void)printf("Destination Net Unreachable\n"); 73238643Sbostic break; 73338643Sbostic case ICMP_UNREACH_HOST: 73438644Sbostic (void)printf("Destination Host Unreachable\n"); 73538643Sbostic break; 73638643Sbostic case ICMP_UNREACH_PROTOCOL: 73738644Sbostic (void)printf("Destination Protocol Unreachable\n"); 73838643Sbostic break; 73938643Sbostic case ICMP_UNREACH_PORT: 74038644Sbostic (void)printf("Destination Port Unreachable\n"); 74138643Sbostic break; 74238643Sbostic case ICMP_UNREACH_NEEDFRAG: 74338644Sbostic (void)printf("frag needed and DF set\n"); 74438643Sbostic break; 74538643Sbostic case ICMP_UNREACH_SRCFAIL: 74638644Sbostic (void)printf("Source Route Failed\n"); 74738643Sbostic break; 74838643Sbostic default: 74938644Sbostic (void)printf("Dest Unreachable, Bad Code: %d\n", 75038644Sbostic icp->icmp_code); 75138643Sbostic break; 75238643Sbostic } 75338643Sbostic /* Print returned IP header information */ 75438643Sbostic #ifndef icmp_data 75538644Sbostic pr_retip(&icp->icmp_ip); 75638643Sbostic #else 75738644Sbostic pr_retip((struct ip *)icp->icmp_data); 75838643Sbostic #endif 75938643Sbostic break; 76038643Sbostic case ICMP_SOURCEQUENCH: 76138644Sbostic (void)printf("Source Quench\n"); 76238643Sbostic #ifndef icmp_data 76338644Sbostic pr_retip(&icp->icmp_ip); 76438643Sbostic #else 76538644Sbostic pr_retip((struct ip *)icp->icmp_data); 76638643Sbostic #endif 76738643Sbostic break; 76838643Sbostic case ICMP_REDIRECT: 76938644Sbostic switch(icp->icmp_code) { 77038643Sbostic case ICMP_REDIRECT_NET: 77138644Sbostic (void)printf("Redirect Network"); 77238643Sbostic break; 77338643Sbostic case ICMP_REDIRECT_HOST: 77438644Sbostic (void)printf("Redirect Host"); 77538643Sbostic break; 77638643Sbostic case ICMP_REDIRECT_TOSNET: 77738644Sbostic (void)printf("Redirect Type of Service and Network"); 77838643Sbostic break; 77938643Sbostic case ICMP_REDIRECT_TOSHOST: 78038644Sbostic (void)printf("Redirect Type of Service and Host"); 78138643Sbostic break; 78238643Sbostic default: 78338644Sbostic (void)printf("Redirect, Bad Code: %d", icp->icmp_code); 78438643Sbostic break; 78538643Sbostic } 78638644Sbostic (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr); 78738643Sbostic #ifndef icmp_data 78838644Sbostic pr_retip(&icp->icmp_ip); 78938643Sbostic #else 79038644Sbostic pr_retip((struct ip *)icp->icmp_data); 79138643Sbostic #endif 79238643Sbostic break; 79338643Sbostic case ICMP_ECHO: 79438644Sbostic (void)printf("Echo Request\n"); 79538643Sbostic /* XXX ID + Seq + Data */ 79638643Sbostic break; 79738643Sbostic case ICMP_TIMXCEED: 79838644Sbostic switch(icp->icmp_code) { 79938643Sbostic case ICMP_TIMXCEED_INTRANS: 80038644Sbostic (void)printf("Time to live exceeded\n"); 80138643Sbostic break; 80238643Sbostic case ICMP_TIMXCEED_REASS: 80338644Sbostic (void)printf("Frag reassembly time exceeded\n"); 80438643Sbostic break; 80538643Sbostic default: 80638644Sbostic (void)printf("Time exceeded, Bad Code: %d\n", 80738644Sbostic icp->icmp_code); 80838643Sbostic break; 80938643Sbostic } 81038643Sbostic #ifndef icmp_data 81138644Sbostic pr_retip(&icp->icmp_ip); 81238643Sbostic #else 81338644Sbostic pr_retip((struct ip *)icp->icmp_data); 81438643Sbostic #endif 81538643Sbostic break; 81638643Sbostic case ICMP_PARAMPROB: 81738644Sbostic (void)printf("Parameter problem: pointer = 0x%02x\n", 81838644Sbostic icp->icmp_hun.ih_pptr); 81938643Sbostic #ifndef icmp_data 82038644Sbostic pr_retip(&icp->icmp_ip); 82138643Sbostic #else 82238644Sbostic pr_retip((struct ip *)icp->icmp_data); 82338643Sbostic #endif 82438643Sbostic break; 82538643Sbostic case ICMP_TSTAMP: 82638644Sbostic (void)printf("Timestamp\n"); 82738643Sbostic /* XXX ID + Seq + 3 timestamps */ 82838643Sbostic break; 82938643Sbostic case ICMP_TSTAMPREPLY: 83038644Sbostic (void)printf("Timestamp Reply\n"); 83138643Sbostic /* XXX ID + Seq + 3 timestamps */ 83238643Sbostic break; 83338643Sbostic case ICMP_IREQ: 83438644Sbostic (void)printf("Information Request\n"); 83538643Sbostic /* XXX ID + Seq */ 83638643Sbostic break; 83738643Sbostic case ICMP_IREQREPLY: 83838644Sbostic (void)printf("Information Reply\n"); 83938643Sbostic /* XXX ID + Seq */ 84038643Sbostic break; 84138643Sbostic #ifdef ICMP_MASKREQ 84238643Sbostic case ICMP_MASKREQ: 84338644Sbostic (void)printf("Address Mask Request\n"); 84438643Sbostic break; 84538643Sbostic #endif 84638643Sbostic #ifdef ICMP_MASKREPLY 84738643Sbostic case ICMP_MASKREPLY: 84838644Sbostic (void)printf("Address Mask Reply\n"); 84938643Sbostic break; 85038643Sbostic #endif 85138643Sbostic default: 85238644Sbostic (void)printf("Bad ICMP type: %d\n", icp->icmp_type); 85338643Sbostic } 85438643Sbostic } 85538643Sbostic 85638643Sbostic /* 85738644Sbostic * pr_iph -- 85838644Sbostic * Print an IP header with options. 85938643Sbostic */ 86038644Sbostic pr_iph(ip) 86138644Sbostic struct ip *ip; 86238643Sbostic { 86338644Sbostic int hlen; 86438644Sbostic u_char *cp; 86538643Sbostic 86638643Sbostic hlen = ip->ip_hl << 2; 86738644Sbostic cp = (u_char *)ip + 20; /* point to options */ 86838643Sbostic 86938644Sbostic (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); 87038644Sbostic (void)printf(" %1x %1x %02x %04x %04x", 87138644Sbostic ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); 87238644Sbostic (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13, 87338644Sbostic (ip->ip_off) & 0x1fff); 87438644Sbostic (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum); 87538644Sbostic (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); 87638644Sbostic (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); 87738643Sbostic /* dump and option bytes */ 87838644Sbostic while (hlen-- > 20) { 87938644Sbostic (void)printf("%02x", *cp++); 88038643Sbostic } 88138644Sbostic (void)putchar('\n'); 88238643Sbostic } 88338643Sbostic 88438643Sbostic /* 88538644Sbostic * pr_addr -- 88638644Sbostic * Return an ascii host address as a dotted quad and optionally with 88738644Sbostic * a hostname. 88838643Sbostic */ 88938643Sbostic char * 89038644Sbostic pr_addr(l) 89138644Sbostic u_long l; 89238643Sbostic { 89338644Sbostic struct hostent *hp; 89438644Sbostic static char buf[80]; 89538643Sbostic 89638644Sbostic if ((options & F_NUMERIC) || 89738644Sbostic !(hp = gethostbyaddr((char *)&l, 4, AF_INET))) 89838644Sbostic (void)sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&l)); 89938643Sbostic else 90038644Sbostic (void)sprintf(buf, "%s (%s)", hp->h_name, 90138644Sbostic inet_ntoa(*(struct in_addr *)&l)); 90238644Sbostic return(buf); 90338643Sbostic } 90438643Sbostic 90538643Sbostic /* 90638644Sbostic * pr_retip -- 90738644Sbostic * Dump some info on a returned (via ICMP) IP packet. 90838643Sbostic */ 90938644Sbostic pr_retip(ip) 91038644Sbostic struct ip *ip; 91138643Sbostic { 91238644Sbostic int hlen; 91338644Sbostic u_char *cp; 91438643Sbostic 91538644Sbostic pr_iph(ip); 91638643Sbostic hlen = ip->ip_hl << 2; 91738644Sbostic cp = (u_char *)ip + hlen; 91838643Sbostic 91938644Sbostic if (ip->ip_p == 6) 92038644Sbostic (void)printf("TCP: from port %u, to port %u (decimal)\n", 92138644Sbostic (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); 92238644Sbostic else if (ip->ip_p == 17) 92338644Sbostic (void)printf("UDP: from port %u, to port %u (decimal)\n", 92438644Sbostic (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); 92538643Sbostic } 92638643Sbostic 92738643Sbostic fill(bp, patp) 92838644Sbostic char *bp, *patp; 92938643Sbostic { 93038644Sbostic register int ii, jj, kk; 93138644Sbostic int pat[16]; 93238644Sbostic char *cp; 93338643Sbostic 93438644Sbostic for (cp = patp; *cp; cp++) 93538644Sbostic if (!isxdigit(*cp)) { 93638644Sbostic (void)fprintf(stderr, 93738644Sbostic "ping: patterns must be specified as hex digits.\n"); 93838644Sbostic exit(1); 93938644Sbostic } 94038644Sbostic ii = sscanf(patp, 94138644Sbostic "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", 94238644Sbostic &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], 94338644Sbostic &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], 94438644Sbostic &pat[13], &pat[14], &pat[15]); 94538643Sbostic 94638644Sbostic if (ii > 0) 94738644Sbostic for (kk = 0; kk <= MAXPACKET - (8 + ii); kk += ii) 94838644Sbostic for (jj = 0; jj < ii; ++jj) 94938644Sbostic bp[jj + kk] = pat[jj]; 95038644Sbostic if (!(options & F_QUIET)) { 95138644Sbostic (void)printf("PATTERN: 0x"); 95238644Sbostic for (jj = 0; jj < ii; ++jj) 95338644Sbostic (void)printf("%02x", bp[jj] & 0xFF); 95438644Sbostic (void)printf("\n"); 95538644Sbostic } 95638644Sbostic } 95738643Sbostic 95838644Sbostic usage() 95938644Sbostic { 96038644Sbostic (void)fprintf(stderr, 96138644Sbostic "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n"); 96238644Sbostic exit(1); 96338643Sbostic } 964