Annotation of 43BSDTahoe/etc/ping.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (c) 1987 Regents of the University of California.
                      3:  * All rights reserved.
                      4:  *
                      5:  * Redistribution and use in source and binary forms are permitted
                      6:  * provided that the above copyright notice and this paragraph are
                      7:  * duplicated in all such forms and that any documentation,
                      8:  * advertising materials, and other materials related to such
                      9:  * distribution and use acknowledge that the software was developed
                     10:  * by the University of California, Berkeley.  The name of the
                     11:  * University may not be used to endorse or promote products derived
                     12:  * from this software without specific prior written permission.
                     13:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
                     14:  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
                     15:  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
                     16:  */
                     17: 
                     18: #ifndef lint
                     19: char copyright[] =
                     20: "@(#) Copyright (c) 1987 Regents of the University of California.\n\
                     21:  All rights reserved.\n";
                     22: #endif /* not lint */
                     23: 
                     24: #ifndef lint
                     25: static char sccsid[] = "@(#)ping.c     4.9 (Berkeley) 6/18/88";
                     26: #endif /* not lint */
                     27: 
                     28: /*
                     29:  *                     P I N G . C
                     30:  *
                     31:  * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
                     32:  * measure round-trip-delays and packet loss across network paths.
                     33:  *
                     34:  * Author -
                     35:  *     Mike Muuss
                     36:  *     U. S. Army Ballistic Research Laboratory
                     37:  *     December, 1983
                     38:  * Modified at Uc Berkeley
                     39:  *
                     40:  * Status -
                     41:  *     Public Domain.  Distribution Unlimited.
                     42:  *
                     43:  * Bugs -
                     44:  *     More statistics could always be gathered.
                     45:  *     This program has to run SUID to ROOT to access the ICMP socket.
                     46:  */
                     47: 
                     48: #include <stdio.h>
                     49: #include <errno.h>
                     50: #include <sys/time.h>
                     51: 
                     52: #include <sys/param.h>
                     53: #include <sys/socket.h>
                     54: #include <sys/file.h>
                     55: 
                     56: #include <netinet/in_systm.h>
                     57: #include <netinet/in.h>
                     58: #include <netinet/ip.h>
                     59: #include <netinet/ip_icmp.h>
                     60: #include <netdb.h>
                     61: 
                     62: #define        MAXWAIT         10      /* max time to wait for response, sec. */
                     63: #define        MAXPACKET       4096    /* max packet size */
                     64: #ifndef MAXHOSTNAMELEN
                     65: #define MAXHOSTNAMELEN 64
                     66: #endif
                     67: 
                     68: int    verbose;
                     69: u_char packet[MAXPACKET];
                     70: int    options;
                     71: extern int errno;
                     72: 
                     73: int s;                 /* Socket file descriptor */
                     74: struct hostent *hp;    /* Pointer to host info */
                     75: struct timezone tz;    /* leftover */
                     76: 
                     77: struct sockaddr whereto;/* Who to ping */
                     78: int datalen;           /* How much data */
                     79: 
                     80: char usage[] = "Usage:  ping [-drv] host [data size] [npackets]\n";
                     81: 
                     82: char *hostname;
                     83: char hnamebuf[MAXHOSTNAMELEN];
                     84: char *inet_ntoa();
                     85: 
                     86: int npackets;
                     87: int ntransmitted = 0;          /* sequence # for outbound packets = #sent */
                     88: int ident;
                     89: 
                     90: int nreceived = 0;             /* # of packets we got back */
                     91: int timing = 0;
                     92: int tmin = 999999999;
                     93: int tmax = 0;
                     94: int tsum = 0;                  /* sum of all times, for doing average */
                     95: int finish(), catcher();
                     96: 
                     97: /*
                     98:  *                     M A I N
                     99:  */
                    100: main(argc, argv)
                    101: char *argv[];
                    102: {
                    103:        struct sockaddr_in from;
                    104:        char **av = argv;
                    105:        char *toaddr = NULL;
                    106:        struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
                    107:        int on = 1;
                    108:        struct protoent *proto;
                    109: 
                    110:        argc--, av++;
                    111:        while (argc > 0 && *av[0] == '-') {
                    112:                while (*++av[0]) switch (*av[0]) {
                    113:                        case 'd':
                    114:                                options |= SO_DEBUG;
                    115:                                break;
                    116:                        case 'r':
                    117:                                options |= SO_DONTROUTE;
                    118:                                break;
                    119:                        case 'v':
                    120:                                verbose++;
                    121:                                break;
                    122:                }
                    123:                argc--, av++;
                    124:        }
                    125:        if( argc < 1)  {
                    126:                printf(usage);
                    127:                exit(1);
                    128:        }
                    129: 
                    130:        bzero( (char *)&whereto, sizeof(struct sockaddr) );
                    131:        to->sin_family = AF_INET;
                    132:        to->sin_addr.s_addr = inet_addr(av[0]);
                    133:        if (to->sin_addr.s_addr != -1) {
                    134:                strcpy(hnamebuf, av[0]);
                    135:                hostname = hnamebuf;
                    136:        } else {
                    137:                hp = gethostbyname(av[0]);
                    138:                if (hp) {
                    139:                        to->sin_family = hp->h_addrtype;
                    140:                        bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
                    141:                        hostname = hp->h_name;
                    142:                        toaddr = inet_ntoa(to->sin_addr.s_addr);
                    143:                } else {
                    144:                        printf("%s: unknown host %s\n", argv[0], av[0]);
                    145:                        exit(1);
                    146:                }
                    147:        }
                    148: 
                    149:        if( argc >= 2 )
                    150:                datalen = atoi( av[1] );
                    151:        else
                    152:                datalen = 64-8;
                    153:        if (datalen > MAXPACKET) {
                    154:                fprintf(stderr, "ping: packet size too large\n");
                    155:                exit(1);
                    156:        }
                    157:        if (datalen >= sizeof(struct timeval))
                    158:                timing = 1;
                    159:        if (argc > 2)
                    160:                npackets = atoi(av[2]);
                    161: 
                    162:        ident = getpid() & 0xFFFF;
                    163: 
                    164:        if ((proto = getprotobyname("icmp")) == NULL) {
                    165:                fprintf(stderr, "icmp: unknown protocol\n");
                    166:                exit(10);
                    167:        }
                    168:        if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
                    169:                perror("ping: socket");
                    170:                exit(5);
                    171:        }
                    172:        if (options & SO_DEBUG)
                    173:                setsockopt(s, SOL_SOCKET, SO_DEBUG, &on, sizeof(on));
                    174:        if (options & SO_DONTROUTE)
                    175:                setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &on, sizeof(on));
                    176: 
                    177:        printf("PING %s", hostname);
                    178:        if (toaddr)
                    179:                printf(" (%s)", toaddr);
                    180:        printf(": %d data bytes\n", datalen);
                    181:                                
                    182: 
                    183:        setlinebuf( stdout );
                    184: 
                    185:        signal( SIGINT, finish );
                    186:        signal(SIGALRM, catcher);
                    187: 
                    188:        catcher();      /* start things going */
                    189: 
                    190:        for (;;) {
                    191:                int len = sizeof (packet);
                    192:                int fromlen = sizeof (from);
                    193:                int cc;
                    194: 
                    195:                if ( (cc=recvfrom(s, packet, len, 0, &from, &fromlen)) < 0) {
                    196:                        if( errno == EINTR )
                    197:                                continue;
                    198:                        perror("ping: recvfrom");
                    199:                        continue;
                    200:                }
                    201:                pr_pack( packet, cc, &from );
                    202:                if (npackets && nreceived >= npackets)
                    203:                        finish();
                    204:        }
                    205:        /*NOTREACHED*/
                    206: }
                    207: 
                    208: /*
                    209:  *                     C A T C H E R
                    210:  * 
                    211:  * This routine causes another PING to be transmitted, and then
                    212:  * schedules another SIGALRM for 1 second from now.
                    213:  * 
                    214:  * Bug -
                    215:  *     Our sense of time will slowly skew (ie, packets will not be launched
                    216:  *     exactly at 1-second intervals).  This does not affect the quality
                    217:  *     of the delay and loss statistics.
                    218:  */
                    219: catcher()
                    220: {
                    221:        int waittime;
                    222: 
                    223:        pinger();
                    224:        if (npackets == 0 || ntransmitted < npackets)
                    225:                alarm(1);
                    226:        else {
                    227:                if (nreceived) {
                    228:                        waittime = 2 * tmax / 1000;
                    229:                        if (waittime == 0)
                    230:                                waittime = 1;
                    231:                } else
                    232:                        waittime = MAXWAIT;
                    233:                signal(SIGALRM, finish);
                    234:                alarm(waittime);
                    235:        }
                    236: }
                    237: 
                    238: /*
                    239:  *                     P I N G E R
                    240:  * 
                    241:  * Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
                    242:  * will be added on by the kernel.  The ID field is our UNIX process ID,
                    243:  * and the sequence number is an ascending integer.  The first 8 bytes
                    244:  * of the data portion are used to hold a UNIX "timeval" struct in VAX
                    245:  * byte-order, to compute the round-trip time.
                    246:  */
                    247: pinger()
                    248: {
                    249:        static u_char outpack[MAXPACKET];
                    250:        register struct icmp *icp = (struct icmp *) outpack;
                    251:        int i, cc;
                    252:        register struct timeval *tp = (struct timeval *) &outpack[8];
                    253:        register u_char *datap = &outpack[8+sizeof(struct timeval)];
                    254: 
                    255:        icp->icmp_type = ICMP_ECHO;
                    256:        icp->icmp_code = 0;
                    257:        icp->icmp_cksum = 0;
                    258:        icp->icmp_seq = ntransmitted++;
                    259:        icp->icmp_id = ident;           /* ID */
                    260: 
                    261:        cc = datalen+8;                 /* skips ICMP portion */
                    262: 
                    263:        if (timing)
                    264:                gettimeofday( tp, &tz );
                    265: 
                    266:        for( i=8; i<datalen; i++)       /* skip 8 for time */
                    267:                *datap++ = i;
                    268: 
                    269:        /* Compute ICMP checksum here */
                    270:        icp->icmp_cksum = in_cksum( icp, cc );
                    271: 
                    272:        /* cc = sendto(s, msg, len, flags, to, tolen) */
                    273:        i = sendto( s, outpack, cc, 0, &whereto, sizeof(struct sockaddr) );
                    274: 
                    275:        if( i < 0 || i != cc )  {
                    276:                if( i<0 )  perror("sendto");
                    277:                printf("ping: wrote %s %d chars, ret=%d\n",
                    278:                        hostname, cc, i );
                    279:                fflush(stdout);
                    280:        }
                    281: }
                    282: 
                    283: /*
                    284:  *                     P R _ T Y P E
                    285:  *
                    286:  * Convert an ICMP "type" field to a printable string.
                    287:  */
                    288: char *
                    289: pr_type( t )
                    290: register int t;
                    291: {
                    292:        static char *ttab[] = {
                    293:                "Echo Reply",
                    294:                "ICMP 1",
                    295:                "ICMP 2",
                    296:                "Dest Unreachable",
                    297:                "Source Quence",
                    298:                "Redirect",
                    299:                "ICMP 6",
                    300:                "ICMP 7",
                    301:                "Echo",
                    302:                "ICMP 9",
                    303:                "ICMP 10",
                    304:                "Time Exceeded",
                    305:                "Parameter Problem",
                    306:                "Timestamp",
                    307:                "Timestamp Reply",
                    308:                "Info Request",
                    309:                "Info Reply"
                    310:        };
                    311: 
                    312:        if( t < 0 || t > 16 )
                    313:                return("OUT-OF-RANGE");
                    314: 
                    315:        return(ttab[t]);
                    316: }
                    317: 
                    318: /*
                    319:  *                     P R _ P A C K
                    320:  *
                    321:  * Print out the packet, if it came from us.  This logic is necessary
                    322:  * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
                    323:  * which arrive ('tis only fair).  This permits multiple copies of this
                    324:  * program to be run without having intermingled output (or statistics!).
                    325:  */
                    326: pr_pack( buf, cc, from )
                    327: char *buf;
                    328: int cc;
                    329: struct sockaddr_in *from;
                    330: {
                    331:        struct ip *ip;
                    332:        register struct icmp *icp;
                    333:        register long *lp = (long *) packet;
                    334:        register int i;
                    335:        struct timeval tv;
                    336:        struct timeval *tp;
                    337:        int hlen, triptime;
                    338: 
                    339:        from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr );
                    340:        gettimeofday( &tv, &tz );
                    341: 
                    342:        ip = (struct ip *) buf;
                    343:        hlen = ip->ip_hl << 2;
                    344:        if (cc < hlen + ICMP_MINLEN) {
                    345:                if (verbose)
                    346:                        printf("packet too short (%d bytes) from %s\n", cc,
                    347:                                inet_ntoa(ntohl(from->sin_addr.s_addr)));
                    348:                return;
                    349:        }
                    350:        cc -= hlen;
                    351:        icp = (struct icmp *)(buf + hlen);
                    352:        if( icp->icmp_type != ICMP_ECHOREPLY )  {
                    353:                if (verbose) {
                    354:                        printf("%d bytes from %s: ", cc,
                    355:                                inet_ntoa(ntohl(from->sin_addr.s_addr)));
                    356:                        printf("icmp_type=%d (%s)\n",
                    357:                                icp->icmp_type, pr_type(icp->icmp_type) );
                    358:                        for( i=0; i<12; i++)
                    359:                            printf("x%2.2x: x%8.8x\n", i*sizeof(long), *lp++ );
                    360:                        printf("icmp_code=%d\n", icp->icmp_code );
                    361:                }
                    362:                return;
                    363:        }
                    364:        if( icp->icmp_id != ident )
                    365:                return;                 /* 'Twas not our ECHO */
                    366: 
                    367:        tp = (struct timeval *)&icp->icmp_data[0];
                    368:        printf("%d bytes from %s: ", cc,
                    369:                inet_ntoa(ntohl(from->sin_addr.s_addr)));
                    370:        printf("icmp_seq=%d. ", icp->icmp_seq );
                    371:        if (timing) {
                    372:                tvsub( &tv, tp );
                    373:                triptime = tv.tv_sec*1000+(tv.tv_usec/1000);
                    374:                printf("time=%d. ms\n", triptime );
                    375:                tsum += triptime;
                    376:                if( triptime < tmin )
                    377:                        tmin = triptime;
                    378:                if( triptime > tmax )
                    379:                        tmax = triptime;
                    380:        } else
                    381:                putchar('\n');
                    382:        nreceived++;
                    383: }
                    384: 
                    385: 
                    386: /*
                    387:  *                     I N _ C K S U M
                    388:  *
                    389:  * Checksum routine for Internet Protocol family headers (C Version)
                    390:  *
                    391:  */
                    392: in_cksum(addr, len)
                    393: u_short *addr;
                    394: int len;
                    395: {
                    396:        register int nleft = len;
                    397:        register u_short *w = addr;
                    398:        register u_short answer;
                    399:        register int sum = 0;
                    400:        u_short odd_byte = 0;
                    401: 
                    402:        /*
                    403:         *  Our algorithm is simple, using a 32 bit accumulator (sum),
                    404:         *  we add sequential 16 bit words to it, and at the end, fold
                    405:         *  back all the carry bits from the top 16 bits into the lower
                    406:         *  16 bits.
                    407:         */
                    408:        while( nleft > 1 )  {
                    409:                sum += *w++;
                    410:                nleft -= 2;
                    411:        }
                    412: 
                    413:        /* mop up an odd byte, if necessary */
                    414:        if( nleft == 1 ) {
                    415:                *(u_char *)(&odd_byte) = *(u_char *)w;
                    416:                sum += odd_byte;
                    417:        }
                    418: 
                    419:        /*
                    420:         * add back carry outs from top 16 bits to low 16 bits
                    421:         */
                    422:        sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
                    423:        sum += (sum >> 16);                     /* add carry */
                    424:        answer = ~sum;                          /* truncate to 16 bits */
                    425:        return (answer);
                    426: }
                    427: 
                    428: /*
                    429:  *                     T V S U B
                    430:  * 
                    431:  * Subtract 2 timeval structs:  out = out - in.
                    432:  * 
                    433:  * Out is assumed to be >= in.
                    434:  */
                    435: tvsub( out, in )
                    436: register struct timeval *out, *in;
                    437: {
                    438:        if( (out->tv_usec -= in->tv_usec) < 0 )   {
                    439:                out->tv_sec--;
                    440:                out->tv_usec += 1000000;
                    441:        }
                    442:        out->tv_sec -= in->tv_sec;
                    443: }
                    444: 
                    445: /*
                    446:  *                     F I N I S H
                    447:  *
                    448:  * Print out statistics, and give up.
                    449:  * Heavily buffered STDIO is used here, so that all the statistics
                    450:  * will be written with 1 sys-write call.  This is nice when more
                    451:  * than one copy of the program is running on a terminal;  it prevents
                    452:  * the statistics output from becomming intermingled.
                    453:  */
                    454: finish()
                    455: {
                    456:        printf("\n----%s PING Statistics----\n", hostname );
                    457:        printf("%d packets transmitted, ", ntransmitted );
                    458:        printf("%d packets received, ", nreceived );
                    459:        if (ntransmitted)
                    460:            printf("%d%% packet loss",
                    461:                (int) (((ntransmitted-nreceived)*100) / ntransmitted ) );
                    462:        printf("\n");
                    463:        if (nreceived && timing)
                    464:            printf("round-trip (ms)  min/avg/max = %d/%d/%d\n",
                    465:                tmin,
                    466:                tsum / nreceived,
                    467:                tmax );
                    468:        fflush(stdout);
                    469:        exit(0);
                    470: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.