/******************************************************************************* ** This source code was posted to Info-MultiNet by Stuart Vance on ** 14-NOV-1996. ** ** In September 1997, it was modified by Hunter Goatley to work as a ** callable PING routine that works with the following interfaces: ** ** SOCKET - Normal DEC C socket library (BG interface) ** BG - $QIO calls to the BG interface (TCPware, MultiNet, UCX) ** INET - $QIO calls to the INET interface (TCPware, MultiNet) ** IP - $QIO calls to the IP interface (TCPware) ** ** For all three $QIO versions, a $QIO to the BG interface is used to do ** the gethostbyname() call. ** ** To compile using DEC C, use: ** ** $ cc/standard=vaxc/define=(MAIN,DO_OUTPUT,xxxx) ping ** $ link ping ** ** where "xxxx" is "SOCKET", "BG", "INET", or "IP". To make a callable ** PING module, simply omit the "MAIN" and "DO_OUTPUT" definitions. ** ** To call this routine from a program, use: ** ** int ping (char *hostname, int number_of_packets); ** *******************************************************************************/ /* * PING.C * * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, * measure round-trip-delays and packet loss across network paths. * * Author - * Mike Muuss * U. S. Army Ballistic Research Laboratory * December, 1983 * Modified at UC Berkeley * Modified even more at SRI for MultiNet * Modified further for sample RAW socket program at TGV Software, Inc. * * * Status - * Copyright (C) 1988 SRI International * Copyright (C) 1990, 1992, 1995 TGV Software, Inc. * * Bugs - * More statistics could always be gathered. * This program has to run SUID to ROOT to access the ICMP socket. */ #ifdef __DECC #define WANT_DECC_STDIO 1 #endif /* __DECC */ #ifdef SOCKET #ifdef QIO #undef QIO #endif #else #define QIO 1 #ifndef BG #define BG 1 #endif #ifdef INET #undef BG #endif #ifdef IP #undef BG #endif #endif #include "ucx$inetdef.h" #include #include #include #include #include #include #include #include #include #include #include "in_systm.h" #include "ip.h" #include "ip_icmp.h" #include #include #include #include #define bzero(x,y) memset(x,0,y) #define bcopy(x,y,z) memcpy(y,x,z) #ifdef INET /*******************************/ /* From MultiNet's INETIODEF.H */ #ifndef IO$S_FCODE #define IO$S_FCODE 6 #endif /*IO$S_FCODE*/ #ifndef IO$_IOCTL #define IO$_SEND (IO$_WRITEVBLK) #define IO$_RECEIVE (IO$_READVBLK) #define IO$_SOCKET (IO$_ACCESS | (0 << IO$S_FCODE)) #define IO$_BIND (IO$_ACCESS | (1 << IO$S_FCODE)) #define IO$_LISTEN (IO$_ACCESS | (2 << IO$S_FCODE)) #define IO$_ACCEPT (IO$_ACCESS | (3 << IO$S_FCODE)) #define IO$_CONNECT (IO$_ACCESS | (4 << IO$S_FCODE)) #define IO$_SETSOCKOPT (IO$_ACCESS | (5 << IO$S_FCODE)) #define IO$_GETSOCKOPT (IO$_ACCESS | (6 << IO$S_FCODE)) #define IO$_IOCTL (IO$_ACCESS | (8 << IO$S_FCODE)) #define IO$_ACCEPT_WAIT (IO$_ACCESS | (10 << IO$S_FCODE)) #define IO$_NETWORK_PTY (IO$_ACCESS | (11 << IO$S_FCODE)) #define IO$_SHUTDOWN (IO$_ACCESS | (12 << IO$S_FCODE)) #define IO$_GETSOCKNAME (IO$_ACCESS | (13 << IO$S_FCODE)) #define IO$_GETPEERNAME (IO$_ACCESS | (15 << IO$S_FCODE)) #define IO$_SELECT (IO$_ACCESS | (17 << IO$S_FCODE)) #endif /*IO$_IOCTL*/ /*******************************/ #endif /* INET */ #define MAXWAIT 5 /* 10 /* max time to wait for response, sec. */ #define MAXPACKET (65536-60-8) /* max packet size */ #define VERBOSE 1 /* verbose flag */ #define QUIET 2 /* quiet flag */ #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif #ifdef SOCKET #define vmserrno vaxc$errno #endif u_char *packet; int packlen; int i, pingflags, options; int s; /* Socket file descriptor */ struct hostent *hp; /* Pointer to host info */ struct sockaddr whereto; /* Who to ping */ int datalen; /* How much data */ char usage[] = "Usage: ping [-drvqn] host [data size] [npackets]\n"; char *hostname; char hnamebuf[MAXHOSTNAMELEN]; int npackets; int ntransmitted = 0; /* sequence # for outbound packets = #sent */ int ident; int nreceived = 0; /* # of packets we got back */ int timing = 0; int tmin = 999999999; int tmax = 0; int tsum = 0; /* sum of all times, for doing average */ static int finish(), catcher(), finish_timeout(); #ifdef DO_OUTPUT char *inet_ntoa(); char *pr_addr(); #endif int numeric = 0; /* -n flag: numeric addresses only */ static int big=32768; int read_efn; int write_efn; int chan = 0; unsigned short iosb[4]; struct itlst { int length; struct sockaddr *host; } remhost; struct it3lst { unsigned short length; unsigned short name; struct sockaddr *host; int *retlen; } srchost; /* * M A I N */ ping(char *the_host_to_ping, int no_of_packets) { struct sockaddr_in from; struct sockaddr_in *to = (struct sockaddr_in *) &whereto; int on = 1; struct protoent *proto; int status; #ifdef QIO $DESCRIPTOR (ucx_device, "_BG:"); $DESCRIPTOR (inet_device, "_INET0:"); $DESCRIPTOR (ip_device, "_IPA0:"); struct dsc$descriptor hostname_d = {0, DSC$K_CLASS_S, DSC$K_DTYPE_T, 0}; struct dsc$descriptor acpcmd_d = {0, DSC$K_CLASS_S, DSC$K_DTYPE_T, 0}; struct dsc$descriptor hostaddr_d = {0, DSC$K_CLASS_S, DSC$K_DTYPE_T, 0}; char hostname_buff[255]; /* Our copy of the host name */ int hostaddr_buff[8]; /* Buffer for ACP output */ int acpcmd = INETACP$C_TRANS * 256 + INETACP_FUNC$C_GETHOSTBYNAME; int retlen; struct SocketParamStruct { unsigned short protocol; unsigned char type; unsigned char family; } SocketParam; struct ILSockOptStruct { unsigned short length; unsigned short code; char *optStructAddr; } ilSockOpt; struct SetSockOptStruct { unsigned short length; unsigned short code; char *optBuffAddr; } setSockOpt[2]; #endif ntransmitted = 0; /* sequence # for outbound packets = #sent */ nreceived = 0; /* # of packets we got back */ timing = 0; tmin = 999999999; tmax = 0; tsum = 0; /* sum of all times, for doing average */ #ifdef DO_OUTPUT pingflags = VERBOSE; #endif bzero( (char *)&whereto, sizeof(struct sockaddr) ); #ifdef QIO /* ** Use the BG interface to do gethostbyname(). */ acpcmd_d.dsc$w_length = sizeof(acpcmd); acpcmd_d.dsc$a_pointer = &acpcmd; hostaddr_d.dsc$w_length = sizeof(hostaddr_buff); hostaddr_d.dsc$a_pointer = &hostaddr_buff; /* * Stupid UCX: it expects the hostname to be writable, too, though there's * no reason for it. Because it may be in a read-only PSECT in our caller, * copy the damn string to our own read/write buffer to make UCX happy. * MultiNet and TCPware didn't need this!!! */ strcpy(hostname_buff, the_host_to_ping); hostname_d.dsc$w_length = strlen(hostname_buff); hostname_d.dsc$a_pointer = &hostname_buff; setSockOpt[0].length = sizeof(int); setSockOpt[0].code = UCX$C_RCVBUF; setSockOpt[0].optBuffAddr = &big; setSockOpt[1].length = sizeof(int); setSockOpt[1].code = UCX$C_SNDBUF; setSockOpt[1].optBuffAddr = &big; ilSockOpt.length = sizeof(setSockOpt); ilSockOpt.optStructAddr = (char *) &setSockOpt; ilSockOpt.code = UCX$C_SOCKOPT; retlen = 0; status = lib$get_ef (&read_efn); if (!(status & 1)) return (status); status = lib$get_ef (&write_efn); if (!(status & 1)) return (status); status = sys$assign(&ucx_device, &chan, 0, 0, 0); if (!(status & 1)) return (status); status = sys$qiow(read_efn, chan, IO$_ACPCONTROL, &iosb, 0, 0, &acpcmd_d, /* P1 command */ &hostname_d, /* P2 host name */ &retlen, /* P3 return length addr */ &hostaddr_d, /* P4 output */ 0, 0); if (status & 1) status = iosb[0]; if (!(status & 1)) return(status); to->sin_family = UCX$C_AF_INET; bcopy(hostaddr_buff, (caddr_t)&to->sin_addr, retlen); strcpy (hnamebuf, the_host_to_ping); hostname = hnamebuf; #ifndef BG /* ** If we're doing IP or INET interfaces, then get rid of ** the BG device and assign a channel to IP or INET. */ sys$dassgn (chan); #ifdef INET status = sys$assign(&inet_device, &chan, 0, 0, 0); #endif /* INET */ #ifdef IP status = sys$assign(&ip_device, &chan, 0, 0, 0); #endif /* IP */ if (!(status & 1)) return (status); #endif /* !BG */ #else to->sin_family = AF_INET; hp = gethostbyname(the_host_to_ping); if (hp) { to->sin_family = hp->h_addrtype; bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length); strncpy( hnamebuf, hp->h_name, sizeof(hnamebuf)-1 ); hostname = hnamebuf; } else { to->sin_addr.s_addr = inet_addr(the_host_to_ping); if (to->sin_addr.s_addr != (unsigned)-1) { strcpy(hnamebuf, the_host_to_ping); hostname = hnamebuf; } else { printf("ping: unknown host %s\n", the_host_to_ping); return(SS$_NOSUCHNODE | STS$M_INHIB_MSG); } } #endif /* QIO */ datalen = 64-8; /* Just send default bytes */ if (datalen >= (2*sizeof(unsigned long))) timing = 1; packlen = datalen + 60 + 76; /* MAXIP + MAXICMP */ if (!((status = lib$get_vm (&packlen, &packet)) & 1)) return (status); npackets = no_of_packets; ident = getpid() & 0xFFFF; #ifdef QIO #ifdef BG /* ** remhost is an item_list_2 descriptor that describes ** the sockaddr for the remote host. */ remhost.length = sizeof(struct sockaddr); remhost.host = to; /* ** srchost is an item_list_3 descriptor that is used ** to receive the remote host info on the IO$_READVBLK ** $QIO call. */ srchost.length = sizeof(struct sockaddr); srchost.host = (struct sockaddr *) &from; srchost.name = INET$C_SOCK_NAME; srchost.retlen = &retlen; SocketParam.family = UCX$C_AF_INET; SocketParam.type = INET_PROTYP$C_RAW; SocketParam.protocol = IPPROTO$C_ICMP; status = sys$qiow(read_efn, chan, IO$_SETMODE, &iosb, 0, 0, &SocketParam, /* P1 socket definition */ 0, 0, 0, &ilSockOpt, /* P5 socket options */ 0); if (status & 1) status = iosb[0]; if (!(status & 1)) return(status); #else /* BG */ #ifdef INET status = sys$qiow(read_efn, chan, IO$_SOCKET, &iosb, 0, 0, AF_INET, SOCK_RAW, IPPROTO$C_ICMP, 0, 0, 0); #endif /* INET */ #ifdef IP status = sys$qiow(read_efn, chan, IO$_SETMODE | IO$M_CTRL | IO$M_STARTUP, &iosb, 0, 0, 0, 0, IPPROTO$C_ICMP, 0, 0, 0); #endif /* IP */ if (status & 1) status = iosb[0]; if (!(status & 1)) return(status); #endif /* BG */ #else /* QIO */ #ifdef VMS if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) <= 0) { #else if ((proto = getprotobyname("icmp")) == NULL) { fprintf(stderr, "icmp: unknown protocol\n"); return(SS$_PROTOCOL | STS$M_INHIB_MSG); } if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { #endif perror("ping: socket"); return(SS$_PROTOCOL | STS$M_INHIB_MSG); } if (options & SO_DEBUG) setsockopt(s, SOL_SOCKET, SO_DEBUG, &on, sizeof(on)); if (options & SO_DONTROUTE) setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &on, sizeof(on)); setsockopt(s, SOL_SOCKET, SO_SNDBUF, &big, sizeof(big)); setsockopt(s, SOL_SOCKET, SO_RCVBUF, &big, sizeof(big)); #endif /* QIO */ #ifdef DO_OUTPUT if(to->sin_family == AF_INET) { printf("PING %s (%s): %d data bytes\n", hostname, inet_ntoa(to->sin_addr.s_addr), datalen); } else { printf("PING %s: %d data bytes\n", hostname, datalen ); } #endif #ifdef SOCKET signal( SIGINT, finish_timeout ); signal( SIGALRM, finish_timeout ); #endif if (npackets == 0 || ntransmitted < npackets) catcher(); /* start things going */ for (;;) { int fromlen = sizeof (from); int cc; #ifdef QIO unsigned short read_iosb[4]; #ifdef BG status = sys$qiow(read_efn, chan, IO$_READVBLK, &read_iosb, 0, 0, packet, /* P1 receive buffer */ packlen, /* P2 # of chars to receive */ &srchost, /* P3 remote host info */ 0, 0, 0); #else #ifdef INET status = sys$qiow(read_efn, chan, IO$_READVBLK, &read_iosb, 0, 0, packet, /* P1 receive buffer */ packlen, /* P2 # of chars to receive */ 0, /* P3 flags */ &from, /* P4 from structure */ fromlen, /* P5 length of from */ 0); #endif /* INET */ #ifdef IP status = sys$qiow(read_efn, chan, IO$_READVBLK, &read_iosb, 0, 0, packet, /* P1 receive buffer */ packlen, /* P2 # of chars to receive */ 0, 0, 0, 0); #endif /* IP */ #endif /* BG */ if (status & 1) status = read_iosb[0]; if (status & 1) cc = read_iosb[1]; if (!(status & 1)) { if (status == SS$_CANCEL) return(finish()); lib$signal(status); continue; } #ifdef IP bzero (&from, sizeof(from)); from.sin_family = UCX$C_AF_INET; bcopy (&packet[12], &from.sin_addr, sizeof(long)); #endif /* IP */ #else if ((cc=recvfrom(s, packet, packlen, 0, &from, &fromlen)) <= 0) { if( errno == EINTR ) continue; if( errno == ENETDOWN ) return(finish()); if( vmserrno == SS$_DEVOFFLINE ) return(finish()); if( vmserrno == SS$_CANCEL | STS$K_SEVERE) return(finish()); perror("PING: recvfrom"); continue; } #endif /* QIO */ pr_pack( packet, cc, &from ); if (npackets && nreceived >= npackets) return(finish()); } /*NOTREACHED*/ } /* * C A T C H E R * * This routine causes another PING to be transmitted, and then * schedules another SIGALRM for 1 second from now. * * Bug - * Our sense of time will slowly skew (ie, packets will not be launched * exactly at 1-second intervals). This does not affect the quality * of the delay and loss statistics. */ static catcher() { int waittime; static int time[2]={-10000000, -1}; time[0] = -10000000; time[1] = -1; pinger(); if (npackets == 0 || ntransmitted < npackets) { #ifdef QIO sys$setimr (0, time, catcher, 0, 0); #else signal( SIGALRM, catcher); alarm(1); #endif /* QIO */ } else { if (nreceived) { waittime = 2 * tmax / 1000; if (waittime == 0) waittime = 1; } else waittime = MAXWAIT; time[0] = -10000000 * waittime; #ifdef QIO sys$setimr (0, time, finish_timeout, 0, 0); #else signal( SIGALRM, finish_timeout); alarm(waittime); #endif /* QIO */ } } /* * P I N G E R * * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet * will be added on by the kernel. The ID field is our UNIX process ID, * and the sequence number is an ascending integer. The first 8 bytes * of the data portion are used to hold a UNIX "timeval" struct in VAX * byte-order, to compute the round-trip time. */ static pinger() { static u_char outpack[MAXPACKET]; register struct icmp *icp = (struct icmp *) outpack; int i, cc; register unsigned long *tp = (unsigned long *) &outpack[8]; register u_char *datap = &outpack[8+2*sizeof(unsigned long)]; static unsigned short write_iosb[4]; /* (void) SYS$SETAST(0); /* Disable ASTs */ icp->icmp_type = ICMP_ECHO; icp->icmp_code = 0; icp->icmp_cksum = 0; icp->icmp_seq = ntransmitted++; icp->icmp_id = ident; /* ID */ cc = datalen+8; /* skips ICMP portion */ if (timing) sys$gettim(tp); for( i=8; iicmp_cksum = in_cksum( icp, cc ); #ifdef QIO #ifdef BG i = sys$qiow(write_efn, chan, IO$_WRITEVBLK, &write_iosb, 0, 0, &outpack, /* Buffer to write */ cc, /* Number of chars to write */ &remhost, /* P3 remote host info */ 0, 0, 0); #else #ifdef INET i = sys$qiow(write_efn, chan, IO$_WRITEVBLK, &write_iosb, 0, 0, &outpack, /* Buffer to write */ cc, /* Number of chars to write */ 0, &whereto, sizeof(struct sockaddr), 0); #endif /* INET */ #ifdef IP { struct sockaddr_in *to = (struct sockaddr_in *) &whereto; i = sys$qiow(write_efn, chan, IO$_WRITEVBLK, &write_iosb, 0, 0, &outpack, /* Buffer to write */ cc, /* Number of chars to write */ 0, /* P3 optional transmit chars */ to->sin_addr, /* P4 dest address */ 0, /* P5 source address */ 0); } #endif /* IP */ #endif /* BG */ if (i & 1) i = write_iosb[0]; if (!(i & 1)) { lib$signal(i); return(i); } #else /* cc = sendto(s, msg, len, flags, to, tolen) */ i = sendto( s, outpack, cc, 0, &whereto, sizeof(struct sockaddr) ); if( i < 0 || i != cc ) { if( i<0 ) perror("sendto"); printf("PING: wrote %s %d chars, ret=%d\n", hostname, cc, i ); fflush(stdout); } #endif /* (void) SYS$SETAST(1); /* Enable ASTs */ } /* * P R _ P A C K * * Print out the packet, if it came from us. This logic is necessary * because ALL readers of the ICMP socket get a copy of ALL ICMP packets * which arrive ('tis only fair). This permits multiple copies of this * program to be run without having intermingled output (or statistics!). */ static pr_pack( buf, cc, from ) char *buf; int cc; struct sockaddr_in *from; { struct ip *ip; register struct icmp *icp; register long *lp = (long *) packet; register int i; unsigned long tv[2]; unsigned long *tp; int hlen, triptime; from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr ); sys$gettim(&tv); /* Check the IP header */ ip = (struct ip *) buf; hlen = ip->ip_hl << 2; if( cc < hlen + ICMP_MINLEN ) { #if DO_OUTPUT if( pingflags & VERBOSE ) printf("packet too short (%d bytes) from %s\n", cc, inet_ntoa(ntohl(from->sin_addr.s_addr))); #endif return; } /* Now the ICMP part */ cc -= hlen; icp = (struct icmp *)(buf + hlen); if( icp->icmp_type == ICMP_ECHOREPLY ) { if( icp->icmp_id != ident ) return; /* 'Twas not our ECHO */ nreceived++; if (timing) { tp = (unsigned long *)&icp->icmp_data[0]; { static int divisor=10000; int remainder; lib$subx(tv, tp, tv); lib$ediv(&divisor, tv, &triptime, &remainder); } tsum += triptime; if( triptime < tmin ) tmin = triptime; if( triptime > tmax ) tmax = triptime; } if( pingflags & QUIET ) return; #ifdef DO_OUTPUT printf("%d bytes from %s: icmp_seq=%d", cc, inet_ntoa(ntohl(from->sin_addr.s_addr)), icp->icmp_seq ); if (timing) printf(" time=%d ms\n", triptime ); else putchar('\n'); #endif } else { /* We've got something other than an ECHOREPLY */ if( !(pingflags & VERBOSE) ) return; #ifdef DO_OUTPUT printf("%d bytes from %s: ", cc, pr_addr(ntohl(from->sin_addr.s_addr)) ); pr_icmph( icp ); #endif } } /* * I N _ C K S U M * * Checksum routine for Internet Protocol family headers (C Version) * */ static in_cksum(addr, len) u_short *addr; int len; { register int nleft = len; register u_short *w = addr; register int sum = 0; u_short answer = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), * we add sequential 16 bit words to it, and at the end, fold * back all the carry bits from the top 16 bits into the lower * 16 bits. */ while( nleft > 1 ) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if( nleft == 1 ) { *(u_char *)(&answer) = *(u_char *)w ; sum += answer; } /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return (answer); } /* * F I N I S H * * Print out statistics, and give up. * Heavily buffered STDIO is used here, so that all the statistics * will be written with 1 sys-write call. This is nice when more * than one copy of the program is running on a terminal; it prevents * the statistics output from becomming intermingled. */ static finish_timeout() { #ifndef QIO int chan; chan = vaxc$get_sdc(s); #endif return(sys$cancel(chan)); } static finish() { #ifdef DO_OUTPUT putchar('\n'); fflush(stdout); printf("\n----%s PING Statistics----\n", hostname ); printf("%d packets transmitted, ", ntransmitted ); printf("%d packets received, ", nreceived ); if (ntransmitted) if( nreceived > ntransmitted) printf("-- somebody's printing up packets!"); else printf("%d%% packet loss", (int) (((ntransmitted-nreceived)*100) / ntransmitted)); printf("\n"); if (nreceived && timing) printf("round-trip (ms) min/avg/max = %d/%d/%d\n", tmin, tsum / nreceived, tmax ); fflush(stdout); #endif /* DO_OUTPUT */ #ifdef QIO lib$free_ef (&read_efn); lib$free_ef (&write_efn); sys$cantim (0, 0); /* Cancel any timers */ sys$dassgn (chan); /* Close the socket */ #else alarm(0); /* Cancel any alarms */ close(s); /* Close the socket */ #endif /* QIO */ lib$free_vm (&packlen, &packet); if (ntransmitted) { if (ntransmitted == nreceived) { return(SS$_NORMAL); } else if (nreceived) { return(SS$_DATALOST); } else { return(SS$_UNREACHABLE); } } else { return(SS$_NORMAL); } } #ifdef DO_OUTPUT static char *ttab[] = { "Echo Reply", /* ip + seq + udata */ "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ "Source Quench", /* IP */ "Redirect", /* redirect type, gateway, + IP */ "Echo", "Time Exceeded", /* transit, frag reassem + IP */ "Parameter Problem", /* pointer + IP */ "Timestamp", /* id + seq + three timestamps */ "Timestamp Reply", /* " */ "Info Request", /* id + sq */ "Info Reply" /* " */ }; /* * Print a descriptive string about an ICMP header. */ pr_icmph( icp ) struct icmp *icp; { switch( icp->icmp_type ) { case ICMP_ECHOREPLY: printf("Echo Reply\n"); /* XXX ID + Seq + Data */ break; case ICMP_UNREACH: switch( icp->icmp_code ) { case ICMP_UNREACH_NET: printf("Destination Net Unreachable\n"); break; case ICMP_UNREACH_HOST: printf("Destination Host Unreachable\n"); break; case ICMP_UNREACH_PROTOCOL: printf("Destination Protocol Unreachable\n"); break; case ICMP_UNREACH_PORT: printf("Destination Port Unreachable\n"); break; case ICMP_UNREACH_NEEDFRAG: printf("frag needed and DF set\n"); break; case ICMP_UNREACH_SRCFAIL: printf("Source Route Failed\n"); break; default: printf("Dest Unreachable, Bad Code: %d\n", icp->icmp_code ); break; } /* Print returned IP header information */ pr_retip( icp->icmp_data ); break; case ICMP_SOURCEQUENCH: printf("Source Quench\n"); pr_retip( icp->icmp_data ); break; case ICMP_REDIRECT: switch( icp->icmp_code ) { case ICMP_REDIRECT_NET: printf("Redirect Network"); break; case ICMP_REDIRECT_HOST: printf("Redirect Host"); break; case ICMP_REDIRECT_TOSNET: printf("Redirect Type of Service and Network"); break; case ICMP_REDIRECT_TOSHOST: printf("Redirect Type of Service and Host"); break; default: printf("Redirect, Bad Code: %d", icp->icmp_code ); break; } printf(" (New addr: 0x%08x)\n", (long *) icp->icmp_hun.ih_pptr ); pr_retip( icp->icmp_data ); break; case ICMP_ECHO: printf("Echo Request\n"); /* XXX ID + Seq + Data */ break; case ICMP_TIMXCEED: switch( icp->icmp_code ) { case ICMP_TIMXCEED_INTRANS: printf("Time to live exceeded\n"); break; case ICMP_TIMXCEED_REASS: printf("Frag reassembly time exceeded\n"); break; default: printf("Time exceeded, Bad Code: %d\n", icp->icmp_code ); break; } pr_retip( icp->icmp_data ); break; case ICMP_PARAMPROB: printf("Parameter problem: pointer = 0x%02x\n", icp->icmp_hun.ih_pptr ); pr_retip( icp->icmp_data ); break; case ICMP_TSTAMP: printf("Timestamp\n"); /* XXX ID + Seq + 3 timestamps */ break; case ICMP_TSTAMPREPLY: printf("Timestamp Reply\n"); /* XXX ID + Seq + 3 timestamps */ break; case ICMP_IREQ: printf("Information Request\n"); /* XXX ID + Seq */ break; case ICMP_IREQREPLY: printf("Information Reply\n"); /* XXX ID + Seq */ break; #ifdef ICMP_MASKREQ case ICMP_MASKREQ: printf("Address Mask Request\n"); break; #endif #ifdef ICMP_MASKREPLY case ICMP_MASKREPLY: printf("Address Mask Reply\n"); break; #endif default: printf("Bad ICMP type: %d\n", icp->icmp_type); } } /* * Print an IP header with options. */ pr_iph( ip ) struct ip *ip; { int hlen; unsigned char *cp; hlen = ip->ip_hl << 2; cp = (unsigned char *)ip + 20; /* point to options */ printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); printf(" %1x %1x %02x %04x %04x", ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id ); printf(" %1x %04x", ((ip->ip_off)&0xe000)>>13, (ip->ip_off)&0x1fff ); printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum ); printf(" %-13s %-13s ", inet_ntoa(ip->ip_src.s_addr), inet_ntoa(ip->ip_dst.s_addr)); /* dump and option bytes */ while( hlen-- > 20 ) { printf( "%02x", *cp++ ); } printf("\n"); } /* * Return an ascii host address * as a dotted quad and optionally with a hostname */ char * pr_addr( l ) unsigned long l; { struct hostent *hp; static char buf[80]; if( numeric || (hp = gethostbyaddr(&l, 4, AF_INET)) == NULL ) sprintf( buf, "%s", inet_ntoa(l) ); else sprintf( buf, "%s (%s)", hp->h_name, inet_ntoa(l) ); return( buf ); } /* * Dump some info on a returned (via ICMP) IP packet. */ pr_retip( ip ) struct ip *ip; { int hlen; unsigned char *cp; pr_iph( ip ); hlen = ip->ip_hl << 2; cp = (unsigned char *)ip + hlen; if( ip->ip_p == 6 ) { printf( "TCP: from port %d, to port %d (decimal)\n", (*cp*256+*(cp+1)), (*(cp+2)*256+*(cp+3)) ); } else if( ip->ip_p == 17 ) { printf( "UDP: from port %d, to port %d (decimal)\n", (*cp*256+*(cp+1)), (*(cp+2)*256+*(cp+3)) ); } } #endif /* DO_OUTPUT */ #ifdef MAIN int main(int argc, char **argv) { int status; return (ping (argv[1],5)); } #endif