#include <stdio.h> #include <arpa/inet.h> #include <unistd.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <netdb.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <iostream> using namespace std; #define PING_DATA_LEN 56 //ICMP消息头部 struct ICMPHeader { unsigned char type;//消息类型 unsigned char code;//消息代码 unsigned short checksum;//校验和 union{ struct{ unsigned short id; unsigned short sequence; }echo; unsigned int gateway; struct{ unsigned short unsed; unsigned short nextmtu; }frag; //pmtu实现 }un; unsigned char data[0];//ICMP数据占位符 }; struct IPHeader { unsigned char headerLen:4; unsigned char version:4; unsigned char tos; //服务类型 unsigned short totalLen; //总长度 unsigned short id; //标识 unsigned short flagOffset; //3位标志+13位片偏移 unsigned char ttl; //TTL unsigned char protocol; //协议 unsigned short checksum; //首部检验和 unsigned int srcIP; //源IP地址 unsigned int dstIP; //目的IP地址 }; //This function calculates the 16-bit one's complement sum //of the supplied buffer (ICMP) header unsigned short checksum(unsigned short* buffer, int size) { unsigned long cksum = 0; while (size > 1) { cksum += *buffer++; size -= sizeof(unsigned short); } if (size) { cksum += *(unsigned char*)buffer; } cksum = (cksum>>16) + (cksum & 0xffff); cksum += (cksum>>16); return (unsigned short)(~cksum); } //校验和算法 unsigned short cal_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; } //若ICMP报头为奇数个字节,会剩下最后一字节。把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加 if( nleft==1) { *(unsigned char *)(&answer)=*(unsigned char *)w; sum+=answer; } sum=(sum>>16)+(sum&0xffff); sum+=(sum>>16); answer=~sum; return answer; } //ip数字转字符串 void ip_ll_to_str(long long ip_num,char* ip_str) { unsigned int iptok1 = (ip_num & 0xFF000000) >> 24; unsigned int iptok2 = (ip_num & 0x00FF0000) >> 16; unsigned int iptok3 = (ip_num & 0x0000FF00) >> 8; unsigned int iptok4 = ip_num & 0x000000FF; char ip[32]; bzero(ip,sizeof(ip)); snprintf(ip,sizeof(ip),"%d.%d.%d.%d",iptok1,iptok2,iptok3,iptok4); strcpy(ip_str,ip); } //发送ICMP报文 void send_icmp_packet(int sockfd,sockaddr_in* dst_addr,int pid,bool build_ip_protocl) { char sendBuf[1024] = ""; int totalLen = sizeof(IPHeader) + sizeof(ICMPHeader)+PING_DATA_LEN; int pos = 0; if(build_ip_protocl) { IPHeader* ipHeader = (IPHeader *)sendBuf; ipHeader->headerLen = sizeof(IPHeader)>>2; ipHeader->version = IPVERSION; //服务类型 ipHeader->tos = 0; ipHeader->totalLen = htons(totalLen); ipHeader->id=0; //设置flag标记为0 ipHeader->flagOffset=0; //运用的协议为ICMP协议 ipHeader->protocol=IPPROTO_ICMP; //一个封包在网络上可以存活的时间 ipHeader->ttl=255; //目的地址 ipHeader->dstIP = dst_addr->sin_addr.s_addr; pos = sizeof(IPHeader); } ICMPHeader *icmpHeader = (ICMPHeader*)(sendBuf+pos); icmpHeader->type = ICMP_ECHO; icmpHeader->code = 0; icmpHeader->un.echo.id = pid; //计算校验和 icmpHeader->checksum = cal_chksum( (unsigned short *)icmpHeader,totalLen); IPHeader* ipHeader = (IPHeader *)sendBuf; char ipHeaderStr[256] = ""; char srcIPStr[64] = "",dstIPStr[64]=""; ip_ll_to_str(ipHeader->srcIP,srcIPStr); ip_ll_to_str(ipHeader->dstIP,dstIPStr); snprintf(ipHeaderStr,sizeof(ipHeaderStr),"request ip header info: version:%d,tos:%d,protocol:%d,ttl:%d,srcIP:%s,dstIP:%s",ipHeader->version,ipHeader->tos,ipHeader->protocol,ipHeader->ttl,srcIPStr,dstIPStr); cout << ipHeaderStr << endl; if(sendto(sockfd,sendBuf,totalLen,0,(struct sockaddr *)dst_addr,sizeof(*dst_addr))<0){ perror("sendto error"); } } //接收解析ICMP报文 void parse_icmp_packet(int sockfd,int pid) { sockaddr_in cliaddr; bzero(&cliaddr,sizeof(cliaddr)); socklen_t cliLen = sizeof(cliaddr); char recvBuf[256] = ""; int recvLen = recvfrom(sockfd,recvBuf,sizeof(recvBuf),0,(sockaddr*)&cliaddr,&cliLen); if( recvLen <0) { if(errno==EINTR){ return; } printf("recvfrom error:%s",strerror(errno)); return; } IPHeader *ipHeader = (IPHeader*)recvBuf; char ipHeaderStr[256] = ""; char srcIPStr[64] = "",dstIPStr[64]=""; ip_ll_to_str(ntohl(ipHeader->srcIP),srcIPStr); ip_ll_to_str(ntohl(ipHeader->dstIP),dstIPStr); snprintf(ipHeaderStr,sizeof(ipHeaderStr),"response ip header info: version:%d,tos:%d,protocol:%d,ttl:%d,srcIP:%s,dstIP:%s",ipHeader->version,ipHeader->tos,ipHeader->protocol,ipHeader->ttl,srcIPStr,dstIPStr); cout << ipHeaderStr << endl; int ipHeaderLen = sizeof(IPHeader); ICMPHeader *icmpHeader = (ICMPHeader *)(recvBuf+sizeof(IPHeader)); //越过ip报头,指向ICMP报头 int icmpLen = recvLen - ipHeaderLen; //小于ICMP报头长度则不合理 if( icmpLen < 8) { printf("ICMP packets/'s length is less than 8/n"); exit(1); } //确保所接收的是我所发的的ICMP的回应 if(icmpHeader->type!=ICMP_ECHOREPLY || icmpHeader->un.echo.id!=pid){ exit(1); } char icmpStr[256] = ""; snprintf(icmpStr,sizeof(icmpStr),"%d byte from %s: icmp_seq=%d,ttl=%dms",icmpLen,inet_ntoa(cliaddr.sin_addr),icmpHeader->un.echo.sequence,ipHeader->ttl); cout << icmpStr << endl; } int main() { int sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP); if( sockfd < 0) { cout << strerror(errno) << endl; return -1; } //扩大套接字接收缓冲区到50K这样做主要为了减小接收缓冲区溢出的的可能性,若无意中ping一个广播地址或多播地址,将会引来大量应答 int bufSize=50*1024; setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&bufSize,sizeof(bufSize) ); //是否自己构造ip协议头 bool build_ip_protocol = true; if(build_ip_protocol) { int on = 1; setsockopt(sockfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on)); } //用主机名或ip地址都可以 //www.baidu.com char dst_str[32] = "220.181.112.244"; sockaddr_in dst_addr; bzero(&dst_addr,sizeof(dst_addr)); dst_addr.sin_family=AF_INET; dst_addr.sin_addr.s_addr = inet_addr(dst_str); if( dst_addr.sin_addr.s_addr == INADDR_NONE) { hostent *host=gethostbyname(dst_str); if(host==NULL) //是主机名 { cout << "gethostbyname error:" << strerror(errno) <<endl; return -1; } memcpy( (char *)&dst_addr.sin_addr,host->h_addr,host->h_length); } //获取main的进程id,用于设置ICMP的标志符 int pid=getpid(); //发送ICMP报文 send_icmp_packet(sockfd,&dst_addr,pid,build_ip_protocol); cout << "PING " << dst_str << "(" << inet_ntoa(dst_addr.sin_addr) << ") " << PING_DATA_LEN << " bytes data in ICMP packets" << endl; //解析所有ICMP报文 parse_icmp_packet(sockfd,pid); close(sockfd); return 0; } //这里的srcIP是0.0.0.0,但实际发出去的报文是我本地的ip,也就是说如果你没设置IP,则系统会自动给你加上IP request ip header info: version:4,tos:0,protocol:1,ttl:255,srcIP:0.0.0.0,dstIP:244.112.181.220 PING 220.181.112.244(220.181.112.244) 56 bytes data in ICMP packets response ip header info: version:4,tos:0,protocol:1,ttl:45,srcIP:220.181.112.244,dstIP:172.16.96.52 64 byte from 220.181.112.244: icmp_seq=0,ttl=45ms
rawsocket发送icmp包
猜你喜欢
转载自xiangjie88.iteye.com/blog/2392275
今日推荐
周排行