Skip to content

Instantly share code, notes, and snippets.

@tony0x59
Last active Oct 11, 2018
Embed
What would you like to do?
the original ping.c from Mike Muuss (https://gist.github.com/bugparty/ccba5744ba8f1cece5e0)
/*
* P I N G . 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
*
* Changed argument to inet_ntoa() to be struct in_addr instead of u_long
* DFM BRL 1992
*
* Status -
* Public Domain. Distribution Unlimited.
*
* Bugs -
* More statistics could always be gathered.
* This program has to run SUID to ROOT to access the ICMP socket.
*/
#include <stdio.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#define MAXWAIT 10 /* max time to wait for response, sec. */
#define MAXPACKET 4096 /* max packet size */
#define VERBOSE 1 /* verbose flag */
#define QUIET 2 /* quiet flag */
#define FLOOD 4 /* floodping flag */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
u_char packet[MAXPACKET];
int i, pingflags, options;
extern int errno;
int s; /* Socket file descriptor */
struct hostent *hp; /* Pointer to host info */
struct timezone tz; /* leftover */
struct sockaddr whereto;/* Who to ping */
int datalen; /* How much data */
char usage[] =
"Usage: ping [-dfqrv] host [packetsize [count [preload]]]\n";
char *hostname;
char hnamebuf[MAXHOSTNAMELEN];
int npackets;
int preload = 0; /* number of packets to "preload" */
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 */
int finish(), catcher();
char *inet_ntoa();
/*
* M A I N
*/
main(argc, argv)
char *argv[];
{
struct sockaddr_in from;
char **av = argv;
struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
int on = 1;
struct protoent *proto;
argc--, av++;
while (argc > 0 && *av[0] == '-') {
while (*++av[0]) switch (*av[0]) {
case 'd':
options |= SO_DEBUG;
break;
case 'r':
options |= SO_DONTROUTE;
break;
case 'v':
pingflags |= VERBOSE;
break;
case 'q':
pingflags |= QUIET;
break;
case 'f':
pingflags |= FLOOD;
break;
}
argc--, av++;
}
if(argc < 1 || argc > 4) {
printf(usage);
exit(1);
}
// 创建 ping 目标地址 addr
bzero((char *)&whereto, sizeof(struct sockaddr) );
to->sin_family = AF_INET;
to->sin_addr.s_addr = inet_addr(av[0]); // ip 格式转换
if(to->sin_addr.s_addr != (unsigned)-1) { // 转换成功
strcpy(hnamebuf, av[0]);
hostname = hnamebuf;
} else {
hp = gethostbyname(av[0]); // 解析域名
if (hp) {
to->sin_family = hp->h_addrtype;
bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
hostname = hp->h_name;
} else {
printf("%s: unknown host %s\n", argv[0], av[0]);
exit(1);
}
}
if( argc >= 2 )
datalen = atoi( av[1] ); // 用于指定数据长度
else
datalen = 64-8;
if (datalen > MAXPACKET) {
fprintf(stderr, "ping: packet size too large\n");
exit(1);
}
// 检查当前数据长度是否可以存储时间戳?
if (datalen >= sizeof(struct timeval)) /* can we time 'em? */
timing = 1;
if (argc >= 3)
npackets = atoi(av[2]);
if (argc == 4)
preload = atoi(av[3]);
ident = getpid() & 0xFFFF; // 用 pid 作为 icmp 标识符
if ((proto = getprotobyname("icmp")) == NULL) {
fprintf(stderr, "icmp: unknown protocol\n");
exit(10);
}
// 创建 RAW SOCKET,proto 为 ICMP
if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
perror("ping: socket");
exit(5);
}
if (options & SO_DEBUG) {
if(pingflags & VERBOSE)
printf("...debug on.\n");
setsockopt(s, SOL_SOCKET, SO_DEBUG, &on, sizeof(on));
}
if (options & SO_DONTROUTE) {
if(pingflags & VERBOSE)
printf("...no routing.\n");
setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &on, sizeof(on));
}
if(to->sin_family == AF_INET) {
printf("PING %s (%s): %d data bytes\n", hostname,
inet_ntoa(to->sin_addr), datalen); /* DFM */
} else {
printf("PING %s: %d data bytes\n", hostname, datalen );
}
setlinebuf( stdout );
signal( SIGINT, finish ); // 配置 ctrl+c 按键信号回调函数
signal(SIGALRM, catcher);
/* fire off them quickies */
for(i=0; i < preload; i++)
pinger();
// 启动首次 ping,并设定 1s 超时回调
if(!(pingflags & FLOOD))
catcher(); /* start things going */
for (;;) {
int len = sizeof (packet);
int fromlen = sizeof (from);
int cc;
struct timeval timeout;
int fdmask = 1 << s;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
if(pingflags & FLOOD) {
pinger();
if( select(32, &fdmask, 0, 0, &timeout) == 0)
continue;
}
// 接收 socket 数据
if ( (cc=recvfrom(s, packet, len, 0, &from, &fromlen)) < 0) {
if( errno == EINTR )
continue;
perror("ping: recvfrom");
continue;
}
// 输出读取到的数据
pr_pack( packet, cc, &from );
if (npackets && nreceived >= npackets)
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.
*/
catcher()
{
int waittime;
pinger();
if (npackets == 0 || ntransmitted < npackets)
alarm(1); // 1s 后发送 ALARM SIGNAL
else {
if (nreceived) {
waittime = 2 * tmax / 1000;
if (waittime == 0)
waittime = 1;
} else
waittime = MAXWAIT;
signal(SIGALRM, finish); // 配置 singal 的回调函数
alarm(waittime);
}
}
/*
* 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.
*/
pinger()
{
static u_char outpack[MAXPACKET];
register struct icmp *icp = (struct icmp *) outpack;
int i, cc;
register struct timeval *tp = (struct timeval *) &outpack[8]; // 后移 8 个字节 作为 time 首部
register u_char *datap = &outpack[8+sizeof(struct timeval)]; // 再后面是 data
icp->icmp_type = ICMP_ECHO; // 1 byte
icp->icmp_code = 0; // 1 byte
icp->icmp_cksum = 0; // 2 bytes
icp->icmp_seq = ntransmitted++; // 2 bytes
icp->icmp_id = ident; /* ID */ // 2 bytes
cc = datalen+8; /* skips ICMP portion */
if (timing)
gettimeofday( tp, &tz ); // 写入当前时间戳
// 将 data 数据区填充值
for( i=8; i<datalen; i++) /* skip 8 for time */
*datap++ = i;
/* Compute ICMP checksum here */
icp->icmp_cksum = in_cksum( icp, cc );
// 发送 ICMP 包
/* 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);
}
if(pingflags == FLOOD) {
putchar('.');
fflush(stdout);
}
}
/*
* P R _ T Y P E
*
* Convert an ICMP "type" field to a printable string.
*/
char *
pr_type( t )
register int t;
{
static char *ttab[] = {
"Echo Reply",
"ICMP 1",
"ICMP 2",
"Dest Unreachable",
"Source Quench",
"Redirect",
"ICMP 6",
"ICMP 7",
"Echo",
"ICMP 9",
"ICMP 10",
"Time Exceeded",
"Parameter Problem",
"Timestamp",
"Timestamp Reply",
"Info Request",
"Info Reply"
};
if( t < 0 || t > 16 )
return("OUT-OF-RANGE");
return(ttab[t]);
}
/*
* 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!).
*/
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;
struct timeval tv;
struct timeval *tp;
int hlen, triptime;
from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr );
gettimeofday( &tv, &tz ); // 获取当前时间戳
ip = (struct ip *) buf;
hlen = ip->ip_hl << 2; // 获取 ip 帧长度
if (cc < hlen + ICMP_MINLEN) {
if (pingflags & VERBOSE)
printf("packet too short (%d bytes) from %s\n", cc,
inet_ntoa(ntohl(from->sin_addr))); /* DFM */
return;
}
cc -= hlen;
icp = (struct icmp *)(buf + hlen); // 将指针挪至 icmp 首部
if( (!(pingflags & QUIET)) && icp->icmp_type != ICMP_ECHOREPLY ) { // 检验 type 且失败
printf("%d bytes from %s: icmp_type=%d (%s) icmp_code=%d\n",
cc, inet_ntoa(ntohl(from->sin_addr)),
icp->icmp_type, pr_type(icp->icmp_type), icp->icmp_code);/*DFM*/
if (pingflags & VERBOSE) {
for( i=0; i<12; i++)
printf("x%2.2x: x%8.8x\n", i*sizeof(long),
*lp++);
}
return;
}
if( icp->icmp_id != ident ) // 检查标识符
return; /* 'Twas not our ECHO */
if (timing) {
tp = (struct timeval *)&icp->icmp_data[0];
tvsub( &tv, tp ); // 时间戳做减法,tv - tp
triptime = tv.tv_sec*1000+(tv.tv_usec/1000);
tsum += triptime;
if( triptime < tmin ) // 记录最小值
tmin = triptime;
if( triptime > tmax ) // 记录最大值
tmax = triptime;
}
if(!(pingflags & QUIET)) {
if(pingflags != FLOOD) { // 输出 ping
printf("%d bytes from %s: icmp_seq=%d", cc,
inet_ntoa(from->sin_addr),
icp->icmp_seq ); /* DFM */
if (timing)
printf(" time=%d ms\n", triptime );
else
putchar('\n');
} else {
putchar('\b');
fflush(stdout);
}
}
nreceived++;
}
/*
* I N _ C K S U M
*
* Checksum routine for Internet Protocol family headers (C Version)
*
*/
in_cksum(addr, len)
u_short *addr;
int len;
{
register int nleft = len;
register u_short *w = addr;
register u_short answer;
register int sum = 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_short u = 0;
*(u_char *)(&u) = *(u_char *)w ;
sum += u;
}
/*
* 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);
}
/*
* T V S U B
*
* Subtract 2 timeval structs: out = out - in.
*
* Out is assumed to be >= in.
*/
tvsub( out, in )
register struct timeval *out, *in;
{
if( (out->tv_usec -= in->tv_usec) < 0 ) {
out->tv_sec--;
out->tv_usec += 1000000;
}
out->tv_sec -= in->tv_sec;
}
/*
* 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.
*/
finish()
{
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);
exit(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment