ping.c revision 27533
11558Srgrimes/* 21558Srgrimes * Copyright (c) 1989, 1993 31558Srgrimes * The Regents of the University of California. All rights reserved. 41558Srgrimes * 51558Srgrimes * This code is derived from software contributed to Berkeley by 61558Srgrimes * Mike Muuss. 71558Srgrimes * 81558Srgrimes * Redistribution and use in source and binary forms, with or without 91558Srgrimes * modification, are permitted provided that the following conditions 101558Srgrimes * are met: 111558Srgrimes * 1. Redistributions of source code must retain the above copyright 121558Srgrimes * notice, this list of conditions and the following disclaimer. 131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141558Srgrimes * notice, this list of conditions and the following disclaimer in the 151558Srgrimes * documentation and/or other materials provided with the distribution. 161558Srgrimes * 3. All advertising materials mentioning features or use of this software 171558Srgrimes * must display the following acknowledgement: 181558Srgrimes * This product includes software developed by the University of 191558Srgrimes * California, Berkeley and its contributors. 201558Srgrimes * 4. Neither the name of the University nor the names of its contributors 211558Srgrimes * may be used to endorse or promote products derived from this software 221558Srgrimes * without specific prior written permission. 231558Srgrimes * 241558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341558Srgrimes * SUCH DAMAGE. 351558Srgrimes */ 361558Srgrimes 371558Srgrimes#ifndef lint 3823247Swollmanstatic const char copyright[] = 391558Srgrimes"@(#) Copyright (c) 1989, 1993\n\ 401558Srgrimes The Regents of the University of California. All rights reserved.\n"; 411558Srgrimes#endif /* not lint */ 421558Srgrimes 431558Srgrimes#ifndef lint 4423247Swollman/* 451558Srgrimesstatic char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93"; 4623247Swollman*/ 4723247Swollmanstatic const char rcsid[] = 4827533Sbde "$Id: ping.c,v 1.25 1997/07/18 17:52:05 wollman Exp $"; 491558Srgrimes#endif /* not lint */ 501558Srgrimes 511558Srgrimes/* 521558Srgrimes * P I N G . C 531558Srgrimes * 541558Srgrimes * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, 551558Srgrimes * measure round-trip-delays and packet loss across network paths. 561558Srgrimes * 571558Srgrimes * Author - 581558Srgrimes * Mike Muuss 591558Srgrimes * U. S. Army Ballistic Research Laboratory 601558Srgrimes * December, 1983 611558Srgrimes * 621558Srgrimes * Status - 631558Srgrimes * Public Domain. Distribution Unlimited. 641558Srgrimes * Bugs - 651558Srgrimes * More statistics could always be gathered. 661558Srgrimes * This program has to run SUID to ROOT to access the ICMP socket. 671558Srgrimes */ 681558Srgrimes 6923247Swollman#include <sys/param.h> /* NB: we rely on this for <sys/types.h> */ 7023247Swollman 7123247Swollman#include <ctype.h> 7223247Swollman#include <err.h> 7323247Swollman#include <errno.h> 7427508Swollman#include <math.h> 7523247Swollman#include <netdb.h> 7623247Swollman#include <signal.h> 7723247Swollman#include <stdio.h> 7823247Swollman#include <stdlib.h> 7923247Swollman#include <string.h> 8023247Swollman#include <sysexits.h> 8123247Swollman#include <termios.h> 8223247Swollman#include <unistd.h> 8323247Swollman 841558Srgrimes#include <sys/socket.h> 851558Srgrimes#include <sys/file.h> 861558Srgrimes#include <sys/time.h> 871558Srgrimes 8823247Swollman#include <netinet/in.h> 891558Srgrimes#include <netinet/in_systm.h> 901558Srgrimes#include <netinet/ip.h> 911558Srgrimes#include <netinet/ip_icmp.h> 921558Srgrimes#include <netinet/ip_var.h> 9323247Swollman#include <arpa/inet.h> 941558Srgrimes 951558Srgrimes#define DEFDATALEN (64 - 8) /* default data length */ 9627533Sbde#define FLOOD_BACKOFF 20000 /* usecs to back off if F_FLOOD mode */ 9727533Sbde /* runs out of buffer space */ 981558Srgrimes#define MAXIPLEN 60 991558Srgrimes#define MAXICMPLEN 76 1001558Srgrimes#define MAXPACKET (65536 - 60 - 8)/* max packet size */ 1011558Srgrimes#define MAXWAIT 10 /* max seconds to wait for response */ 1021558Srgrimes#define NROUTES 9 /* number of record route slots */ 1031558Srgrimes 1041558Srgrimes#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ 1051558Srgrimes#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ 1061558Srgrimes#define SET(bit) (A(bit) |= B(bit)) 1071558Srgrimes#define CLR(bit) (A(bit) &= (~B(bit))) 1081558Srgrimes#define TST(bit) (A(bit) & B(bit)) 1091558Srgrimes 1101558Srgrimes/* various options */ 1111558Srgrimesint options; 11220540Sfenner#define F_FLOOD 0x0001 11320540Sfenner#define F_INTERVAL 0x0002 11420540Sfenner#define F_NUMERIC 0x0004 11520540Sfenner#define F_PINGFILLED 0x0008 11620540Sfenner#define F_QUIET 0x0010 11720540Sfenner#define F_RROUTE 0x0020 11820540Sfenner#define F_SO_DEBUG 0x0040 11920540Sfenner#define F_SO_DONTROUTE 0x0080 12020540Sfenner#define F_VERBOSE 0x0100 12120540Sfenner#define F_QUIET2 0x0200 12220540Sfenner#define F_NOLOOP 0x0400 12320540Sfenner#define F_MTTL 0x0800 12420540Sfenner#define F_MIF 0x1000 12522417Sdanny#define F_AUDIBLE 0x2000 1261558Srgrimes 1271558Srgrimes/* 1281558Srgrimes * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum 1291558Srgrimes * number of received sequence numbers we can keep track of. Change 128 1301558Srgrimes * to 8192 for complete accuracy... 1311558Srgrimes */ 1321558Srgrimes#define MAX_DUP_CHK (8 * 128) 1331558Srgrimesint mx_dup_ck = MAX_DUP_CHK; 1341558Srgrimeschar rcvd_tbl[MAX_DUP_CHK / 8]; 1351558Srgrimes 1361558Srgrimesstruct sockaddr whereto; /* who to ping */ 1371558Srgrimesint datalen = DEFDATALEN; 1381558Srgrimesint s; /* socket file descriptor */ 1391558Srgrimesu_char outpack[MAXPACKET]; 1401558Srgrimeschar BSPACE = '\b'; /* characters written for flood */ 1411558Srgrimeschar DOT = '.'; 1421558Srgrimeschar *hostname; 1431558Srgrimesint ident; /* process id to identify our packets */ 14423295Simpint uid; /* cached uid for micro-optimization */ 1451558Srgrimes 1461558Srgrimes/* counters */ 1471558Srgrimeslong npackets; /* max packets to transmit */ 1481558Srgrimeslong nreceived; /* # of packets we got back */ 1491558Srgrimeslong nrepeats; /* number of duplicates */ 1501558Srgrimeslong ntransmitted; /* sequence # for outbound packets = #sent */ 1511558Srgrimesint interval = 1; /* interval between packets */ 1521558Srgrimes 1531558Srgrimes/* timing */ 1541558Srgrimesint timing; /* flag to do timing */ 1551558Srgrimesdouble tmin = 999999999.0; /* minimum round trip time */ 1561558Srgrimesdouble tmax = 0.0; /* maximum round trip time */ 1571558Srgrimesdouble tsum = 0.0; /* sum of all times, for doing average */ 15827508Swollmandouble tsumsq = 0.0; /* sum of all times squared, for std. dev. */ 1591558Srgrimes 16027533Sbdevolatile sig_atomic_t finish_up; /* nonzero if we've been told to finish up */ 1613792Ssefint reset_kerninfo; 16227533Sbdevolatile sig_atomic_t siginfo_p; 1633792Ssef 16423247Swollmanstatic void fill(char *, char *); 16523247Swollmanstatic u_short in_cksum(u_short *, int); 16623247Swollmanstatic void catcher(int sig); 16723247Swollmanstatic void check_status(void); 16827533Sbdestatic void finish(void) __dead2; 16923247Swollmanstatic void pinger(void); 17023247Swollmanstatic char *pr_addr(struct in_addr); 17123247Swollmanstatic void pr_icmph(struct icmp *); 17223247Swollmanstatic void pr_iph(struct ip *); 17323247Swollmanstatic void pr_pack(char *, int, struct sockaddr_in *); 17423247Swollmanstatic void pr_retip(struct ip *); 17523247Swollmanstatic void status(int); 17627533Sbdestatic void stopit(int); 17723247Swollmanstatic void tvsub(struct timeval *, struct timeval *); 17823247Swollmanstatic void usage(const char *) __dead2; 1791558Srgrimes 18023247Swollmanint 1811558Srgrimesmain(argc, argv) 1821558Srgrimes int argc; 18323247Swollman char *const *argv; 1841558Srgrimes{ 1851558Srgrimes struct timeval timeout; 1861558Srgrimes struct hostent *hp; 1871558Srgrimes struct sockaddr_in *to; 1883792Ssef struct termios ts; 1891558Srgrimes register int i; 19017474Sfenner int ch, fdmask, hold, packlen, preload, sockerrno; 19120540Sfenner struct in_addr ifaddr; 19220540Sfenner unsigned char ttl, loop; 1931558Srgrimes u_char *datap, *packet; 19423247Swollman char *target, hnamebuf[MAXHOSTNAMELEN]; 19523247Swollman char *ep; 19623247Swollman u_long ultmp; 1971558Srgrimes#ifdef IP_OPTIONS 1981558Srgrimes char rspace[3 + 4 * NROUTES + 1]; /* record route space */ 1991558Srgrimes#endif 20020195Ssef struct sigaction si_sa; 2011558Srgrimes 20217474Sfenner /* 20317474Sfenner * Do the stuff that we need root priv's for *first*, and 20417474Sfenner * then drop our setuid bit. Save error reporting for 20517474Sfenner * after arg parsing. 20617474Sfenner */ 20723247Swollman s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 20823247Swollman sockerrno = errno; 20917474Sfenner 21017474Sfenner setuid(getuid()); 21123295Simp uid = getuid(); 21217474Sfenner 2131558Srgrimes preload = 0; 2143792Ssef 2151558Srgrimes datap = &outpack[8 + sizeof(struct timeval)]; 21623251Simp while ((ch = getopt(argc, argv, "I:LQRT:c:adfi:l:np:qrs:v")) != -1) { 2171558Srgrimes switch(ch) { 21822417Sdanny case 'a': 21922417Sdanny options |= F_AUDIBLE; 22022417Sdanny break; 2211558Srgrimes case 'c': 22223247Swollman ultmp = strtoul(optarg, &ep, 0); 22323247Swollman if (*ep || ep == optarg || ultmp > LONG_MAX || !ultmp) 22423247Swollman errx(EX_USAGE, 22523247Swollman "invalid count of packets to transmit: `%s'", 22623247Swollman optarg); 22723247Swollman npackets = ultmp; 2281558Srgrimes break; 2291558Srgrimes case 'd': 2301558Srgrimes options |= F_SO_DEBUG; 2311558Srgrimes break; 2321558Srgrimes case 'f': 2331558Srgrimes if (getuid()) { 23423247Swollman errno = EPERM; 23523247Swollman err(EX_NOPERM, "-f flag"); 2361558Srgrimes } 2371558Srgrimes options |= F_FLOOD; 2381558Srgrimes setbuf(stdout, (char *)NULL); 2391558Srgrimes break; 2401558Srgrimes case 'i': /* wait between sending packets */ 24123247Swollman ultmp = strtoul(optarg, &ep, 0); 24223247Swollman if (*ep || ep == optarg || ultmp > INT_MAX) 24323247Swollman errx(EX_USAGE, 24423247Swollman "invalid timing interval: `%s'", optarg); 2451558Srgrimes options |= F_INTERVAL; 24623247Swollman interval = ultmp; 2471558Srgrimes break; 24820540Sfenner case 'I': /* multicast interface */ 24923247Swollman if (inet_aton(optarg, &ifaddr) == 0) 25023247Swollman errx(EX_USAGE, 25123247Swollman "invalid multicast interface: `%s'", 25223247Swollman optarg); 25320540Sfenner options |= F_MIF; 25420540Sfenner break; 2551558Srgrimes case 'l': 25623247Swollman ultmp = strtoul(optarg, &ep, 0); 25723247Swollman if (*ep || ep == optarg || ultmp > INT_MAX) 25823247Swollman errx(EX_USAGE, 25923247Swollman "invalid preload value: `%s'", optarg); 26023251Simp if (getuid()) { 26123251Simp errno = EPERM; 26223251Simp err(EX_NOPERM, "-l flag"); 26323251Simp } 26423251Simp options |= F_FLOOD; 26523247Swollman preload = ultmp; 2661558Srgrimes break; 26720540Sfenner case 'L': 26820540Sfenner options |= F_NOLOOP; 26920540Sfenner loop = 0; 27020540Sfenner break; 2711558Srgrimes case 'n': 2721558Srgrimes options |= F_NUMERIC; 2731558Srgrimes break; 2741558Srgrimes case 'p': /* fill buffer with user pattern */ 2751558Srgrimes options |= F_PINGFILLED; 2761558Srgrimes fill((char *)datap, optarg); 2771558Srgrimes break; 27817724Sfenner case 'Q': 27917724Sfenner options |= F_QUIET2; 28017724Sfenner break; 2811558Srgrimes case 'q': 2821558Srgrimes options |= F_QUIET; 2831558Srgrimes break; 2841558Srgrimes case 'R': 2851558Srgrimes options |= F_RROUTE; 2861558Srgrimes break; 2871558Srgrimes case 'r': 2881558Srgrimes options |= F_SO_DONTROUTE; 2891558Srgrimes break; 2901558Srgrimes case 's': /* size of packet to send */ 29123247Swollman ultmp = strtoul(optarg, &ep, 0); 29223247Swollman if (ultmp > MAXPACKET) 29323247Swollman errx(EX_USAGE, "packet size too large: %lu", 29423247Swollman ultmp); 29523247Swollman if (*ep || ep == optarg || !ultmp) 29623247Swollman errx(EX_USAGE, "invalid packet size: `%s'", 29723247Swollman optarg); 29823247Swollman datalen = ultmp; 2991558Srgrimes break; 30020540Sfenner case 'T': /* multicast TTL */ 30123247Swollman ultmp = strtoul(optarg, &ep, 0); 30223247Swollman if (*ep || ep == optarg || ultmp > 255) 30323247Swollman errx(EX_USAGE, "invalid multicast TTL: `%s'", 30423247Swollman optarg); 30523247Swollman ttl = ultmp; 30620540Sfenner options |= F_MTTL; 30720540Sfenner break; 3081558Srgrimes case 'v': 3091558Srgrimes options |= F_VERBOSE; 3101558Srgrimes break; 3111558Srgrimes default: 31223247Swollman 31323247Swollman usage(argv[0]); 3141558Srgrimes } 31523247Swollman } 3161558Srgrimes 31723247Swollman if (argc - optind != 1) 31823247Swollman usage(argv[0]); 31923247Swollman target = argv[optind]; 3201558Srgrimes 3211558Srgrimes bzero((char *)&whereto, sizeof(struct sockaddr)); 3221558Srgrimes to = (struct sockaddr_in *)&whereto; 3231558Srgrimes to->sin_family = AF_INET; 32423247Swollman if (inet_aton(target, &to->sin_addr) != 0) { 3251558Srgrimes hostname = target; 32623247Swollman } else { 32723247Swollman hp = gethostbyname2(target, AF_INET); 32823247Swollman if (!hp) 32923247Swollman errx(EX_NOHOST, "cannot resolve %s: %s", 33023247Swollman target, hstrerror(h_errno)); 33123247Swollman 33223247Swollman to->sin_len = sizeof *to; 33323327Simp if (hp->h_length > sizeof(to->sin_addr)) 33423327Simp errx(1,"gethostbyname2 returned an illegal address"); 33523247Swollman memcpy(&to->sin_addr, hp->h_addr_list[0], sizeof to->sin_addr); 3361558Srgrimes (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1); 33723247Swollman hnamebuf[(sizeof hnamebuf) - 1] = '\0'; 3381558Srgrimes hostname = hnamebuf; 3391558Srgrimes } 3401558Srgrimes 34123247Swollman if (options & F_FLOOD && options & F_INTERVAL) 34223247Swollman errx(EX_USAGE, "-f and -i: incompatible options"); 3431558Srgrimes 34423247Swollman if (options & F_FLOOD && IN_MULTICAST(ntohl(to->sin_addr.s_addr))) 34523247Swollman errx(EX_USAGE, 34623247Swollman "-f flag cannot be used with multicast destination"); 34723247Swollman if (options & (F_MIF | F_NOLOOP | F_MTTL) 34823247Swollman && !IN_MULTICAST(ntohl(to->sin_addr.s_addr))) 34923247Swollman errx(EX_USAGE, 35023247Swollman "-I, -L, -T flags cannot be used with unicast destination"); 35123247Swollman 3521558Srgrimes if (datalen >= sizeof(struct timeval)) /* can we time transfer */ 3531558Srgrimes timing = 1; 3541558Srgrimes packlen = datalen + MAXIPLEN + MAXICMPLEN; 35523247Swollman if (!(packet = (u_char *)malloc((size_t)packlen))) 35623247Swollman err(EX_UNAVAILABLE, "malloc"); 35723247Swollman 3581558Srgrimes if (!(options & F_PINGFILLED)) 3591558Srgrimes for (i = 8; i < datalen; ++i) 3601558Srgrimes *datap++ = i; 3611558Srgrimes 3621558Srgrimes ident = getpid() & 0xFFFF; 3631558Srgrimes 36417474Sfenner if (s < 0) { 36517474Sfenner errno = sockerrno; 36623247Swollman err(EX_OSERR, "socket"); 3671558Srgrimes } 3681558Srgrimes hold = 1; 3691558Srgrimes if (options & F_SO_DEBUG) 3701558Srgrimes (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, 3711558Srgrimes sizeof(hold)); 3721558Srgrimes if (options & F_SO_DONTROUTE) 3731558Srgrimes (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, 3741558Srgrimes sizeof(hold)); 3751558Srgrimes 3761558Srgrimes /* record route option */ 3771558Srgrimes if (options & F_RROUTE) { 3781558Srgrimes#ifdef IP_OPTIONS 3791558Srgrimes rspace[IPOPT_OPTVAL] = IPOPT_RR; 3801558Srgrimes rspace[IPOPT_OLEN] = sizeof(rspace)-1; 3811558Srgrimes rspace[IPOPT_OFFSET] = IPOPT_MINOFF; 3821558Srgrimes if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, 38323247Swollman sizeof(rspace)) < 0) 38423247Swollman err(EX_OSERR, "setsockopt IP_OPTIONS"); 3851558Srgrimes#else 38623247Swollman errx(EX_UNAVAILABLE, 38723247Swollman "record route not available in this implementation"); 3881558Srgrimes#endif /* IP_OPTIONS */ 3891558Srgrimes } 3901558Srgrimes 39120540Sfenner if (options & F_NOLOOP) { 39220540Sfenner if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, 39320540Sfenner sizeof(loop)) < 0) { 39423247Swollman err(EX_OSERR, "setsockopt IP_MULTICAST_LOOP"); 39520540Sfenner } 39620540Sfenner } 39720540Sfenner if (options & F_MTTL) { 39820540Sfenner if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, 39920540Sfenner sizeof(ttl)) < 0) { 40023247Swollman err(EX_OSERR, "setsockopt IP_MULTICAST_TTL"); 40120540Sfenner } 40220540Sfenner } 40320540Sfenner if (options & F_MIF) { 40420540Sfenner if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, 40520540Sfenner sizeof(ifaddr)) < 0) { 40623247Swollman err(EX_OSERR, "setsockopt IP_MULTICAST_IF"); 40720540Sfenner } 40820540Sfenner } 40920540Sfenner 4101558Srgrimes /* 4111558Srgrimes * When pinging the broadcast address, you can get a lot of answers. 4121558Srgrimes * Doing something so evil is useful if you are trying to stress the 4131558Srgrimes * ethernet, or just want to fill the arp cache to get some stuff for 41423247Swollman * /etc/ethers. But beware: RFC 1122 allows hosts to ignore broadcast 41523247Swollman * or multicast pings if they wish. 4161558Srgrimes */ 4171558Srgrimes hold = 48 * 1024; 4181558Srgrimes (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, 4191558Srgrimes sizeof(hold)); 4201558Srgrimes 4211558Srgrimes if (to->sin_family == AF_INET) 4221558Srgrimes (void)printf("PING %s (%s): %d data bytes\n", hostname, 42323247Swollman inet_ntoa(to->sin_addr), 4241558Srgrimes datalen); 4251558Srgrimes else 4261558Srgrimes (void)printf("PING %s: %d data bytes\n", hostname, datalen); 4271558Srgrimes 42820280Sbde /* 42927354Ssef * Use sigaction() instead of signal() to get unambiguous semantics, 43027354Ssef * in particular with SA_RESTART not set. 43120280Sbde */ 43227354Ssef 43320205Spst sigemptyset(&si_sa.sa_mask); 43420195Ssef si_sa.sa_flags = 0; 43527354Ssef 43627354Ssef si_sa.sa_handler = stopit; 43727354Ssef if (sigaction(SIGINT, &si_sa, 0) == -1) { 43827354Ssef err(EX_OSERR, "sigaction SIGINT"); 43927354Ssef } 44027354Ssef 44127354Ssef si_sa.sa_handler = catcher; 44227354Ssef if (sigaction(SIGALRM, &si_sa, 0) == -1) { 44327354Ssef err(EX_OSERR, "sigaction SIGALRM"); 44427354Ssef } 44527354Ssef 44627354Ssef si_sa.sa_handler = status; 44720195Ssef if (sigaction(SIGINFO, &si_sa, 0) == -1) { 44823385Simp err(EX_OSERR, "sigaction"); 44920195Ssef } 45020195Ssef 45119864Ssef if (tcgetattr(STDOUT_FILENO, &ts) != -1) { 45219864Ssef reset_kerninfo = !(ts.c_lflag & NOKERNINFO); 45319864Ssef ts.c_lflag |= NOKERNINFO; 45419864Ssef tcsetattr(STDOUT_FILENO, TCSANOW, &ts); 45519864Ssef } 45619864Ssef 4571558Srgrimes while (preload--) /* fire off them quickies */ 4581558Srgrimes pinger(); 4591558Srgrimes 4601558Srgrimes if ((options & F_FLOOD) == 0) 46123247Swollman catcher(0); /* start things going */ 4621558Srgrimes 46327533Sbde while (!finish_up) { 4641558Srgrimes struct sockaddr_in from; 4651558Srgrimes register int cc; 4661558Srgrimes int fromlen; 4671558Srgrimes 46820280Sbde check_status(); 4691558Srgrimes if (options & F_FLOOD) { 4701558Srgrimes pinger(); 4711558Srgrimes timeout.tv_sec = 0; 4721558Srgrimes timeout.tv_usec = 10000; 4731558Srgrimes fdmask = 1 << s; 4741558Srgrimes if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL, 4751558Srgrimes (fd_set *)NULL, &timeout) < 1) 4761558Srgrimes continue; 4771558Srgrimes } 4781558Srgrimes fromlen = sizeof(from); 4791558Srgrimes if ((cc = recvfrom(s, (char *)packet, packlen, 0, 4801558Srgrimes (struct sockaddr *)&from, &fromlen)) < 0) { 4811558Srgrimes if (errno == EINTR) 4821558Srgrimes continue; 4831558Srgrimes perror("ping: recvfrom"); 4841558Srgrimes continue; 4851558Srgrimes } 4861558Srgrimes pr_pack((char *)packet, cc, &from); 4871558Srgrimes if (npackets && nreceived >= npackets) 4881558Srgrimes break; 4891558Srgrimes } 49027533Sbde finish(); 4911558Srgrimes /* NOTREACHED */ 49223251Simp exit(0); /* Make the compiler happy */ 4931558Srgrimes} 4941558Srgrimes 4951558Srgrimes/* 49627533Sbde * stopit -- 49727533Sbde * Set the global bit that causes the main loop to quit. 49827533Sbde * Do NOT call finish() from here, since finish() does far too much 49927533Sbde * to be called from a signal handler. 50027299Sjulian */ 50127299Sjulianvoid 50227533Sbdestopit(sig) 50327533Sbde int sig; 50427299Sjulian{ 50527299Sjulian finish_up = 1; 50627299Sjulian} 50727299Sjulian 50827299Sjulian/* 5091558Srgrimes * catcher -- 5101558Srgrimes * This routine causes another PING to be transmitted, and then 5111558Srgrimes * schedules another SIGALRM for 1 second from now. 5121558Srgrimes * 5131558Srgrimes * bug -- 5141558Srgrimes * Our sense of time will slowly skew (i.e., packets will not be 5151558Srgrimes * launched exactly at 1-second intervals). This does not affect the 5161558Srgrimes * quality of the delay and loss statistics. 5171558Srgrimes */ 51823247Swollmanstatic void 51923247Swollmancatcher(int sig) 5201558Srgrimes{ 5211558Srgrimes int waittime; 52227354Ssef struct sigaction si_sa; 5231558Srgrimes 5241558Srgrimes pinger(); 52527354Ssef 5261558Srgrimes if (!npackets || ntransmitted < npackets) 52727354Ssef (void)alarm((u_int)interval); 5281558Srgrimes else { 5291558Srgrimes if (nreceived) { 5301558Srgrimes waittime = 2 * tmax / 1000; 5311558Srgrimes if (!waittime) 5321558Srgrimes waittime = 1; 5331558Srgrimes } else 5341558Srgrimes waittime = MAXWAIT; 53527354Ssef 53627354Ssef si_sa.sa_handler = stopit; 53727354Ssef sigemptyset(&si_sa.sa_mask); 53827354Ssef si_sa.sa_flags = 0; 53927354Ssef if (sigaction(SIGALRM, &si_sa, 0) == -1) { 54027354Ssef finish_up = 1; 54127354Ssef return; 54227354Ssef } 5431558Srgrimes (void)alarm((u_int)waittime); 5441558Srgrimes } 5451558Srgrimes} 5461558Srgrimes 5471558Srgrimes/* 5481558Srgrimes * pinger -- 5491558Srgrimes * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet 5501558Srgrimes * will be added on by the kernel. The ID field is our UNIX process ID, 5511558Srgrimes * and the sequence number is an ascending integer. The first 8 bytes 55217724Sfenner * of the data portion are used to hold a UNIX "timeval" struct in host 5531558Srgrimes * byte-order, to compute the round-trip time. 55427533Sbde * 55527533Sbde * bug -- 55627533Sbde * this does far too much to be called from a signal handler. 5571558Srgrimes */ 55823247Swollmanstatic void 55923247Swollmanpinger(void) 5601558Srgrimes{ 5611558Srgrimes register struct icmp *icp; 5621558Srgrimes register int cc; 5631558Srgrimes int i; 5641558Srgrimes 5651558Srgrimes icp = (struct icmp *)outpack; 5661558Srgrimes icp->icmp_type = ICMP_ECHO; 5671558Srgrimes icp->icmp_code = 0; 5681558Srgrimes icp->icmp_cksum = 0; 56927301Sjulian icp->icmp_seq = ntransmitted; 5701558Srgrimes icp->icmp_id = ident; /* ID */ 5711558Srgrimes 5721558Srgrimes CLR(icp->icmp_seq % mx_dup_ck); 5731558Srgrimes 5741558Srgrimes if (timing) 5751558Srgrimes (void)gettimeofday((struct timeval *)&outpack[8], 5761558Srgrimes (struct timezone *)NULL); 5771558Srgrimes 5781558Srgrimes cc = datalen + 8; /* skips ICMP portion */ 5791558Srgrimes 5801558Srgrimes /* compute ICMP checksum here */ 5811558Srgrimes icp->icmp_cksum = in_cksum((u_short *)icp, cc); 5821558Srgrimes 5831558Srgrimes i = sendto(s, (char *)outpack, cc, 0, &whereto, 5841558Srgrimes sizeof(struct sockaddr)); 5851558Srgrimes 5861558Srgrimes if (i < 0 || i != cc) { 58723247Swollman if (i < 0) { 58827533Sbde if (options & F_FLOOD && errno == ENOBUFS) { 58927299Sjulian usleep(FLOOD_BACKOFF); 59027299Sjulian return; 59127299Sjulian } 59223247Swollman warn("sendto"); 59323247Swollman } else { 59423247Swollman warn("%s: partial write: %d of %d bytes", 59523247Swollman hostname, cc, i); 59623247Swollman } 59727533Sbde } else 59827533Sbde ntransmitted++; /* only count those that made it out */ 5991558Srgrimes if (!(options & F_QUIET) && options & F_FLOOD) 6001558Srgrimes (void)write(STDOUT_FILENO, &DOT, 1); 6011558Srgrimes} 6021558Srgrimes 6031558Srgrimes/* 6041558Srgrimes * pr_pack -- 6051558Srgrimes * Print out the packet, if it came from us. This logic is necessary 6061558Srgrimes * because ALL readers of the ICMP socket get a copy of ALL ICMP packets 6071558Srgrimes * which arrive ('tis only fair). This permits multiple copies of this 6081558Srgrimes * program to be run without having intermingled output (or statistics!). 6091558Srgrimes */ 61023247Swollmanstatic void 6111558Srgrimespr_pack(buf, cc, from) 6121558Srgrimes char *buf; 6131558Srgrimes int cc; 6141558Srgrimes struct sockaddr_in *from; 6151558Srgrimes{ 6161558Srgrimes register struct icmp *icp; 6171558Srgrimes register u_long l; 6181558Srgrimes register int i, j; 6191558Srgrimes register u_char *cp,*dp; 6201558Srgrimes static int old_rrlen; 6211558Srgrimes static char old_rr[MAX_IPOPTLEN]; 6221558Srgrimes struct ip *ip; 6231558Srgrimes struct timeval tv, *tp; 62427533Sbde double triptime; 6251558Srgrimes int hlen, dupflag; 6261558Srgrimes 6271558Srgrimes (void)gettimeofday(&tv, (struct timezone *)NULL); 6281558Srgrimes 6291558Srgrimes /* Check the IP header */ 6301558Srgrimes ip = (struct ip *)buf; 6311558Srgrimes hlen = ip->ip_hl << 2; 6321558Srgrimes if (cc < hlen + ICMP_MINLEN) { 6331558Srgrimes if (options & F_VERBOSE) 63423247Swollman warn("packet too short (%d bytes) from %s", cc, 63523247Swollman inet_ntoa(from->sin_addr)); 6361558Srgrimes return; 6371558Srgrimes } 6381558Srgrimes 6391558Srgrimes /* Now the ICMP part */ 6401558Srgrimes cc -= hlen; 6411558Srgrimes icp = (struct icmp *)(buf + hlen); 6421558Srgrimes if (icp->icmp_type == ICMP_ECHOREPLY) { 6431558Srgrimes if (icp->icmp_id != ident) 6441558Srgrimes return; /* 'Twas not our ECHO */ 6451558Srgrimes ++nreceived; 64627533Sbde triptime = 0.0; 6471558Srgrimes if (timing) { 6481558Srgrimes#ifndef icmp_data 6491558Srgrimes tp = (struct timeval *)&icp->icmp_ip; 6501558Srgrimes#else 6511558Srgrimes tp = (struct timeval *)icp->icmp_data; 6521558Srgrimes#endif 6531558Srgrimes tvsub(&tv, tp); 6541558Srgrimes triptime = ((double)tv.tv_sec) * 1000.0 + 6551558Srgrimes ((double)tv.tv_usec) / 1000.0; 6561558Srgrimes tsum += triptime; 65727508Swollman tsumsq += triptime * triptime; 6581558Srgrimes if (triptime < tmin) 6591558Srgrimes tmin = triptime; 6601558Srgrimes if (triptime > tmax) 6611558Srgrimes tmax = triptime; 6621558Srgrimes } 6631558Srgrimes 6641558Srgrimes if (TST(icp->icmp_seq % mx_dup_ck)) { 6651558Srgrimes ++nrepeats; 6661558Srgrimes --nreceived; 6671558Srgrimes dupflag = 1; 6681558Srgrimes } else { 6691558Srgrimes SET(icp->icmp_seq % mx_dup_ck); 6701558Srgrimes dupflag = 0; 6711558Srgrimes } 6721558Srgrimes 6731558Srgrimes if (options & F_QUIET) 6741558Srgrimes return; 6751558Srgrimes 6761558Srgrimes if (options & F_FLOOD) 6771558Srgrimes (void)write(STDOUT_FILENO, &BSPACE, 1); 6781558Srgrimes else { 6791558Srgrimes (void)printf("%d bytes from %s: icmp_seq=%u", cc, 6801558Srgrimes inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), 6811558Srgrimes icp->icmp_seq); 6821558Srgrimes (void)printf(" ttl=%d", ip->ip_ttl); 6831558Srgrimes if (timing) 6841859Sdg (void)printf(" time=%.3f ms", triptime); 6851558Srgrimes if (dupflag) 6861558Srgrimes (void)printf(" (DUP!)"); 68722417Sdanny if (options & F_AUDIBLE) 68822417Sdanny (void)printf("\a"); 6891558Srgrimes /* check the data */ 6901558Srgrimes cp = (u_char*)&icp->icmp_data[8]; 6911558Srgrimes dp = &outpack[8 + sizeof(struct timeval)]; 6921558Srgrimes for (i = 8; i < datalen; ++i, ++cp, ++dp) { 6931558Srgrimes if (*cp != *dp) { 6941558Srgrimes (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", 6951558Srgrimes i, *dp, *cp); 6961558Srgrimes cp = (u_char*)&icp->icmp_data[0]; 6971558Srgrimes for (i = 8; i < datalen; ++i, ++cp) { 6981558Srgrimes if ((i % 32) == 8) 6991558Srgrimes (void)printf("\n\t"); 7001558Srgrimes (void)printf("%x ", *cp); 7011558Srgrimes } 7021558Srgrimes break; 7031558Srgrimes } 7041558Srgrimes } 7051558Srgrimes } 7061558Srgrimes } else { 70717724Sfenner /* 70817724Sfenner * We've got something other than an ECHOREPLY. 70917724Sfenner * See if it's a reply to something that we sent. 71017724Sfenner * We can compare IP destination, protocol, 71117724Sfenner * and ICMP type and ID. 71223251Simp * 71323251Simp * Only print all the error messages if we are running 71423251Simp * as root to avoid leaking information not normally 71523251Simp * available to those not running as root. 71617724Sfenner */ 71717724Sfenner#ifndef icmp_data 71817724Sfenner struct ip *oip = &icp->icmp_ip; 71917724Sfenner#else 72017724Sfenner struct ip *oip = (struct ip *)icp->icmp_data; 72117724Sfenner#endif 72217724Sfenner struct icmp *oicmp = (struct icmp *)(oip + 1); 72317724Sfenner 72423295Simp if (((options & F_VERBOSE) && uid == 0) || 72517724Sfenner (!(options & F_QUIET2) && 72617724Sfenner (oip->ip_dst.s_addr == 72717724Sfenner ((struct sockaddr_in *)&whereto)->sin_addr.s_addr) && 72817724Sfenner (oip->ip_p == IPPROTO_ICMP) && 72917724Sfenner (oicmp->icmp_type == ICMP_ECHO) && 73017724Sfenner (oicmp->icmp_id == ident))) { 73117724Sfenner (void)printf("%d bytes from %s: ", cc, 73223247Swollman pr_addr(from->sin_addr)); 73317724Sfenner pr_icmph(icp); 73417724Sfenner } else 73517724Sfenner return; 7361558Srgrimes } 7371558Srgrimes 7381558Srgrimes /* Display any IP options */ 7391558Srgrimes cp = (u_char *)buf + sizeof(struct ip); 7401558Srgrimes 7411558Srgrimes for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) 7421558Srgrimes switch (*cp) { 7431558Srgrimes case IPOPT_EOL: 7441558Srgrimes hlen = 0; 7451558Srgrimes break; 7461558Srgrimes case IPOPT_LSRR: 7471558Srgrimes (void)printf("\nLSRR: "); 7481558Srgrimes hlen -= 2; 7491558Srgrimes j = *++cp; 7501558Srgrimes ++cp; 7511558Srgrimes if (j > IPOPT_MINOFF) 7521558Srgrimes for (;;) { 7531558Srgrimes l = *++cp; 7541558Srgrimes l = (l<<8) + *++cp; 7551558Srgrimes l = (l<<8) + *++cp; 7561558Srgrimes l = (l<<8) + *++cp; 75723247Swollman if (l == 0) { 75823247Swollman printf("\t0.0.0.0"); 75923247Swollman } else { 76023247Swollman struct in_addr ina; 76123247Swollman ina.s_addr = ntohl(l); 76223247Swollman printf("\t%s", pr_addr(ina)); 76323247Swollman } 7641558Srgrimes hlen -= 4; 7651558Srgrimes j -= 4; 7661558Srgrimes if (j <= IPOPT_MINOFF) 7671558Srgrimes break; 7681558Srgrimes (void)putchar('\n'); 7691558Srgrimes } 7701558Srgrimes break; 7711558Srgrimes case IPOPT_RR: 7721558Srgrimes j = *++cp; /* get length */ 7731558Srgrimes i = *++cp; /* and pointer */ 7741558Srgrimes hlen -= 2; 7751558Srgrimes if (i > j) 7761558Srgrimes i = j; 7771558Srgrimes i -= IPOPT_MINOFF; 7781558Srgrimes if (i <= 0) 7791558Srgrimes continue; 7801558Srgrimes if (i == old_rrlen 7811558Srgrimes && cp == (u_char *)buf + sizeof(struct ip) + 2 7821558Srgrimes && !bcmp((char *)cp, old_rr, i) 7831558Srgrimes && !(options & F_FLOOD)) { 7841558Srgrimes (void)printf("\t(same route)"); 7851558Srgrimes i = ((i + 3) / 4) * 4; 7861558Srgrimes hlen -= i; 7871558Srgrimes cp += i; 7881558Srgrimes break; 7891558Srgrimes } 7901558Srgrimes old_rrlen = i; 7911558Srgrimes bcopy((char *)cp, old_rr, i); 7921558Srgrimes (void)printf("\nRR: "); 7931558Srgrimes for (;;) { 7941558Srgrimes l = *++cp; 7951558Srgrimes l = (l<<8) + *++cp; 7961558Srgrimes l = (l<<8) + *++cp; 7971558Srgrimes l = (l<<8) + *++cp; 79823247Swollman if (l == 0) { 79923247Swollman printf("\t0.0.0.0"); 80023247Swollman } else { 80123247Swollman struct in_addr ina; 80223247Swollman ina.s_addr = ntohl(l); 80323247Swollman printf("\t%s", pr_addr(ina)); 80423247Swollman } 8051558Srgrimes hlen -= 4; 8061558Srgrimes i -= 4; 8071558Srgrimes if (i <= 0) 8081558Srgrimes break; 8091558Srgrimes (void)putchar('\n'); 8101558Srgrimes } 8111558Srgrimes break; 8121558Srgrimes case IPOPT_NOP: 8131558Srgrimes (void)printf("\nNOP"); 8141558Srgrimes break; 8151558Srgrimes default: 8161558Srgrimes (void)printf("\nunknown option %x", *cp); 8171558Srgrimes break; 8181558Srgrimes } 8191558Srgrimes if (!(options & F_FLOOD)) { 8201558Srgrimes (void)putchar('\n'); 8211558Srgrimes (void)fflush(stdout); 8221558Srgrimes } 8231558Srgrimes} 8241558Srgrimes 8251558Srgrimes/* 8261558Srgrimes * in_cksum -- 8271558Srgrimes * Checksum routine for Internet Protocol family headers (C Version) 8281558Srgrimes */ 82923247Swollmanu_short 8301558Srgrimesin_cksum(addr, len) 8311558Srgrimes u_short *addr; 8321558Srgrimes int len; 8331558Srgrimes{ 8341558Srgrimes register int nleft = len; 8351558Srgrimes register u_short *w = addr; 8361558Srgrimes register int sum = 0; 8371558Srgrimes u_short answer = 0; 8381558Srgrimes 8391558Srgrimes /* 8401558Srgrimes * Our algorithm is simple, using a 32 bit accumulator (sum), we add 8411558Srgrimes * sequential 16 bit words to it, and at the end, fold back all the 8421558Srgrimes * carry bits from the top 16 bits into the lower 16 bits. 8431558Srgrimes */ 8441558Srgrimes while (nleft > 1) { 8451558Srgrimes sum += *w++; 8461558Srgrimes nleft -= 2; 8471558Srgrimes } 8481558Srgrimes 8491558Srgrimes /* mop up an odd byte, if necessary */ 8501558Srgrimes if (nleft == 1) { 8511558Srgrimes *(u_char *)(&answer) = *(u_char *)w ; 8521558Srgrimes sum += answer; 8531558Srgrimes } 8541558Srgrimes 8551558Srgrimes /* add back carry outs from top 16 bits to low 16 bits */ 8561558Srgrimes sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 8571558Srgrimes sum += (sum >> 16); /* add carry */ 8581558Srgrimes answer = ~sum; /* truncate to 16 bits */ 8591558Srgrimes return(answer); 8601558Srgrimes} 8611558Srgrimes 8621558Srgrimes/* 8631558Srgrimes * tvsub -- 8641558Srgrimes * Subtract 2 timeval structs: out = out - in. Out is assumed to 8651558Srgrimes * be >= in. 8661558Srgrimes */ 86723247Swollmanstatic void 8681558Srgrimestvsub(out, in) 8691558Srgrimes register struct timeval *out, *in; 8701558Srgrimes{ 8711558Srgrimes if ((out->tv_usec -= in->tv_usec) < 0) { 8721558Srgrimes --out->tv_sec; 8731558Srgrimes out->tv_usec += 1000000; 8741558Srgrimes } 8751558Srgrimes out->tv_sec -= in->tv_sec; 8761558Srgrimes} 8771558Srgrimes 8781558Srgrimes/* 8793792Ssef * status -- 8803792Ssef * Print out statistics when SIGINFO is received. 8813792Ssef */ 8823792Ssef 88323247Swollmanstatic void 88420280Sbdestatus(sig) 88520280Sbde int sig; 88620280Sbde{ 88720195Ssef siginfo_p = 1; 88820195Ssef} 88920195Ssef 89023247Swollmanstatic void 89120195Ssefcheck_status() 8923792Ssef{ 89320195Ssef if (siginfo_p) { 89420195Ssef siginfo_p = 0; 89520280Sbde (void)fprintf(stderr, 89620280Sbde "\r%ld/%ld packets received (%.0f%%) %.3f min / %.3f avg / %.3f max\n", 89720280Sbde nreceived, ntransmitted, 89820280Sbde ntransmitted ? nreceived * 100.0 / ntransmitted : 0.0, 89920280Sbde nreceived ? tmin : 0.0, 90020280Sbde nreceived + nrepeats ? tsum / (nreceived + nrepeats) : tsum, 90120280Sbde tmax); 90220195Ssef } 9033792Ssef} 9043792Ssef 9053792Ssef/* 9061558Srgrimes * finish -- 9071558Srgrimes * Print out statistics, and give up. 9081558Srgrimes */ 90923247Swollmanstatic void 91027533Sbdefinish() 9111558Srgrimes{ 9123792Ssef struct termios ts; 9131558Srgrimes 9141558Srgrimes (void)signal(SIGINT, SIG_IGN); 91527354Ssef (void)signal(SIGALRM, SIG_IGN); 9161558Srgrimes (void)putchar('\n'); 9171558Srgrimes (void)fflush(stdout); 9181558Srgrimes (void)printf("--- %s ping statistics ---\n", hostname); 9191558Srgrimes (void)printf("%ld packets transmitted, ", ntransmitted); 9201558Srgrimes (void)printf("%ld packets received, ", nreceived); 9211558Srgrimes if (nrepeats) 9221558Srgrimes (void)printf("+%ld duplicates, ", nrepeats); 9231558Srgrimes if (ntransmitted) 9241558Srgrimes if (nreceived > ntransmitted) 9251558Srgrimes (void)printf("-- somebody's printing up packets!"); 9261558Srgrimes else 9271558Srgrimes (void)printf("%d%% packet loss", 9281558Srgrimes (int) (((ntransmitted - nreceived) * 100) / 9291558Srgrimes ntransmitted)); 9301558Srgrimes (void)putchar('\n'); 93127508Swollman if (nreceived && timing) { 93227508Swollman double n = nreceived + nrepeats; 93327508Swollman double avg = tsum / n; 93427508Swollman double vari = tsumsq / n - avg * avg; 93527508Swollman printf("round-trip min/avg/max/stddev = " 93627508Swollman "%.3f/%.3f/%.3f/%.3f ms\n", 93727508Swollman tmin, avg, tmax, sqrt(vari)); 93827508Swollman } 93919395Sbde if (reset_kerninfo && tcgetattr(STDOUT_FILENO, &ts) != -1) { 9403792Ssef ts.c_lflag &= ~NOKERNINFO; 94119395Sbde tcsetattr(STDOUT_FILENO, TCSANOW, &ts); 9423792Ssef } 9433792Ssef 9448871Srgrimes if (nreceived) 9454862Sdg exit(0); 9464862Sdg else 9474862Sdg exit(2); 9481558Srgrimes} 9491558Srgrimes 9501558Srgrimes#ifdef notdef 9511558Srgrimesstatic char *ttab[] = { 9521558Srgrimes "Echo Reply", /* ip + seq + udata */ 9531558Srgrimes "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ 9541558Srgrimes "Source Quench", /* IP */ 9551558Srgrimes "Redirect", /* redirect type, gateway, + IP */ 9561558Srgrimes "Echo", 9571558Srgrimes "Time Exceeded", /* transit, frag reassem + IP */ 9581558Srgrimes "Parameter Problem", /* pointer + IP */ 9591558Srgrimes "Timestamp", /* id + seq + three timestamps */ 9601558Srgrimes "Timestamp Reply", /* " */ 9611558Srgrimes "Info Request", /* id + sq */ 9621558Srgrimes "Info Reply" /* " */ 9631558Srgrimes}; 9641558Srgrimes#endif 9651558Srgrimes 9661558Srgrimes/* 9671558Srgrimes * pr_icmph -- 9681558Srgrimes * Print a descriptive string about an ICMP header. 9691558Srgrimes */ 97023247Swollmanstatic void 9711558Srgrimespr_icmph(icp) 9721558Srgrimes struct icmp *icp; 9731558Srgrimes{ 9741558Srgrimes switch(icp->icmp_type) { 9751558Srgrimes case ICMP_ECHOREPLY: 9761558Srgrimes (void)printf("Echo Reply\n"); 9771558Srgrimes /* XXX ID + Seq + Data */ 9781558Srgrimes break; 9791558Srgrimes case ICMP_UNREACH: 9801558Srgrimes switch(icp->icmp_code) { 9811558Srgrimes case ICMP_UNREACH_NET: 9821558Srgrimes (void)printf("Destination Net Unreachable\n"); 9831558Srgrimes break; 9841558Srgrimes case ICMP_UNREACH_HOST: 9851558Srgrimes (void)printf("Destination Host Unreachable\n"); 9861558Srgrimes break; 9871558Srgrimes case ICMP_UNREACH_PROTOCOL: 9881558Srgrimes (void)printf("Destination Protocol Unreachable\n"); 9891558Srgrimes break; 9901558Srgrimes case ICMP_UNREACH_PORT: 9911558Srgrimes (void)printf("Destination Port Unreachable\n"); 9921558Srgrimes break; 9931558Srgrimes case ICMP_UNREACH_NEEDFRAG: 99417724Sfenner (void)printf("frag needed and DF set (MTU %d)\n", 99517724Sfenner icp->icmp_nextmtu); 9961558Srgrimes break; 9971558Srgrimes case ICMP_UNREACH_SRCFAIL: 9981558Srgrimes (void)printf("Source Route Failed\n"); 9991558Srgrimes break; 100017724Sfenner case ICMP_UNREACH_FILTER_PROHIB: 100117724Sfenner (void)printf("Communication prohibited by filter\n"); 100217724Sfenner break; 10031558Srgrimes default: 10041558Srgrimes (void)printf("Dest Unreachable, Bad Code: %d\n", 10051558Srgrimes icp->icmp_code); 10061558Srgrimes break; 10071558Srgrimes } 10081558Srgrimes /* Print returned IP header information */ 10091558Srgrimes#ifndef icmp_data 10101558Srgrimes pr_retip(&icp->icmp_ip); 10111558Srgrimes#else 10121558Srgrimes pr_retip((struct ip *)icp->icmp_data); 10131558Srgrimes#endif 10141558Srgrimes break; 10151558Srgrimes case ICMP_SOURCEQUENCH: 10161558Srgrimes (void)printf("Source Quench\n"); 10171558Srgrimes#ifndef icmp_data 10181558Srgrimes pr_retip(&icp->icmp_ip); 10191558Srgrimes#else 10201558Srgrimes pr_retip((struct ip *)icp->icmp_data); 10211558Srgrimes#endif 10221558Srgrimes break; 10231558Srgrimes case ICMP_REDIRECT: 10241558Srgrimes switch(icp->icmp_code) { 10251558Srgrimes case ICMP_REDIRECT_NET: 10261558Srgrimes (void)printf("Redirect Network"); 10271558Srgrimes break; 10281558Srgrimes case ICMP_REDIRECT_HOST: 10291558Srgrimes (void)printf("Redirect Host"); 10301558Srgrimes break; 10311558Srgrimes case ICMP_REDIRECT_TOSNET: 10321558Srgrimes (void)printf("Redirect Type of Service and Network"); 10331558Srgrimes break; 10341558Srgrimes case ICMP_REDIRECT_TOSHOST: 10351558Srgrimes (void)printf("Redirect Type of Service and Host"); 10361558Srgrimes break; 10371558Srgrimes default: 10381558Srgrimes (void)printf("Redirect, Bad Code: %d", icp->icmp_code); 10391558Srgrimes break; 10401558Srgrimes } 10411558Srgrimes (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr); 10421558Srgrimes#ifndef icmp_data 10431558Srgrimes pr_retip(&icp->icmp_ip); 10441558Srgrimes#else 10451558Srgrimes pr_retip((struct ip *)icp->icmp_data); 10461558Srgrimes#endif 10471558Srgrimes break; 10481558Srgrimes case ICMP_ECHO: 10491558Srgrimes (void)printf("Echo Request\n"); 10501558Srgrimes /* XXX ID + Seq + Data */ 10511558Srgrimes break; 10521558Srgrimes case ICMP_TIMXCEED: 10531558Srgrimes switch(icp->icmp_code) { 10541558Srgrimes case ICMP_TIMXCEED_INTRANS: 10551558Srgrimes (void)printf("Time to live exceeded\n"); 10561558Srgrimes break; 10571558Srgrimes case ICMP_TIMXCEED_REASS: 10581558Srgrimes (void)printf("Frag reassembly time exceeded\n"); 10591558Srgrimes break; 10601558Srgrimes default: 10611558Srgrimes (void)printf("Time exceeded, Bad Code: %d\n", 10621558Srgrimes icp->icmp_code); 10631558Srgrimes break; 10641558Srgrimes } 10651558Srgrimes#ifndef icmp_data 10661558Srgrimes pr_retip(&icp->icmp_ip); 10671558Srgrimes#else 10681558Srgrimes pr_retip((struct ip *)icp->icmp_data); 10691558Srgrimes#endif 10701558Srgrimes break; 10711558Srgrimes case ICMP_PARAMPROB: 10721558Srgrimes (void)printf("Parameter problem: pointer = 0x%02x\n", 10731558Srgrimes icp->icmp_hun.ih_pptr); 10741558Srgrimes#ifndef icmp_data 10751558Srgrimes pr_retip(&icp->icmp_ip); 10761558Srgrimes#else 10771558Srgrimes pr_retip((struct ip *)icp->icmp_data); 10781558Srgrimes#endif 10791558Srgrimes break; 10801558Srgrimes case ICMP_TSTAMP: 10811558Srgrimes (void)printf("Timestamp\n"); 10821558Srgrimes /* XXX ID + Seq + 3 timestamps */ 10831558Srgrimes break; 10841558Srgrimes case ICMP_TSTAMPREPLY: 10851558Srgrimes (void)printf("Timestamp Reply\n"); 10861558Srgrimes /* XXX ID + Seq + 3 timestamps */ 10871558Srgrimes break; 10881558Srgrimes case ICMP_IREQ: 10891558Srgrimes (void)printf("Information Request\n"); 10901558Srgrimes /* XXX ID + Seq */ 10911558Srgrimes break; 10921558Srgrimes case ICMP_IREQREPLY: 10931558Srgrimes (void)printf("Information Reply\n"); 10941558Srgrimes /* XXX ID + Seq */ 10951558Srgrimes break; 10961558Srgrimes case ICMP_MASKREQ: 10971558Srgrimes (void)printf("Address Mask Request\n"); 10981558Srgrimes break; 10991558Srgrimes case ICMP_MASKREPLY: 11001558Srgrimes (void)printf("Address Mask Reply\n"); 11011558Srgrimes break; 110217724Sfenner case ICMP_ROUTERADVERT: 110317724Sfenner (void)printf("Router Advertisement\n"); 110417724Sfenner break; 110517724Sfenner case ICMP_ROUTERSOLICIT: 110617724Sfenner (void)printf("Router Solicitation\n"); 110717724Sfenner break; 11081558Srgrimes default: 11091558Srgrimes (void)printf("Bad ICMP type: %d\n", icp->icmp_type); 11101558Srgrimes } 11111558Srgrimes} 11121558Srgrimes 11131558Srgrimes/* 11141558Srgrimes * pr_iph -- 11151558Srgrimes * Print an IP header with options. 11161558Srgrimes */ 111723247Swollmanstatic void 11181558Srgrimespr_iph(ip) 11191558Srgrimes struct ip *ip; 11201558Srgrimes{ 11211558Srgrimes int hlen; 11221558Srgrimes u_char *cp; 11231558Srgrimes 11241558Srgrimes hlen = ip->ip_hl << 2; 11251558Srgrimes cp = (u_char *)ip + 20; /* point to options */ 11261558Srgrimes 112717724Sfenner (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst\n"); 11281558Srgrimes (void)printf(" %1x %1x %02x %04x %04x", 112917724Sfenner ip->ip_v, ip->ip_hl, ip->ip_tos, ntohs(ip->ip_len), 113017724Sfenner ntohs(ip->ip_id)); 113123247Swollman (void)printf(" %1lx %04lx", (ntohl(ip->ip_off) & 0xe000) >> 13, 113217724Sfenner ntohl(ip->ip_off) & 0x1fff); 113317724Sfenner (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, 113417724Sfenner ntohs(ip->ip_sum)); 11351558Srgrimes (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); 11361558Srgrimes (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); 113717724Sfenner /* dump any option bytes */ 11381558Srgrimes while (hlen-- > 20) { 11391558Srgrimes (void)printf("%02x", *cp++); 11401558Srgrimes } 11411558Srgrimes (void)putchar('\n'); 11421558Srgrimes} 11431558Srgrimes 11441558Srgrimes/* 11451558Srgrimes * pr_addr -- 11461558Srgrimes * Return an ascii host address as a dotted quad and optionally with 11471558Srgrimes * a hostname. 11481558Srgrimes */ 114923247Swollmanstatic char * 115023247Swollmanpr_addr(ina) 115123247Swollman struct in_addr ina; 11521558Srgrimes{ 11531558Srgrimes struct hostent *hp; 115423251Simp static char buf[16 + 3 + MAXHOSTNAMELEN]; 11551558Srgrimes 11561558Srgrimes if ((options & F_NUMERIC) || 115723247Swollman !(hp = gethostbyaddr((char *)&ina, 4, AF_INET))) 115823247Swollman return inet_ntoa(ina); 11591558Srgrimes else 116017320Speter (void)snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name, 116123247Swollman inet_ntoa(ina)); 11621558Srgrimes return(buf); 11631558Srgrimes} 11641558Srgrimes 11651558Srgrimes/* 11661558Srgrimes * pr_retip -- 11671558Srgrimes * Dump some info on a returned (via ICMP) IP packet. 11681558Srgrimes */ 116923247Swollmanstatic void 11701558Srgrimespr_retip(ip) 11711558Srgrimes struct ip *ip; 11721558Srgrimes{ 11731558Srgrimes int hlen; 11741558Srgrimes u_char *cp; 11751558Srgrimes 11761558Srgrimes pr_iph(ip); 11771558Srgrimes hlen = ip->ip_hl << 2; 11781558Srgrimes cp = (u_char *)ip + hlen; 11791558Srgrimes 11801558Srgrimes if (ip->ip_p == 6) 11811558Srgrimes (void)printf("TCP: from port %u, to port %u (decimal)\n", 11821558Srgrimes (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); 11831558Srgrimes else if (ip->ip_p == 17) 11841558Srgrimes (void)printf("UDP: from port %u, to port %u (decimal)\n", 11851558Srgrimes (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); 11861558Srgrimes} 11871558Srgrimes 118823247Swollmanstatic void 11891558Srgrimesfill(bp, patp) 11901558Srgrimes char *bp, *patp; 11911558Srgrimes{ 11921558Srgrimes register int ii, jj, kk; 11931558Srgrimes int pat[16]; 11941558Srgrimes char *cp; 11951558Srgrimes 119623247Swollman for (cp = patp; *cp; cp++) { 119723247Swollman if (!isxdigit(*cp)) 119823247Swollman errx(EX_USAGE, 119923247Swollman "patterns must be specified as hex digits"); 120023247Swollman 120123247Swollman } 12021558Srgrimes ii = sscanf(patp, 12031558Srgrimes "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", 12041558Srgrimes &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], 12051558Srgrimes &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], 12061558Srgrimes &pat[13], &pat[14], &pat[15]); 12071558Srgrimes 12081558Srgrimes if (ii > 0) 12091558Srgrimes for (kk = 0; 12101558Srgrimes kk <= MAXPACKET - (8 + sizeof(struct timeval) + ii); 12111558Srgrimes kk += ii) 12121558Srgrimes for (jj = 0; jj < ii; ++jj) 12131558Srgrimes bp[jj + kk] = pat[jj]; 12141558Srgrimes if (!(options & F_QUIET)) { 12151558Srgrimes (void)printf("PATTERN: 0x"); 12161558Srgrimes for (jj = 0; jj < ii; ++jj) 12171558Srgrimes (void)printf("%02x", bp[jj] & 0xFF); 12181558Srgrimes (void)printf("\n"); 12191558Srgrimes } 12201558Srgrimes} 12211558Srgrimes 122223247Swollmanstatic void 122323247Swollmanusage(argv0) 122423247Swollman const char *argv0; 12251558Srgrimes{ 122623251Simp if (strrchr(argv0,'/')) 122723251Simp argv0 = strrchr(argv0,'/') + 1; 122823247Swollman fprintf(stderr, 122923251Simp "usage: %s [-QRadfnqrv] [-c count] [-i wait] [-l preload] " 123023295Simp "[-p pattern]\n [-s packetsize] " 123123247Swollman "[host | [-L] [-I iface] [-T ttl] mcast-group]\n", 123223247Swollman argv0); 123323247Swollman exit(EX_USAGE); 12341558Srgrimes} 1235