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

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

unix.superglobalmegacorp.com

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