002.ICMP--拼接ICMP包,实现简单Ping程序(原始套接字)
2015-11-19 20:32
651 查看
一.大致流程:
将ICMP头和时间数据设置好后,通过创建好的原始套接字socket发出去。目的主机计算效验和后会将数据原样返回,用当前时间和返回的数据结算时间差,计算出rtt。
二.数据结构:
ICMP时间戳请求与应答报文格式:
myping
将ICMP头和时间数据设置好后,通过创建好的原始套接字socket发出去。目的主机计算效验和后会将数据原样返回,用当前时间和返回的数据结算时间差,计算出rtt。
二.数据结构:
ICMP时间戳请求与应答报文格式:
/* ============================================================================ Name : myping.c Author : huh Version : 0.01 Copyright : Your copyright notice Description : Hello World in C, Ansi-style ============================================================================ */ #include <sys/types.h> #include <sys/select.h> #include <stdio.h> #include <time.h> #include <string.h> #include <netdb.h> #include <arpa/inet.h> #include <unistd.h> #include <signal.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/ip_icmp.h> #define PACKET_SIZE 1024*4 int pid; int sockfd; int datalen = 56; int nsent = 1, nrecv = 1; char sendbuf[PACKET_SIZE]; char recvbuf[PACKET_SIZE]; struct sockaddr_in dest_addr; //socket目的地址 struct sockaddr_in src_addr; struct timeval tvrecv; void send_packet(); void un_packet(int); void tv_sub(struct timeval *out, struct timeval *in); unsigned short in_chksum(unsigned short *addr, int len); int main() { pid = getpid(); char str[20]; unsigned int inaddr; struct hostent *host; int size = 1024 * 25; strcpy(str,"www.baidu.com"); //IP地址(域名)到无符号整数的转换 inaddr = inet_addr(str); if (inaddr == INADDR_NONE) { host = gethostbyname(str); if (host == NULL) { printf("参数格式不正确,请重新输入!\n"); return 0; } memcpy((char*) &inaddr, host->h_addr, sizeof(dest_addr.sin_addr)); } //设置套接字地址 dest_addr.sin_family = AF_INET; memcpy((char *)&dest_addr.sin_addr, (char *)&inaddr,sizeof(inaddr)); //创建套接字 sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); //改变socket缓冲区大小 setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); printf("PING %s (%s) %d(84) bytes of data.\n",str, inet_ntoa(dest_addr.sin_addr), datalen); //不停的发送和接受ICMP数据包 while (1) { send_packet(); int src_addr_len = sizeof(struct sockaddr_in); //接收数据包,一直阻塞到有数据包到达为止 int len = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *) &src_addr, (socklen_t *) &src_addr_len); if(len < 0) printf("recvfrom error!\n"); un_packet(len); sleep(1); //间隔一秒,后面会用信号实现每秒发送一个ICMP包。 } return 0; } //解包 void un_packet(int len) { int hlen1; double rtt; struct ip *ip; struct icmp *icmp; struct timeval *tvsend; ip = (struct ip *) recvbuf; hlen1 = ip->ip_hl << 2; if (ip->ip_p != IPPROTO_ICMP) return ; icmp = (struct icmp *) (recvbuf + hlen1); len -= hlen1; if ((icmp->icmp_type == ICMP_ECHOREPLY)) { if (icmp->icmp_id != pid) return; tvsend = (struct timeval *) icmp->icmp_data; //发送时间 gettimeofday(&tvrecv, NULL); //得到当前时间 tv_sub(&tvrecv, tvsend); //计算接收和发送的时间差 rtt = tvrecv.tv_sec * 1000.0 + tvrecv.tv_usec / 1000.0; //以毫秒单位计算rtt 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); nrecv++; } } //手动构建数据包,并通过原始套接字发送 void send_packet() { int len; struct icmp *icmp; icmp = (struct icmp *) (sendbuf); icmp->icmp_type = ICMP_ECHO; //拼接icmp icmp->icmp_code = 0; icmp->icmp_id = pid; //2字节 icmp->icmp_seq = nsent++; //2字节 memset(icmp->icmp_data, 0xa5, datalen); gettimeofday((struct timeval *) icmp->icmp_data, NULL); //将发送时间作为数据传递过去 len = datalen + 8; icmp->icmp_cksum = 0; //校验和需要先置0 icmp->icmp_cksum = in_chksum((unsigned short *) icmp, len); //计算效验和 sendto(sockfd, sendbuf, len, 0, (struct sockaddr *) &dest_addr, sizeof(dest_addr)); //将包发出去 //printf("package have sent!\n"); } //计算效验和 unsigned short in_chksum(unsigned short *addr, int len) { int nleft = len; int sum = 0; unsigned short *w = addr; unsigned short answer = 0; //把ICMP报头二进制数据以2字节为单位累加起来 while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(unsigned char *) (&answer) = *(unsigned char *) w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return answer; } //计算时间差 void tv_sub(struct timeval *out, struct timeval *in) { if ((out->tv_usec -= in->tv_usec) < 0) { --out->tv_sec; out->tv_usec += 1000000; } out->tv_sec -= in->tv_sec; }
myping
相关文章推荐
- 【有感】——在错误中成长
- CGI与Servlet的区别和联系
- 学习笔记(二)
- 学习笔记(二)
- 内核态和用户态通讯知识收集
- Android项目-PopupWndow与应用的启动与卸载
- angularjs 验证码60秒倒计时
- XtraReport 实例化 打印
- Emmet插件使用方法总结
- Android项目-快捷方式
- 百度地图工具类封装(包括定位,附近、城市、范围poi检索,反地理编码)
- ”在禁用UAC时,无法激活此应用“问题
- Android进阶-如何避免频繁修改UI代码
- Java 去除字符串中的标点
- RollPitchYaw傻傻分不清
- VMware安装windows XP OS 虚拟机时常见的问题
- UI基础_UITableView
- openfire插件编译
- Android进阶-分页与分批及XUtils的注解来代替findViewById
- linux shell中find的使用