ping.c revision 8871
167754Smsmith/* 267754Smsmith * Copyright (c) 1989, 1993 3167802Sjkim * The Regents of the University of California. All rights reserved. 467754Smsmith * 567754Smsmith * This code is derived from software contributed to Berkeley by 667754Smsmith * Mike Muuss. 767754Smsmith * 867754Smsmith * Redistribution and use in source and binary forms, with or without 967754Smsmith * modification, are permitted provided that the following conditions 1067754Smsmith * are met: 11193267Sjkim * 1. Redistributions of source code must retain the above copyright 1270243Smsmith * notice, this list of conditions and the following disclaimer. 1367754Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1467754Smsmith * notice, this list of conditions and the following disclaimer in the 1567754Smsmith * documentation and/or other materials provided with the distribution. 1667754Smsmith * 3. All advertising materials mentioning features or use of this software 1767754Smsmith * must display the following acknowledgement: 1867754Smsmith * This product includes software developed by the University of 1967754Smsmith * California, Berkeley and its contributors. 2067754Smsmith * 4. Neither the name of the University nor the names of its contributors 2167754Smsmith * may be used to endorse or promote products derived from this software 2267754Smsmith * without specific prior written permission. 2367754Smsmith * 2467754Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2567754Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2667754Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2767754Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2867754Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2967754Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3067754Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3167754Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3267754Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3367754Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3467754Smsmith * SUCH DAMAGE. 3567754Smsmith */ 3667754Smsmith 3767754Smsmith#ifndef lint 3867754Smsmithstatic char copyright[] = 3967754Smsmith"@(#) Copyright (c) 1989, 1993\n\ 4067754Smsmith The Regents of the University of California. All rights reserved.\n"; 4167754Smsmith#endif /* not lint */ 4267754Smsmith 4367754Smsmith#ifndef lint 4467754Smsmithstatic char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93"; 4567754Smsmith#endif /* not lint */ 4667754Smsmith 4767754Smsmith/* 4867754Smsmith * P I N G . C 4967754Smsmith * 5067754Smsmith * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, 5167754Smsmith * measure round-trip-delays and packet loss across network paths. 5267754Smsmith * 5367754Smsmith * Author - 5467754Smsmith * Mike Muuss 5567754Smsmith * U. S. Army Ballistic Research Laboratory 5667754Smsmith * December, 1983 5767754Smsmith * 5867754Smsmith * Status - 5967754Smsmith * Public Domain. Distribution Unlimited. 6067754Smsmith * Bugs - 6167754Smsmith * More statistics could always be gathered. 6267754Smsmith * This program has to run SUID to ROOT to access the ICMP socket. 6367754Smsmith */ 6467754Smsmith 6567754Smsmith#include <sys/param.h> 6667754Smsmith#include <sys/socket.h> 6767754Smsmith#include <sys/file.h> 6867754Smsmith#include <sys/time.h> 6967754Smsmith#include <sys/signal.h> 7067754Smsmith#include <termios.h> 7167754Smsmith 7267754Smsmith#include <netinet/in_systm.h> 7367754Smsmith#include <netinet/in.h> 7467754Smsmith#include <netinet/ip.h> 7567754Smsmith#include <netinet/ip_icmp.h> 7667754Smsmith#include <netinet/ip_var.h> 7767754Smsmith#include <netdb.h> 7867754Smsmith#include <unistd.h> 7967754Smsmith#include <stdio.h> 8067754Smsmith#include <ctype.h> 8167754Smsmith#include <errno.h> 8267754Smsmith#include <string.h> 8367754Smsmith 8467754Smsmith#define DEFDATALEN (64 - 8) /* default data length */ 8567754Smsmith#define MAXIPLEN 60 8667754Smsmith#define MAXICMPLEN 76 8767754Smsmith#define MAXPACKET (65536 - 60 - 8)/* max packet size */ 8867754Smsmith#define MAXWAIT 10 /* max seconds to wait for response */ 8967754Smsmith#define NROUTES 9 /* number of record route slots */ 9067754Smsmith 9167754Smsmith#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ 9267754Smsmith#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ 9367754Smsmith#define SET(bit) (A(bit) |= B(bit)) 9467754Smsmith#define CLR(bit) (A(bit) &= (~B(bit))) 9567754Smsmith#define TST(bit) (A(bit) & B(bit)) 9667754Smsmith 9767754Smsmith/* various options */ 9867754Smsmithint options; 9967754Smsmith#define F_FLOOD 0x001 10067754Smsmith#define F_INTERVAL 0x002 10167754Smsmith#define F_NUMERIC 0x004 10267754Smsmith#define F_PINGFILLED 0x008 10367754Smsmith#define F_QUIET 0x010 10467754Smsmith#define F_RROUTE 0x020 10567754Smsmith#define F_SO_DEBUG 0x040 10667754Smsmith#define F_SO_DONTROUTE 0x080 10767754Smsmith#define F_VERBOSE 0x100 10867754Smsmith 10967754Smsmith/* 11067754Smsmith * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum 11167754Smsmith * number of received sequence numbers we can keep track of. Change 128 11267754Smsmith * to 8192 for complete accuracy... 11367754Smsmith */ 11467754Smsmith#define MAX_DUP_CHK (8 * 128) 11567754Smsmithint mx_dup_ck = MAX_DUP_CHK; 11667754Smsmithchar rcvd_tbl[MAX_DUP_CHK / 8]; 11767754Smsmith 118193341Sjkimstruct sockaddr whereto; /* who to ping */ 119193341Sjkimint datalen = DEFDATALEN; 120193341Sjkimint s; /* socket file descriptor */ 12167754Smsmithu_char outpack[MAXPACKET]; 12277424Smsmithchar BSPACE = '\b'; /* characters written for flood */ 12391116Smsmithchar DOT = '.'; 12467754Smsmithchar *hostname; 125151937Sjkimint ident; /* process id to identify our packets */ 12667754Smsmith 127197104Sjkim/* counters */ 128197104Sjkimlong npackets; /* max packets to transmit */ 129197104Sjkimlong nreceived; /* # of packets we got back */ 130197104Sjkimlong nrepeats; /* number of duplicates */ 131197104Sjkimlong ntransmitted; /* sequence # for outbound packets = #sent */ 132197104Sjkimint interval = 1; /* interval between packets */ 133197104Sjkim 134197104Sjkim/* timing */ 135197104Sjkimint timing; /* flag to do timing */ 136197104Sjkimdouble tmin = 999999999.0; /* minimum round trip time */ 137167802Sjkimdouble tmax = 0.0; /* maximum round trip time */ 138167802Sjkimdouble tsum = 0.0; /* sum of all times, for doing average */ 139167802Sjkim 140193267Sjkimint reset_kerninfo; 141151937Sjkim 142151937Sjkimchar *pr_addr(); 14367754Smsmithvoid catcher(), finish(), status(); 14467754Smsmith 145193267Sjkimmain(argc, argv) 146193267Sjkim int argc; 147193267Sjkim char **argv; 148193267Sjkim{ 149193267Sjkim extern int errno, optind; 150193267Sjkim extern char *optarg; 151193267Sjkim struct timeval timeout; 152193267Sjkim struct hostent *hp; 153193267Sjkim struct sockaddr_in *to; 154193267Sjkim struct protoent *proto; 155193267Sjkim struct termios ts; 156193267Sjkim register int i; 157193267Sjkim int ch, fdmask, hold, packlen, preload; 158193267Sjkim u_char *datap, *packet; 159193267Sjkim char *target, hnamebuf[MAXHOSTNAMELEN], *malloc(); 160193267Sjkim#ifdef IP_OPTIONS 161193267Sjkim char rspace[3 + 4 * NROUTES + 1]; /* record route space */ 162193267Sjkim#endif 163193267Sjkim 164193267Sjkim preload = 0; 165193267Sjkim if (tcgetattr (0, &ts) != -1) { 166193267Sjkim reset_kerninfo = !(ts.c_lflag & NOKERNINFO); 167193267Sjkim ts.c_lflag |= NOKERNINFO; 168193267Sjkim tcsetattr (0, TCSANOW, &ts); 169193267Sjkim } 170193267Sjkim 171167802Sjkim datap = &outpack[8 + sizeof(struct timeval)]; 17267754Smsmith while ((ch = getopt(argc, argv, "Rc:dfh:i:l:np:qrs:v")) != EOF) 173167802Sjkim switch(ch) { 17467754Smsmith case 'c': 175167802Sjkim npackets = atoi(optarg); 17667754Smsmith if (npackets <= 0) { 177167802Sjkim (void)fprintf(stderr, 178167802Sjkim "ping: bad number of packets to transmit.\n"); 179151937Sjkim exit(1); 18067754Smsmith } 18167754Smsmith break; 182167802Sjkim case 'd': 183167802Sjkim options |= F_SO_DEBUG; 184167802Sjkim break; 18567754Smsmith case 'f': 18667754Smsmith if (getuid()) { 187167802Sjkim (void)fprintf(stderr, 188167802Sjkim "ping: %s\n", strerror(EPERM)); 189167802Sjkim exit(1); 190167802Sjkim } 19183174Smsmith options |= F_FLOOD; 192167802Sjkim setbuf(stdout, (char *)NULL); 193167802Sjkim break; 19467754Smsmith case 'i': /* wait between sending packets */ 19582367Smsmith interval = atoi(optarg); 196167802Sjkim if (interval <= 0) { 197167802Sjkim (void)fprintf(stderr, 198197104Sjkim "ping: bad timing interval.\n"); 199197104Sjkim exit(1); 200197104Sjkim } 201197104Sjkim options |= F_INTERVAL; 202197104Sjkim break; 203197104Sjkim case 'l': 204197104Sjkim preload = atoi(optarg); 205197104Sjkim if (preload < 0) { 206197104Sjkim (void)fprintf(stderr, 207197104Sjkim "ping: bad preload value.\n"); 208197104Sjkim exit(1); 209197104Sjkim } 210197104Sjkim break; 211197104Sjkim case 'n': 212197104Sjkim options |= F_NUMERIC; 213197104Sjkim break; 214197104Sjkim case 'p': /* fill buffer with user pattern */ 215197104Sjkim options |= F_PINGFILLED; 216197104Sjkim fill((char *)datap, optarg); 217197104Sjkim break; 218197104Sjkim case 'q': 219197104Sjkim options |= F_QUIET; 220197104Sjkim break; 221197104Sjkim case 'R': 222197104Sjkim options |= F_RROUTE; 223197104Sjkim break; 224197104Sjkim case 'r': 225197104Sjkim options |= F_SO_DONTROUTE; 226197104Sjkim break; 227197104Sjkim case 's': /* size of packet to send */ 228197104Sjkim datalen = atoi(optarg); 229197104Sjkim if (datalen > MAXPACKET) { 230197104Sjkim (void)fprintf(stderr, 231197104Sjkim "ping: packet size too large.\n"); 232197104Sjkim exit(1); 233197104Sjkim } 234197104Sjkim if (datalen <= 0) { 235197104Sjkim (void)fprintf(stderr, 236197104Sjkim "ping: illegal packet size.\n"); 237197104Sjkim exit(1); 238197104Sjkim } 239197104Sjkim break; 240197104Sjkim case 'v': 241197104Sjkim options |= F_VERBOSE; 242197104Sjkim break; 243197104Sjkim default: 244197104Sjkim usage(); 245197104Sjkim } 246197104Sjkim argc -= optind; 247197104Sjkim argv += optind; 248197104Sjkim 249197104Sjkim if (argc != 1) 250197104Sjkim usage(); 251197104Sjkim target = *argv; 252197104Sjkim 253197104Sjkim bzero((char *)&whereto, sizeof(struct sockaddr)); 254197104Sjkim to = (struct sockaddr_in *)&whereto; 255197104Sjkim to->sin_family = AF_INET; 256197104Sjkim to->sin_addr.s_addr = inet_addr(target); 257197104Sjkim if (to->sin_addr.s_addr != (u_int)-1) 258197104Sjkim hostname = target; 259167802Sjkim else { 260167802Sjkim hp = gethostbyname(target); 261167802Sjkim if (!hp) { 262167802Sjkim (void)fprintf(stderr, 263167802Sjkim "ping: unknown host %s\n", target); 264167802Sjkim exit(1); 265167802Sjkim } 266167802Sjkim to->sin_family = hp->h_addrtype; 267167802Sjkim bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length); 268167802Sjkim (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1); 269151937Sjkim hostname = hnamebuf; 270167802Sjkim } 271167802Sjkim 272167802Sjkim if (options & F_FLOOD && options & F_INTERVAL) { 273167802Sjkim (void)fprintf(stderr, 274167802Sjkim "ping: -f and -i incompatible options.\n"); 275197104Sjkim exit(1); 276151937Sjkim } 277197104Sjkim 278193267Sjkim if (datalen >= sizeof(struct timeval)) /* can we time transfer */ 279193267Sjkim timing = 1; 280193267Sjkim packlen = datalen + MAXIPLEN + MAXICMPLEN; 281193267Sjkim if (!(packet = (u_char *)malloc((u_int)packlen))) { 282167802Sjkim (void)fprintf(stderr, "ping: out of memory.\n"); 283167802Sjkim exit(1); 284193267Sjkim } 285151937Sjkim if (!(options & F_PINGFILLED)) 286193267Sjkim for (i = 8; i < datalen; ++i) 287193267Sjkim *datap++ = i; 288193267Sjkim 289167802Sjkim ident = getpid() & 0xFFFF; 290167802Sjkim 29167754Smsmith if (!(proto = getprotobyname("icmp"))) { 292167802Sjkim (void)fprintf(stderr, "ping: unknown protocol icmp.\n"); 29367754Smsmith exit(1); 294197104Sjkim } 295197104Sjkim if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { 296197104Sjkim perror("ping: socket"); 297197104Sjkim exit(1); 298193267Sjkim } 299167802Sjkim hold = 1; 300167802Sjkim if (options & F_SO_DEBUG) 301167802Sjkim (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, 302167802Sjkim sizeof(hold)); 303197104Sjkim if (options & F_SO_DONTROUTE) 304167802Sjkim (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, 305167802Sjkim sizeof(hold)); 306167802Sjkim 307167802Sjkim /* record route option */ 308151937Sjkim if (options & F_RROUTE) { 309197104Sjkim#ifdef IP_OPTIONS 310197104Sjkim rspace[IPOPT_OPTVAL] = IPOPT_RR; 311167802Sjkim rspace[IPOPT_OLEN] = sizeof(rspace)-1; 312193267Sjkim rspace[IPOPT_OFFSET] = IPOPT_MINOFF; 313197104Sjkim if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, 314197104Sjkim sizeof(rspace)) < 0) { 315197104Sjkim perror("ping: record route"); 316197104Sjkim exit(1); 31767754Smsmith } 31867754Smsmith#else 31967754Smsmith (void)fprintf(stderr, 32067754Smsmith "ping: record route not available in this implementation.\n"); 32167754Smsmith exit(1); 32267754Smsmith#endif /* IP_OPTIONS */ 323167802Sjkim } 32467754Smsmith 325167802Sjkim /* 326167802Sjkim * When pinging the broadcast address, you can get a lot of answers. 32767754Smsmith * Doing something so evil is useful if you are trying to stress the 32867754Smsmith * ethernet, or just want to fill the arp cache to get some stuff for 32967754Smsmith * /etc/ethers. 330167802Sjkim */ 331167802Sjkim hold = 48 * 1024; 33267754Smsmith (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, 33367754Smsmith sizeof(hold)); 33467754Smsmith 33567754Smsmith if (to->sin_family == AF_INET) 336167802Sjkim (void)printf("PING %s (%s): %d data bytes\n", hostname, 337167802Sjkim inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr), 338167802Sjkim datalen); 33967754Smsmith else 340167802Sjkim (void)printf("PING %s: %d data bytes\n", hostname, datalen); 34167754Smsmith 34267754Smsmith (void)signal(SIGINT, finish); 343167802Sjkim (void)signal(SIGALRM, catcher); 34477424Smsmith (void)signal(SIGINFO, status); 345167802Sjkim 34677424Smsmith while (preload--) /* fire off them quickies */ 347167802Sjkim pinger(); 34867754Smsmith 349167802Sjkim if ((options & F_FLOOD) == 0) 35067754Smsmith catcher(); /* start things going */ 351167802Sjkim 352193267Sjkim for (;;) { 353193267Sjkim struct sockaddr_in from; 354193267Sjkim register int cc; 355151937Sjkim int fromlen; 356167802Sjkim 357167802Sjkim if (options & F_FLOOD) { 358167802Sjkim pinger(); 35967754Smsmith timeout.tv_sec = 0; 36067754Smsmith timeout.tv_usec = 10000; 361167802Sjkim fdmask = 1 << s; 362167802Sjkim if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL, 36367754Smsmith (fd_set *)NULL, &timeout) < 1) 36467754Smsmith continue; 365167802Sjkim } 366167802Sjkim fromlen = sizeof(from); 367167802Sjkim if ((cc = recvfrom(s, (char *)packet, packlen, 0, 368167802Sjkim (struct sockaddr *)&from, &fromlen)) < 0) { 369167802Sjkim if (errno == EINTR) 370167802Sjkim continue; 371167802Sjkim perror("ping: recvfrom"); 372167802Sjkim continue; 373167802Sjkim } 374167802Sjkim pr_pack((char *)packet, cc, &from); 375167802Sjkim if (npackets && nreceived >= npackets) 376167802Sjkim break; 377151937Sjkim } 378167802Sjkim finish(); 379167802Sjkim /* NOTREACHED */ 380167802Sjkim} 381193267Sjkim 382167802Sjkim/* 383167802Sjkim * catcher -- 384167802Sjkim * This routine causes another PING to be transmitted, and then 38567754Smsmith * schedules another SIGALRM for 1 second from now. 38667754Smsmith * 387167802Sjkim * bug -- 38867754Smsmith * Our sense of time will slowly skew (i.e., packets will not be 389167802Sjkim * launched exactly at 1-second intervals). This does not affect the 39067754Smsmith * quality of the delay and loss statistics. 39167754Smsmith */ 392167802Sjkimvoid 39367754Smsmithcatcher() 39467754Smsmith{ 39567754Smsmith int waittime; 39667754Smsmith 39767754Smsmith pinger(); 398167802Sjkim (void)signal(SIGALRM, catcher); 39967754Smsmith if (!npackets || ntransmitted < npackets) 400167802Sjkim alarm((u_int)interval); 401167802Sjkim else { 402167802Sjkim if (nreceived) { 403167802Sjkim waittime = 2 * tmax / 1000; 40467754Smsmith if (!waittime) 405167802Sjkim waittime = 1; 40667754Smsmith } else 407193267Sjkim waittime = MAXWAIT; 408193267Sjkim (void)signal(SIGALRM, finish); 409193267Sjkim (void)alarm((u_int)waittime); 410193267Sjkim } 41167754Smsmith} 41267754Smsmith 41367754Smsmith/* 414167802Sjkim * pinger -- 415167802Sjkim * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet 416167802Sjkim * will be added on by the kernel. The ID field is our UNIX process ID, 417167802Sjkim * and the sequence number is an ascending integer. The first 8 bytes 418193267Sjkim * of the data portion are used to hold a UNIX "timeval" struct in VAX 41967754Smsmith * byte-order, to compute the round-trip time. 420193267Sjkim */ 421193267Sjkimpinger() 422193267Sjkim{ 423193267Sjkim register struct icmp *icp; 424193267Sjkim register int cc; 42567754Smsmith int i; 42667754Smsmith 427167802Sjkim icp = (struct icmp *)outpack; 428167802Sjkim icp->icmp_type = ICMP_ECHO; 429167802Sjkim icp->icmp_code = 0; 430167802Sjkim icp->icmp_cksum = 0; 431167802Sjkim icp->icmp_seq = ntransmitted++; 432167802Sjkim icp->icmp_id = ident; /* ID */ 43367754Smsmith 434167802Sjkim CLR(icp->icmp_seq % mx_dup_ck); 43567754Smsmith 436193267Sjkim if (timing) 437193267Sjkim (void)gettimeofday((struct timeval *)&outpack[8], 438167802Sjkim (struct timezone *)NULL); 439167802Sjkim 440167802Sjkim cc = datalen + 8; /* skips ICMP portion */ 44167754Smsmith 442193453Sjkim /* compute ICMP checksum here */ 443193453Sjkim icp->icmp_cksum = in_cksum((u_short *)icp, cc); 444193453Sjkim 445193453Sjkim i = sendto(s, (char *)outpack, cc, 0, &whereto, 446193453Sjkim sizeof(struct sockaddr)); 447193453Sjkim 448193453Sjkim if (i < 0 || i != cc) { 449193453Sjkim if (i < 0) 450193453Sjkim perror("ping: sendto"); 451193453Sjkim (void)printf("ping: wrote %s %d chars, ret=%d\n", 452193453Sjkim hostname, cc, i); 453193453Sjkim } 454193453Sjkim if (!(options & F_QUIET) && options & F_FLOOD) 455193453Sjkim (void)write(STDOUT_FILENO, &DOT, 1); 456193453Sjkim} 457193267Sjkim 45867754Smsmith/* 459167802Sjkim * pr_pack -- 460193267Sjkim * Print out the packet, if it came from us. This logic is necessary 461167802Sjkim * because ALL readers of the ICMP socket get a copy of ALL ICMP packets 462193267Sjkim * which arrive ('tis only fair). This permits multiple copies of this 463193267Sjkim * program to be run without having intermingled output (or statistics!). 464193267Sjkim */ 465167802Sjkimpr_pack(buf, cc, from) 466167802Sjkim char *buf; 46767754Smsmith int cc; 468193267Sjkim struct sockaddr_in *from; 469193267Sjkim{ 470193267Sjkim register struct icmp *icp; 471193267Sjkim register u_long l; 472193267Sjkim register int i, j; 473193267Sjkim register u_char *cp,*dp; 474193267Sjkim static int old_rrlen; 475193267Sjkim static char old_rr[MAX_IPOPTLEN]; 476193267Sjkim struct ip *ip; 477193267Sjkim struct timeval tv, *tp; 478193267Sjkim double triptime; 479193267Sjkim int hlen, dupflag; 480193267Sjkim 481193267Sjkim (void)gettimeofday(&tv, (struct timezone *)NULL); 482193267Sjkim 483193267Sjkim /* Check the IP header */ 484193267Sjkim ip = (struct ip *)buf; 485193267Sjkim hlen = ip->ip_hl << 2; 486193267Sjkim if (cc < hlen + ICMP_MINLEN) { 487193267Sjkim if (options & F_VERBOSE) 488193267Sjkim (void)fprintf(stderr, 489193267Sjkim "ping: packet too short (%d bytes) from %s\n", cc, 490193267Sjkim inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr)); 491193267Sjkim return; 492193267Sjkim } 493193267Sjkim 494167802Sjkim /* Now the ICMP part */ 495167802Sjkim cc -= hlen; 496167802Sjkim icp = (struct icmp *)(buf + hlen); 497193267Sjkim if (icp->icmp_type == ICMP_ECHOREPLY) { 498167802Sjkim if (icp->icmp_id != ident) 499167802Sjkim return; /* 'Twas not our ECHO */ 500167802Sjkim ++nreceived; 501167802Sjkim if (timing) { 502193267Sjkim#ifndef icmp_data 503167802Sjkim tp = (struct timeval *)&icp->icmp_ip; 504193267Sjkim#else 505167802Sjkim tp = (struct timeval *)icp->icmp_data; 506167802Sjkim#endif 50767754Smsmith tvsub(&tv, tp); 508167802Sjkim triptime = ((double)tv.tv_sec) * 1000.0 + 50967754Smsmith ((double)tv.tv_usec) / 1000.0; 510193267Sjkim tsum += triptime; 51167754Smsmith if (triptime < tmin) 512167802Sjkim tmin = triptime; 513167802Sjkim if (triptime > tmax) 514193267Sjkim tmax = triptime; 51567754Smsmith } 51667754Smsmith 51767754Smsmith if (TST(icp->icmp_seq % mx_dup_ck)) { 51867754Smsmith ++nrepeats; 51967754Smsmith --nreceived; 520167802Sjkim dupflag = 1; 52167754Smsmith } else { 522167802Sjkim SET(icp->icmp_seq % mx_dup_ck); 523167802Sjkim dupflag = 0; 52467754Smsmith } 525167802Sjkim 52667754Smsmith if (options & F_QUIET) 527167802Sjkim return; 528167802Sjkim 52967754Smsmith if (options & F_FLOOD) 530167802Sjkim (void)write(STDOUT_FILENO, &BSPACE, 1); 531167802Sjkim else { 532167802Sjkim (void)printf("%d bytes from %s: icmp_seq=%u", cc, 53367754Smsmith inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), 53467754Smsmith icp->icmp_seq); 535167802Sjkim (void)printf(" ttl=%d", ip->ip_ttl); 536167802Sjkim if (timing) 537167802Sjkim (void)printf(" time=%.3f ms", triptime); 538193267Sjkim if (dupflag) 53967754Smsmith (void)printf(" (DUP!)"); 540167802Sjkim /* check the data */ 54167754Smsmith cp = (u_char*)&icp->icmp_data[8]; 54267754Smsmith dp = &outpack[8 + sizeof(struct timeval)]; 543167802Sjkim for (i = 8; i < datalen; ++i, ++cp, ++dp) { 544167802Sjkim if (*cp != *dp) { 545167802Sjkim (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", 546167802Sjkim i, *dp, *cp); 547167802Sjkim cp = (u_char*)&icp->icmp_data[0]; 54867754Smsmith for (i = 8; i < datalen; ++i, ++cp) { 549167802Sjkim if ((i % 32) == 8) 550167802Sjkim (void)printf("\n\t"); 551167802Sjkim (void)printf("%x ", *cp); 552167802Sjkim } 553167802Sjkim break; 554167802Sjkim } 555167802Sjkim } 556167802Sjkim } 557167802Sjkim } else { 558167802Sjkim /* We've got something other than an ECHOREPLY */ 559193267Sjkim if (!(options & F_VERBOSE)) 560193267Sjkim return; 561167802Sjkim (void)printf("%d bytes from %s: ", cc, 562167802Sjkim pr_addr(from->sin_addr.s_addr)); 56367754Smsmith pr_icmph(icp); 564167802Sjkim } 565167802Sjkim 566167802Sjkim /* Display any IP options */ 567167802Sjkim cp = (u_char *)buf + sizeof(struct ip); 56867754Smsmith 569167802Sjkim for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) 570193267Sjkim switch (*cp) { 571193267Sjkim case IPOPT_EOL: 572167802Sjkim hlen = 0; 57367754Smsmith break; 574167802Sjkim case IPOPT_LSRR: 575167802Sjkim (void)printf("\nLSRR: "); 57667754Smsmith hlen -= 2; 57767754Smsmith j = *++cp; 57867754Smsmith ++cp; 57967754Smsmith if (j > IPOPT_MINOFF) 580151937Sjkim for (;;) { 581151937Sjkim l = *++cp; 582167802Sjkim l = (l<<8) + *++cp; 583151937Sjkim l = (l<<8) + *++cp; 584167802Sjkim l = (l<<8) + *++cp; 585151937Sjkim if (l == 0) 586167802Sjkim (void)printf("\t0.0.0.0"); 587151937Sjkim else 588167802Sjkim (void)printf("\t%s", pr_addr(ntohl(l))); 589167802Sjkim hlen -= 4; 590167802Sjkim j -= 4; 591167802Sjkim if (j <= IPOPT_MINOFF) 592167802Sjkim break; 593167802Sjkim (void)putchar('\n'); 594167802Sjkim } 595151937Sjkim break; 596151937Sjkim case IPOPT_RR: 597151937Sjkim j = *++cp; /* get length */ 598167802Sjkim i = *++cp; /* and pointer */ 599193267Sjkim hlen -= 2; 600151937Sjkim if (i > j) 601167802Sjkim i = j; 602193267Sjkim i -= IPOPT_MINOFF; 603193267Sjkim if (i <= 0) 604167802Sjkim continue; 605167802Sjkim if (i == old_rrlen 606167802Sjkim && cp == (u_char *)buf + sizeof(struct ip) + 2 607167802Sjkim && !bcmp((char *)cp, old_rr, i) 608167802Sjkim && !(options & F_FLOOD)) { 609167802Sjkim (void)printf("\t(same route)"); 610151937Sjkim i = ((i + 3) / 4) * 4; 611151937Sjkim hlen -= i; 612167802Sjkim cp += i; 613151937Sjkim break; 614151937Sjkim } 615167802Sjkim old_rrlen = i; 616167802Sjkim bcopy((char *)cp, old_rr, i); 617167802Sjkim (void)printf("\nRR: "); 618167802Sjkim for (;;) { 619167802Sjkim l = *++cp; 620151937Sjkim l = (l<<8) + *++cp; 621167802Sjkim l = (l<<8) + *++cp; 622167802Sjkim l = (l<<8) + *++cp; 623167802Sjkim if (l == 0) 624193267Sjkim (void)printf("\t0.0.0.0"); 625193267Sjkim else 626167802Sjkim (void)printf("\t%s", pr_addr(ntohl(l))); 627167802Sjkim hlen -= 4; 628167802Sjkim i -= 4; 629167802Sjkim if (i <= 0) 630167802Sjkim break; 631167802Sjkim (void)putchar('\n'); 632167802Sjkim } 633167802Sjkim break; 634167802Sjkim case IPOPT_NOP: 635167802Sjkim (void)printf("\nNOP"); 636167802Sjkim break; 637167802Sjkim default: 638167802Sjkim (void)printf("\nunknown option %x", *cp); 639167802Sjkim break; 640167802Sjkim } 641167802Sjkim if (!(options & F_FLOOD)) { 642167802Sjkim (void)putchar('\n'); 643167802Sjkim (void)fflush(stdout); 644167802Sjkim } 645167802Sjkim} 646167802Sjkim 647167802Sjkim/* 648167802Sjkim * in_cksum -- 649167802Sjkim * Checksum routine for Internet Protocol family headers (C Version) 650167802Sjkim */ 651167802Sjkimin_cksum(addr, len) 652167802Sjkim u_short *addr; 653167802Sjkim int len; 654167802Sjkim{ 655167802Sjkim register int nleft = len; 656167802Sjkim register u_short *w = addr; 657167802Sjkim register int sum = 0; 658167802Sjkim u_short answer = 0; 659167802Sjkim 660167802Sjkim /* 661167802Sjkim * Our algorithm is simple, using a 32 bit accumulator (sum), we add 662167802Sjkim * sequential 16 bit words to it, and at the end, fold back all the 663167802Sjkim * carry bits from the top 16 bits into the lower 16 bits. 664167802Sjkim */ 665167802Sjkim while (nleft > 1) { 666167802Sjkim sum += *w++; 667167802Sjkim nleft -= 2; 668167802Sjkim } 669167802Sjkim 670167802Sjkim /* mop up an odd byte, if necessary */ 671167802Sjkim if (nleft == 1) { 672167802Sjkim *(u_char *)(&answer) = *(u_char *)w ; 673167802Sjkim sum += answer; 674167802Sjkim } 675167802Sjkim 676167802Sjkim /* add back carry outs from top 16 bits to low 16 bits */ 677167802Sjkim sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 678167802Sjkim sum += (sum >> 16); /* add carry */ 679167802Sjkim answer = ~sum; /* truncate to 16 bits */ 680167802Sjkim return(answer); 681167802Sjkim} 682167802Sjkim 683167802Sjkim/* 684167802Sjkim * tvsub -- 685167802Sjkim * Subtract 2 timeval structs: out = out - in. Out is assumed to 686167802Sjkim * be >= in. 687167802Sjkim */ 688167802Sjkimtvsub(out, in) 689167802Sjkim register struct timeval *out, *in; 690167802Sjkim{ 691167802Sjkim if ((out->tv_usec -= in->tv_usec) < 0) { 692193267Sjkim --out->tv_sec; 693193267Sjkim out->tv_usec += 1000000; 694167802Sjkim } 695167802Sjkim out->tv_sec -= in->tv_sec; 696193267Sjkim} 697193267Sjkim 698193267Sjkim/* 699167802Sjkim * status -- 700167802Sjkim * Print out statistics when SIGINFO is received. 701167802Sjkim */ 702167802Sjkim 703167802Sjkimvoid 704167802Sjkimstatus() 705167802Sjkim{ 706167802Sjkim double temp_min = nreceived ? tmin : 0; 707167802Sjkim (void)fprintf(stderr, "%ld/%ld packets received (%ld%%) " 708167802Sjkim "%.3f min / %.3f avg / %.3f max\n", 709151937Sjkim nreceived, ntransmitted, 710167802Sjkim (ntransmitted ? 711167802Sjkim 100 - (int) (((ntransmitted - nreceived) * 100) 712167802Sjkim / ntransmitted) 713167802Sjkim : 0), 714151937Sjkim temp_min, 715167802Sjkim ((nreceived + nrepeats) ? 716193267Sjkim (tsum / (nreceived + nrepeats))/1000.0 717193267Sjkim : tsum), 718167802Sjkim tmax/ 1000.0); 719151937Sjkim} 720167802Sjkim 721151937Sjkim/* 722167802Sjkim * finish -- 723167802Sjkim * Print out statistics, and give up. 724167802Sjkim */ 725167802Sjkimvoid 726167802Sjkimfinish() 727167802Sjkim{ 728167802Sjkim register int i; 729167802Sjkim struct termios ts; 730167802Sjkim 731167802Sjkim (void)signal(SIGINT, SIG_IGN); 732167802Sjkim (void)putchar('\n'); 733167802Sjkim (void)fflush(stdout); 734167802Sjkim (void)printf("--- %s ping statistics ---\n", hostname); 735167802Sjkim (void)printf("%ld packets transmitted, ", ntransmitted); 736167802Sjkim (void)printf("%ld packets received, ", nreceived); 737167802Sjkim if (nrepeats) 738167802Sjkim (void)printf("+%ld duplicates, ", nrepeats); 739167802Sjkim if (ntransmitted) 740167802Sjkim if (nreceived > ntransmitted) 741167802Sjkim (void)printf("-- somebody's printing up packets!"); 742167802Sjkim else 743167802Sjkim (void)printf("%d%% packet loss", 744193267Sjkim (int) (((ntransmitted - nreceived) * 100) / 745167802Sjkim ntransmitted)); 746167802Sjkim (void)putchar('\n'); 747167802Sjkim if (nreceived && timing) { 748167802Sjkim /* Only display average to microseconds */ 749167802Sjkim i = 1000.0 * tsum / (nreceived + nrepeats); 750167802Sjkim (void)printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n", 751193267Sjkim tmin, ((double)i) / 1000.0, tmax); 752151937Sjkim } 753151937Sjkim if (reset_kerninfo && tcgetattr (0, &ts) != -1) { 754151937Sjkim ts.c_lflag &= ~NOKERNINFO; 755167802Sjkim tcsetattr (0, TCSANOW, &ts); 756151937Sjkim } 757 758 if (nreceived) 759 exit(0); 760 else 761 exit(2); 762} 763 764#ifdef notdef 765static char *ttab[] = { 766 "Echo Reply", /* ip + seq + udata */ 767 "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ 768 "Source Quench", /* IP */ 769 "Redirect", /* redirect type, gateway, + IP */ 770 "Echo", 771 "Time Exceeded", /* transit, frag reassem + IP */ 772 "Parameter Problem", /* pointer + IP */ 773 "Timestamp", /* id + seq + three timestamps */ 774 "Timestamp Reply", /* " */ 775 "Info Request", /* id + sq */ 776 "Info Reply" /* " */ 777}; 778#endif 779 780/* 781 * pr_icmph -- 782 * Print a descriptive string about an ICMP header. 783 */ 784pr_icmph(icp) 785 struct icmp *icp; 786{ 787 switch(icp->icmp_type) { 788 case ICMP_ECHOREPLY: 789 (void)printf("Echo Reply\n"); 790 /* XXX ID + Seq + Data */ 791 break; 792 case ICMP_UNREACH: 793 switch(icp->icmp_code) { 794 case ICMP_UNREACH_NET: 795 (void)printf("Destination Net Unreachable\n"); 796 break; 797 case ICMP_UNREACH_HOST: 798 (void)printf("Destination Host Unreachable\n"); 799 break; 800 case ICMP_UNREACH_PROTOCOL: 801 (void)printf("Destination Protocol Unreachable\n"); 802 break; 803 case ICMP_UNREACH_PORT: 804 (void)printf("Destination Port Unreachable\n"); 805 break; 806 case ICMP_UNREACH_NEEDFRAG: 807 (void)printf("frag needed and DF set\n"); 808 break; 809 case ICMP_UNREACH_SRCFAIL: 810 (void)printf("Source Route Failed\n"); 811 break; 812 default: 813 (void)printf("Dest Unreachable, Bad Code: %d\n", 814 icp->icmp_code); 815 break; 816 } 817 /* Print returned IP header information */ 818#ifndef icmp_data 819 pr_retip(&icp->icmp_ip); 820#else 821 pr_retip((struct ip *)icp->icmp_data); 822#endif 823 break; 824 case ICMP_SOURCEQUENCH: 825 (void)printf("Source Quench\n"); 826#ifndef icmp_data 827 pr_retip(&icp->icmp_ip); 828#else 829 pr_retip((struct ip *)icp->icmp_data); 830#endif 831 break; 832 case ICMP_REDIRECT: 833 switch(icp->icmp_code) { 834 case ICMP_REDIRECT_NET: 835 (void)printf("Redirect Network"); 836 break; 837 case ICMP_REDIRECT_HOST: 838 (void)printf("Redirect Host"); 839 break; 840 case ICMP_REDIRECT_TOSNET: 841 (void)printf("Redirect Type of Service and Network"); 842 break; 843 case ICMP_REDIRECT_TOSHOST: 844 (void)printf("Redirect Type of Service and Host"); 845 break; 846 default: 847 (void)printf("Redirect, Bad Code: %d", icp->icmp_code); 848 break; 849 } 850 (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr); 851#ifndef icmp_data 852 pr_retip(&icp->icmp_ip); 853#else 854 pr_retip((struct ip *)icp->icmp_data); 855#endif 856 break; 857 case ICMP_ECHO: 858 (void)printf("Echo Request\n"); 859 /* XXX ID + Seq + Data */ 860 break; 861 case ICMP_TIMXCEED: 862 switch(icp->icmp_code) { 863 case ICMP_TIMXCEED_INTRANS: 864 (void)printf("Time to live exceeded\n"); 865 break; 866 case ICMP_TIMXCEED_REASS: 867 (void)printf("Frag reassembly time exceeded\n"); 868 break; 869 default: 870 (void)printf("Time exceeded, Bad Code: %d\n", 871 icp->icmp_code); 872 break; 873 } 874#ifndef icmp_data 875 pr_retip(&icp->icmp_ip); 876#else 877 pr_retip((struct ip *)icp->icmp_data); 878#endif 879 break; 880 case ICMP_PARAMPROB: 881 (void)printf("Parameter problem: pointer = 0x%02x\n", 882 icp->icmp_hun.ih_pptr); 883#ifndef icmp_data 884 pr_retip(&icp->icmp_ip); 885#else 886 pr_retip((struct ip *)icp->icmp_data); 887#endif 888 break; 889 case ICMP_TSTAMP: 890 (void)printf("Timestamp\n"); 891 /* XXX ID + Seq + 3 timestamps */ 892 break; 893 case ICMP_TSTAMPREPLY: 894 (void)printf("Timestamp Reply\n"); 895 /* XXX ID + Seq + 3 timestamps */ 896 break; 897 case ICMP_IREQ: 898 (void)printf("Information Request\n"); 899 /* XXX ID + Seq */ 900 break; 901 case ICMP_IREQREPLY: 902 (void)printf("Information Reply\n"); 903 /* XXX ID + Seq */ 904 break; 905#ifdef ICMP_MASKREQ 906 case ICMP_MASKREQ: 907 (void)printf("Address Mask Request\n"); 908 break; 909#endif 910#ifdef ICMP_MASKREPLY 911 case ICMP_MASKREPLY: 912 (void)printf("Address Mask Reply\n"); 913 break; 914#endif 915 default: 916 (void)printf("Bad ICMP type: %d\n", icp->icmp_type); 917 } 918} 919 920/* 921 * pr_iph -- 922 * Print an IP header with options. 923 */ 924pr_iph(ip) 925 struct ip *ip; 926{ 927 int hlen; 928 u_char *cp; 929 930 hlen = ip->ip_hl << 2; 931 cp = (u_char *)ip + 20; /* point to options */ 932 933 (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); 934 (void)printf(" %1x %1x %02x %04x %04x", 935 ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); 936 (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13, 937 (ip->ip_off) & 0x1fff); 938 (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum); 939 (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); 940 (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); 941 /* dump and option bytes */ 942 while (hlen-- > 20) { 943 (void)printf("%02x", *cp++); 944 } 945 (void)putchar('\n'); 946} 947 948/* 949 * pr_addr -- 950 * Return an ascii host address as a dotted quad and optionally with 951 * a hostname. 952 */ 953char * 954pr_addr(l) 955 u_long l; 956{ 957 struct hostent *hp; 958 static char buf[80]; 959 960 if ((options & F_NUMERIC) || 961 !(hp = gethostbyaddr((char *)&l, 4, AF_INET))) 962 (void)sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&l)); 963 else 964 (void)sprintf(buf, "%s (%s)", hp->h_name, 965 inet_ntoa(*(struct in_addr *)&l)); 966 return(buf); 967} 968 969/* 970 * pr_retip -- 971 * Dump some info on a returned (via ICMP) IP packet. 972 */ 973pr_retip(ip) 974 struct ip *ip; 975{ 976 int hlen; 977 u_char *cp; 978 979 pr_iph(ip); 980 hlen = ip->ip_hl << 2; 981 cp = (u_char *)ip + hlen; 982 983 if (ip->ip_p == 6) 984 (void)printf("TCP: from port %u, to port %u (decimal)\n", 985 (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); 986 else if (ip->ip_p == 17) 987 (void)printf("UDP: from port %u, to port %u (decimal)\n", 988 (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); 989} 990 991fill(bp, patp) 992 char *bp, *patp; 993{ 994 register int ii, jj, kk; 995 int pat[16]; 996 char *cp; 997 998 for (cp = patp; *cp; cp++) 999 if (!isxdigit(*cp)) { 1000 (void)fprintf(stderr, 1001 "ping: patterns must be specified as hex digits.\n"); 1002 exit(1); 1003 } 1004 ii = sscanf(patp, 1005 "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", 1006 &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], 1007 &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], 1008 &pat[13], &pat[14], &pat[15]); 1009 1010 if (ii > 0) 1011 for (kk = 0; 1012 kk <= MAXPACKET - (8 + sizeof(struct timeval) + ii); 1013 kk += ii) 1014 for (jj = 0; jj < ii; ++jj) 1015 bp[jj + kk] = pat[jj]; 1016 if (!(options & F_QUIET)) { 1017 (void)printf("PATTERN: 0x"); 1018 for (jj = 0; jj < ii; ++jj) 1019 (void)printf("%02x", bp[jj] & 0xFF); 1020 (void)printf("\n"); 1021 } 1022} 1023 1024usage() 1025{ 1026 (void)fprintf(stderr, 1027 "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n"); 1028 exit(1); 1029} 1030