码迷,mamicode.com
首页 > 其他好文 > 详细

002.原始套接字,构建ICMP包,实现Ping程序,简化版

时间:2015-11-19 22:11:50      阅读:177      评论:0      收藏:0      [点我收藏+]

标签:

大致流程:

    将ICMP头和时间数据设置好后,通过创建好的原始套接字socket发出去。目的主机计算效验和后会将数据原样返回,用当前时间和返回的数据结算时间差,计算出rtt。

其中:

1.我们以ping www.baidu.com为例,但我们并没有计算平均rtt

2.我们并没有手动创建IP头,而是交给了内核去帮我们处理

3.中间设计到的函数需要查相关资料

代码实现:

  1 /*
  2  ============================================================================
  3  Name        : myping.c
  4  Author      : huh
  5  Version     : 0.01
  6  Copyright   : Your copyright notice
  7  Description : Hello World in C, Ansi-style
  8  ============================================================================
  9  */
 10 
 11 #include <sys/types.h>
 12 #include <sys/select.h>
 13 #include <stdio.h>
 14 #include <time.h>
 15 #include <string.h>
 16 #include <netdb.h>
 17 #include <arpa/inet.h>
 18 #include <unistd.h>
 19 #include <signal.h>
 20 #include <sys/time.h>
 21 #include <sys/socket.h>
 22 #include <netinet/ip_icmp.h>
 23 
 24 #define PACKET_SIZE 1024*4
 25 
 26 int pid;
 27 int sockfd;
 28 int datalen = 56;
 29 int nsent = 1, nrecv = 1;
 30 char sendbuf[PACKET_SIZE];
 31 char recvbuf[PACKET_SIZE];
 32 struct sockaddr_in dest_addr; //socket目的地址
 33 struct sockaddr_in src_addr;
 34 struct timeval tvrecv;
 35 
 36 void send_packet();
 37 void un_packet(int);
 38 void tv_sub(struct timeval *out, struct timeval *in);
 39 unsigned short cal_chksum(unsigned short *addr, int len);
 40 
 41 int main()
 42 {
 43     pid = getpid();
 44     char str[20];
 45     unsigned int inaddr;
 46     struct hostent *host;
 47     int size = 1024 * 25;
 48     strcpy(str,"www.baidu.com");
 49     //IP地址(域名)到无符号整数的转换
 50     inaddr = inet_addr(str);
 51     if (inaddr == INADDR_NONE)
 52     {
 53         host = gethostbyname(str);
 54         if (host == NULL)
 55         {
 56             printf("参数格式不正确,请重新输入!\n");
 57             return 0;
 58         }
 59         memcpy((char*) &inaddr, host->h_addr, sizeof(dest_addr.sin_addr));
 60     }
 61     //设置套接字地址
 62     dest_addr.sin_family = AF_INET;
 63     memcpy((char *)&dest_addr.sin_addr, (char *)&inaddr,sizeof(inaddr));
 64     //创建套接字
 65     sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
 66     //改变socket缓冲区大小
 67     setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
 68 
 69     printf("PING %s (%s) %d(84) bytes of data.\n",str, inet_ntoa(dest_addr.sin_addr), datalen);
 70     //不停的发送和接受ICMP数据包
 71     while (1)
 72     {
 73         send_packet();
 74         int src_addr_len = sizeof(struct sockaddr_in);
 75         //接收数据包,一直阻塞到有数据包到达为止
 76         int len = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *) &src_addr, (socklen_t *) &src_addr_len);
 77         if(len < 0)
 78             printf("recvfrom error!\n");
 79         un_packet(len);
 80         sleep(1); //间隔一秒,后面会用信号实现每秒发送一个ICMP包。
 81     }
 82     return 0;
 83 }
 84 
 85 //解包
 86 void un_packet(int len)
 87 {
 88     int hlen1;
 89     double rtt;
 90     struct ip *ip;
 91     struct icmp *icmp;
 92     struct timeval *tvsend;
 93 
 94     ip = (struct ip *) recvbuf;
 95     hlen1 = ip->ip_hl << 2;
 96     if (ip->ip_p != IPPROTO_ICMP)
 97         return ;
 98 
 99     icmp = (struct icmp *) (recvbuf + hlen1);
100     len -= hlen1;
101 
102     if ((icmp->icmp_type == ICMP_ECHOREPLY))
103     {
104         if (icmp->icmp_id != pid)
105             return;
106         tvsend = (struct timeval *) icmp->icmp_data;  //发送时间
107         gettimeofday(&tvrecv, NULL);  //得到当前时间
108 
109         tv_sub(&tvrecv, tvsend); //计算接收和发送的时间差
110         rtt = tvrecv.tv_sec * 1000.0 + tvrecv.tv_usec / 1000.0; //以毫秒单位计算rtt
111         printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3fms\n", len, inet_ntoa(src_addr.sin_addr), icmp->icmp_seq, ip->ip_ttl, rtt);
112         nrecv++;
113     }
114 }
115 
116 //手动构建数据包,并通过原始套接字发送
117 void send_packet()
118 {
119     int len;
120     struct icmp *icmp;
121     icmp = (struct icmp *) (sendbuf);
122     icmp->icmp_type = ICMP_ECHO;  //拼接icmp
123     icmp->icmp_code = 0;
124     icmp->icmp_id = pid;   //2字节
125     icmp->icmp_seq = nsent++; //2字节
126     memset(icmp->icmp_data, 0xa5, datalen);
127     gettimeofday((struct timeval *) icmp->icmp_data, NULL);    //将发送时间作为数据传递过去
128 
129     len = datalen + 8;
130     icmp->icmp_cksum = 0;  //校验和需要先置0
131     icmp->icmp_cksum = cal_chksum((unsigned short *) icmp, len);  //计算效验和
132 
133     sendto(sockfd, sendbuf, len, 0, (struct sockaddr *) &dest_addr,
134             sizeof(dest_addr));  //将包发出去
135     //printf("package have sent!\n");
136 }
137 
138 //计算效验和
139 unsigned short cal_chksum(unsigned short *addr, int len)
140 {
141     int nleft = len;
142     int sum = 0;
143     unsigned short *w = addr;
144     unsigned short answer = 0;
145     //把ICMP报头二进制数据以2字节为单位累加起来
146     while (nleft > 1)
147     {
148         sum += *w++;
149         nleft -= 2;
150     }
151     if (nleft == 1)
152     {
153         *(unsigned char *) (&answer) = *(unsigned char *) w;
154         sum += answer;
155     }
156     sum = (sum >> 16) + (sum & 0xffff);
157     sum += (sum >> 16);
158     answer = ~sum;
159     return answer;
160 }
161 
162 //计算时间差
163 void tv_sub(struct timeval *out, struct timeval *in)
164 {
165     if ((out->tv_usec -= in->tv_usec) < 0)
166     {
167         --out->tv_sec;
168         out->tv_usec += 1000000;
169     }
170     out->tv_sec -= in->tv_sec;
171 }

 

002.原始套接字,构建ICMP包,实现Ping程序,简化版

标签:

原文地址:http://www.cnblogs.com/ruo-yu/p/4978968.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!