#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/time.h> #include<arpa/inet.h> #include<netdb.h> #define ICMP_SIZE (sizeof(struct icmp)) #define ICMP_ECHO 0 #define ICMP_ECHOREPLY 0 #define BUF_SIZE 1024 #define NUM 5 //報文發送次數 #define UCHAR unsigned char #define USHORT unsigned short #define UINT unsigned int struct icmp { UCHAR type; //類型 UCHAR code; //代碼 USHORT checksum; //校驗和 USHORT id; //序列號 USHORT sequence; //序號 struct timeval timestamp; //時間戳 }; //ip首部數據結構 struct ip { //主機字節序判斷 //#if __BYTE_ORDER == __LITTLE_ENDINA #if __BYTE_ORDER == __LITTLE_ENDIAN UCHAR hlen:4; //首部長度 UCHAR version:4; //版本 #endif #if __BYTE_ORDER == __BIG_ENDIAN UCHAR version:4; UCHAR hlen:4; #endif UCHAR tos; //服務類型 USHORT len; //總長度 USHORT id; //標識符 USHORT offset; //標志和片偏移 UCHAR ttl; //生存時間 UCHAR protocol; //協議 USHORT checksum; //校驗和 struct in_addr ipsrc; //32位源ip地址 struct in_addr ipdst; //32位目的ip地址 }; char buf[BUF_SIZE] = {0}; USHORT checkSum(USHORT *, int); //計算校驗和 float timediff(struct timeval *, struct timeval *); //計算時間差 void pack(struct icmp *, int); //封裝一個ICMP報文 int unpack(char *, int , char *); //對接收的IP保溫進行解包 int main(int argc, char *argv[]) { struct hostent *host; struct icmp sendicmp; struct sockaddr_in from; struct sockaddr_in to; int fromlen = 0; int sockfd; int nsend = 0; int nreceived = 0; int i, n; in_addr_t inaddr; memset(&from, 0, sizeof(struct sockaddr_in)); memset(&to, 0, sizeof(struct sockaddr_in)); if (argc < 2) { printf("use : %s hostname/IP address \n", argv[0]); exit(1); } //生成原始套接字 if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1) { printf("socket() error\n"); exit(1); } //設置目的地址信息 to.sin_family = AF_INET; //判斷域名還是ip地址(好像沒實現) if (inaddr = inet_addr(argv[1]) == INADDR_NONE) { //域名 if ((host = gethostbyname(argv[1])) == NULL) { printf("gethostbyname() error \n"); exit(1); } to.sin_addr = *(struct in_addr *)host->h_addr_list[0]; } else { //ip地址 to.sin_addr.s_addr = inaddr; } //輸出域名ip地址信息 printf("ping %s (%s) : %d bytes of data.\n", argv[1], inet_ntoa(to.sin_addr), (int)ICMP_SIZE); //循環發送接收報文 for (i = 0; i < NUM; i++) { nsend++; memset(&sendicmp, 0, ICMP_SIZE); pack(&sendicmp, nsend); if (sendto(sockfd, &sendicmp, ICMP_SIZE, 0, (struct sockaddr*)&to, sizeof(to)) == -1) { printf("sendto() error\n"); continue; } if ((n = recvfrom(sockfd, buf, BUF_SIZE, 0, (struct sockaddr *) &from, &fromlen)) < 0) { printf("recvfrom() error\n"); continue; } nreceived++; if (unpack(buf, n, inet_ntoa(from.sin_addr)) == -1) { printf("unpack() error \n"); } sleep(1); } printf("--- %s ping statistics ---\n", argv[1]); printf("%d packets transmitted, %d received, %%%d packet loss\n", nsend, nreceived, (nsend - nreceived) / nsend * 100); return 0; } USHORT checkSum(USHORT *addr, int len) { UINT sum = 0; while(len > 1) { sum += *addr++; len -= 2; } //處理剩下的一個字節 if (len == 1) { sum += *(UCHAR *)addr; } //將32位的高16位和低16位相加 sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (USHORT) ~sum; } float timediff(struct timeval *begin, struct timeval *end) { int n; n = (end->tv_sec - begin->tv_sec) * 100000 + (end->tv_usec - begin->tv_usec); //轉為毫秒返回 return (float) (n / 1000); } void pack(struct icmp *icmp, int sequence) { icmp->type = ICMP_ECHO; icmp->code = 0; icmp->checksum = 0; icmp->id = getpid(); icmp->sequence = sequence; gettimeofday(&icmp->timestamp, 0); icmp->checksum = checkSum((USHORT *)icmp, ICMP_SIZE); } int unpack(char *buf, int len, char *addr) { int i, ipheadlen; struct ip * ip; struct icmp * icmp; float rtt; struct timeval end; ip = (struct ip *) buf; //計算ip首部長度,即ip首部的長度標識乘4 ipheadlen = ip->hlen << 2; //越過ip首部,指向icmp報文 icmp = (struct icmp *)(buf + ipheadlen); //icmp報文總長度 len -= ipheadlen; if (len < 8) { printf("ICMP packets\'s length is less than 8 \n"); return -1; } if (icmp->type != ICMP_ECHOREPLY || icmp->id != getpid()) { printf("ICMP packets are not send by us \n"); return -1; } gettimeofday(&end, 0); rtt = timediff(&icmp->timestamp, &end); printf("%d bytes form %s : icmp_seq=%u ttl=%d rtt=%fms \n", len, addr, icmp->sequence, ip->ttl, rtt); return 0; }