mogul@Shasta.ARPA (03/12/85)
#! /bin/sh : This is a shar archive. Extract with sh, not csh. echo x - README cat > README << '17645!Funky!Stuff!' "Ping" is used to send ICMP Echo Requests to an IP host. It was originally written by Larry Allen of MIT, and has been improved and debugged by Jeff Mogul at Stanford. It is quite useful for checking to see if a host or gateway is up and functioning. We use it all the time for debugging network problems. You cannot run this program with an unmodified 4.2BSD kernel. You must change /sys/netinet/in_proto.c to give user programs access to ICMP packets: *** in_proto.c.old Fri Jul 29 07:14:46 1983 --- in_proto.c Sat Dec 10 16:41:51 1983 *************** *** 46,54 0, ip_init, 0, ip_slowtimo, ip_drain, }, ! { 0, PF_INET, IPPROTO_ICMP, 0, ! icmp_input, 0, 0, 0, ! 0, 0, 0, 0, 0, }, { SOCK_DGRAM, PF_INET, IPPROTO_UDP, PR_ATOMIC|PR_ADDR, --- 46,54 ----- 0, ip_init, 0, ip_slowtimo, ip_drain, }, ! { SOCK_RAW, PF_INET, IPPROTO_ICMP, PR_ATOMIC|PR_ADDR, ! icmp_input, rip_output, 0, 0, ! raw_usrreq, 0, 0, 0, 0, }, { SOCK_DGRAM, PF_INET, IPPROTO_UDP, PR_ATOMIC|PR_ADDR, Note: your line numbers may vary. Also, the /sys/netinet/ip_icmp.c shipped with 4.2 had a number of bugs, some of which cause Unix to crash. All have been described on net.bugs.4bsd; if you use "ping" without having fixed these bugs, beware! "Ping" must be run by the super-user; the makefile included installs it with the setuid bit set, so that anybody can use it. 17645!Funky!Stuff! echo x - ping.1 cat > ping.1 << '17645!Funky!Stuff!' .TH PING 1 .SH NAME ping \- IP/ICMP echo user program .SH SYNOPSIS .B ping [ \-cnnn .B ] [ \-snnn .B ] [ \-t .B ] [ \-q .B ] hostname .B [ ... .B ] .SH DESCRIPTION .I Ping is used to send an ICMP (Internet Control Message Protocol) ``Echo Me'' packet to a host; it waits for a reply to see if the host responds. Since every IP host is required to respond to ICMP packets, this is a simple way to determine if a host is up. .PP If more than one host name argument is given, the hosts are pinged in order. .SH OPTIONS The following options are recognized. Note that numeric arguments follow the option flags immediately, without intervening spaces. .IP "\-cnnn" 1i For each host specified, send the echo .I nnn times. If .I nnn is zero or not given, then send the echo (practically) forever. .IP "\-snnn" 1i Make the packets .I nnn bytes long. .IP "\-t" 1i Print timing information, in milliseconds per round trip. .IP "\-q" 1i (Quiet mode) Don't print a line per packet, print summary line only. .SH BUGS By changing the default length you may create a situation where Unix may send the echo packet but will drop the response, thus confusing the issue. .PP Since the Unix hostname software is abysmally slow, it often takes longer to look up the hostname than it does to exchange packets. Specifying a host number (e.g., 10.1.0.11) works faster. .PP Timing resolution is 10 mSec; (un)fortunately round trip times are usually longer than this. 17645!Funky!Stuff! echo x - makefile cat > makefile << '17645!Funky!Stuff!' # makefile # for ping (ICMP echo) program # # 14 December 1983 Jeffrey Mogul Stanford # DESTDIR= /bin CFLAGS= -O ping: ping.o resolve_host.o cc -o ping ping.o resolve_host.o install: ping install -s ping $(DESTDIR)/ping chown root $(DESTDIR)/ping chmod u+s $(DESTDIR)/ping clean: rm -f *.o *.BAK *.CKP ping ping.shar a.out core shar: shar README ping.1 makefile ping.c resolve_host.c >ping.shar 17645!Funky!Stuff! echo x - ping.c cat > ping.c << '17645!Funky!Stuff!' /* * ping.c * * Send ICMP Echo Request packets. * * HISTORY: * 7 March 1985 Jeffrey Mogul Stanford * - Fixed sign-extension bug in non-VAX version of checksum * routine. * 27 February 1985 Jeffrey Mogul Stanford * - Avoid all-zero ICMP messages (tickles TOPS-20 bug) * (well, the bug is partly in 4.2BSD) * - flush stdout after each host, for logging to disk * 22 February 1985 Jeff Mogul Stanford * - per request, added INTR trapping * - -c0 means "nearly infinite count" * 20 February 1985 Jeff Mogul Stanford * - added -t (round-trip time), -q (quiet) options * 14 December 1983 Jeffrey Mogul Stanford * - added -c [count] and -s [packet size] options * * ? November 1983 Larry Allen MIT * - Created. */ #include <stdio.h> #include <sys/types.h> #include <sys/time.h> #include <errno.h> #include <signal.h> #include <sys/socket.h> #include <netdb.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #define MAXPKTSIZ 576 #define DATASIZE sizeof(struct ip) + 16 #define TIMEOUT 5 #define MAXINT (((unsigned long)(-1))>>1) /* works on a two's-complement machine */ struct pingpkt { struct icmp p_hdr; char p_data[DATASIZE]; } outpkt; u_char inbuf[MAXPKTSIZ]; struct sockaddr_in sa; /* foreign sockaddr */ char *fname; extern errno; int quiet = 0; int interrupted = 0; main(argc, argv) int argc; char *argv[]; { register int s; register struct sockaddr_in *sap; register struct protoent *pr; register int i,j; extern int alarm_int(); extern int intr_int(); extern struct sockaddr_in *resolve_host(); int packet_size = sizeof(outpkt); int repeat_count = 1; char *cmd_name = argv[0]; char *buffer; int timed = 0; double rtt; /* round-trip time in milliseconds */ double total_time; double max_time; double min_time; int successes; while ((argc > 2) && (argv[1][0] == '-')) { switch (argv[1][1]) { case 'c': /* -cnnn */ repeat_count = atoi(&argv[1][2]); if (repeat_count == 0) repeat_count = MAXINT; break; case 'q': /* -q */ quiet++; break; case 's': /* -snnn */ packet_size = atoi(&argv[1][2]); if (packet_size < sizeof(long)) { fprintf(stderr, "%s: -s%d is too short\n", cmd_name, packet_size); exit(1); } break; case 't': /* -t */ timed++; break; default: fprintf(stderr, "%s: Unknown flag -%c\n", cmd_name, argv[1][1]); Usage(cmd_name); exit(1); } argc--; argv++; } if (argc < 2) { Usage(cmd_name); exit (1); } signal (SIGALRM, alarm_int); if ((pr = getprotobyname("icmp")) == NULL) { perror("Can't resolve icmp protocol"); exit(1); } if ((buffer = (char *)malloc(packet_size + sizeof(outpkt))) == 0) { perror("malloc failure"); exit(1); } for (i = 1; (i < argc) && (!interrupted) ; i++) { fname = argv[i]; if ((sap = resolve_host(fname)) == NULL) { printf("Don't know host %s.\n", fname); continue; } sa = *sap; /* copy the beast */ if ((s = socket(sa.sin_family, SOCK_RAW, pr->p_proto)) < 0) { perror("Can't open socket"); exit(1); } outpkt.p_hdr.icmp_type = ICMP_ECHO; outpkt.p_hdr.icmp_code = 0; #ifndef SEND_ZEROS /* * Some TOPS-20 systems can't handle all-zero ICMPs, * so we set the ID and Sequence fields non-zero. */ outpkt.p_hdr.icmp_seq = 1; outpkt.p_hdr.icmp_id = getpid()&0xFFFF; #endif SEND_ZEROS outpkt.p_hdr.icmp_cksum = 0; outpkt.p_hdr.icmp_cksum = ~cksum (&outpkt, (sizeof (outpkt) + 1) >> 1); if (connect(s, &sa, sizeof(sa)) < 0) { perror("connect error"); exit(1); } bcopy(&outpkt, buffer, sizeof(outpkt)); total_time = 0.0; max_time = 0.0; min_time = TIMEOUT*2000.0; /* twice max possible */ successes = 0; signal (SIGINT, intr_int); for (j = 0; (j < repeat_count) && (!interrupted); j++) { if (timed) { if (DoPing(s, buffer, packet_size, &rtt)) { total_time += rtt; if (rtt > max_time) max_time = rtt; if (rtt < min_time) min_time = rtt; successes++; } } else if (DoPing(s, buffer, packet_size, 0)) successes++; } if ((repeat_count > 1) || quiet) { /* print summary line only if it is interesting */ printf(" %s: %d/%d successes", fname, successes, j); if (successes) printf(" (%.0f%%)", (successes * 100.0)/(j + 0.0)); if (timed) { if (successes) { printf(": %.0f min, %.0f max, %.0f avg. mSec/pkt", min_time, max_time, total_time/(successes + 0.0)); } } printf("\n"); fflush(stdout); signal (SIGINT, SIG_DFL); } close(s); } exit(0); } DoPing(s, buf, size, rttp) int s; char *buf; int size; double *rttp; { register struct icmp *picmp; struct timeval start, finish; if (rttp) gettimeofday(&start, 0); if (send(s, buf, size, 0) != size) { perror("send error"); exit(1); } alarm(TIMEOUT); for (;;) { if (recv(s, inbuf, MAXPKTSIZ, 0) <= 0) { if (errno == EINTR) return(0); perror("recv error"); exit(1); } picmp = (struct icmp *)inbuf; if (picmp->icmp_type == ICMP_ECHOREPLY) { alarm(0); if (rttp) { long rtt_usec; gettimeofday(&finish, 0); rtt_usec = finish.tv_usec - start.tv_usec; rtt_usec += 1000000 * (finish.tv_sec - start.tv_sec); *rttp = (rtt_usec + 0.0)/1000.0; if (!quiet) printf ("%s responding, %.0f mSec.\n", fname, *rttp); } else if (!quiet) printf ("%s responding.\n", fname); return(1); } } } int alarm_int (signo) int signo; { if (!quiet) printf("%s not responding\n", fname); } int intr_int (signo) int signo; { if (!quiet) printf("\n"); interrupted++; } int cksum (ibuf, ilen) /* compute the 16-bit one's complement checksum of the specified * buffer. The buffer length is specified in 16-bit words. */ short *ibuf; int ilen; { register unsigned short *buf = (unsigned short *)ibuf; register int len = ilen; register int sum; sum = 0; while (len-- > 0) { #ifdef VAX asm ("addw2 (r11)+,r9"); asm ("adwc $0, r9"); #else sum += *buf++; /* *buf unsigned, so no sign extension */ if (sum & 0x10000) /* add 1 (end-around carry) and subtract the 0x10000 */ sum -= 0xFFFF; #endif } return (sum & 0xFFFF); } Usage(us) char *us; { fprintf(stderr, "usage: %s [-cnnn] [-snnn] [-t] [-q] host [...]\n", us); } 17645!Funky!Stuff! echo x - resolve_host.c cat > resolve_host.c << '17645!Funky!Stuff!' /* resolve_host.c */ #include <stdio.h> #include <sys/types.h> #include <errno.h> #include <signal.h> #include <sys/socket.h> #include <netdb.h> #include <ctype.h> #include <netinet/in.h> struct sockaddr_in * resolve_host(name) /* Resolve the specified host name into an internet address. The "name" may * be either a character string name, or an address in the form a.b.c.d where * the pieces are octal, decimal, or hex numbers. Returns a pointer to a * sockaddr_in struct (note this structure is statically allocated and must * be copied), or NULL if the name is unknown. */ register char *name; { register struct hostent *fhost; struct in_addr fadd; static struct sockaddr_in sa; if (!(isdigit(name[0])) && ((fhost = gethostbyname(name)) != NULL)) { sa.sin_family = fhost->h_addrtype; sa.sin_port = 0; bcopy(fhost->h_addr, &sa.sin_addr, fhost->h_length); } else { fadd.s_addr = inet_addr(name); if ((fadd.s_addr != -1) || !strcmp(name, "255.255.255.255")) { sa.sin_family = AF_INET; /* grot */ sa.sin_port = 0; sa.sin_addr.s_addr = fadd.s_addr; } else return(NULL); } return(&sa); } 17645!Funky!Stuff!