阅读更多
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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) <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