首页 > 技术知识 > 正文

一 原理

ping命令工作在ip层,在程序中通过raw scket进行数据的收发,发数据时不需要填充ip头部,但是在接收数据时需要过滤掉ip头部信息。icmp头部重要的字段有三 个,type,code,checksum,其中type表示命令的类型,对于ping命令来说,type的值为8表示发送icmp,type值为0表示 是icmp的回包,code表示type下的子命令,对于ping命令来说,code的值为0。checksum字段表示icmp包的校验字段,校验方法 为对icmp的所有字段取和再取反码。

二 实现代码:

ping.h

#ifndef __SAM_PING_

#define __SAM_PING_

#include “stdlib.h”

#include “string.h”

#include “stdio.h”

#include “fcntl.h”

#include “errno.h”

#include “signal.h”

#include “sys/types.h”

#include “sys/socket.h”

#include “sys/time.h”

#include “netinet/in.h”

#include “arpa/inet.h”

#include “netdb.h”

#define MAXBUFFLEN 200

#define ICMP_ECHO 8 /* icmp echo requir */

#define ICMP_ECHOREPLY 0 /* icmp echo reply */

#define ICMP_HEADSIZE 8 /* icmp packet header size */

#define IP_HEADSIZE 20 /* ip packet header size */

#pragma pack(1)

typedef struct tagIpHead /* icmp packet header */

{

u_char ip_verlen; /* ip version and ip header lenth*/

u_char ip_tos; /* ip type of service */

u_short ip_len; /* ip packet lenghth */

u_short ip_id; /* ip packet identification */

u_short ip_fragoff; /* ip packet fragment and offset */

u_char ip_ttl; /* ip packet time to live */

u_char ip_proto; /* ip packet protocol type */

u_short ip_chksum; /* ip packet header checksum */

u_long ip_src_addr; /* ip source ip adress */

u_long ip_dst_addr; /* ip destination ip adress */

} IPHEAD;

typedef struct tagIcmpHead /* icmp header */

{

u_char icmp_type; /* icmp service type */

/* 8 echo require, 0 echo reply */

u_char icmp_code; /* icmp header code */

u_short icmp_chksum; /* icmp header chksum */

u_short icmp_id; /* icmp packet identification */

u_short icmp_seq; /* icmp packet sequent */

u_char icmp_data[1]; /* icmp data, use as pointer */

} ICMPHEAD;

#pragma pack()

class CICMP

{

private:

int m_iSocket;

int m_iPkgSize;

long m_iSendTime; /*发送ping包的时间戳*/

long m_iRecvTime; /*接收ping包的时间戳*/

long m_iDelay;

IPHEAD m_stIpHead;

ICMPHEAD m_stIcmpHead;

char m_szIcmpData[MAXBUFFLEN];

protected:

long TimeNow();

bool validChkSum(ushort* buffer,int size);

u_short ChkSum(u_short* pIcmpData,int iDataLen);

int ParseRecv(IPHEAD* pHeader,int size);

public:

CICMP(int pkgSize = 0):m_iPkgSize(pkgSize){memset(m_szIcmpData,0,MAXBUFFLEN);}

int CreateRawSocket();

int Ping(const char* szIp,int pkgSize);

int Stat();

int Receive();

};

#endif
<

ping.cpp

#include “ping.h”

#include

#include

using namespace std;

u_short CICMP::ChkSum( u_short * buffer, int size )

/* for check sum of icmp header */

{

unsigned long cksum=0;

while(size >1)

{

cksum+=*buffer++;

size-=sizeof(unsigned short);

}

if(size) cksum+=*(unsigned short*)buffer;

cksum=(cksum >> 16)+(cksum&0xffff);

cksum+=(cksum >>16);

return (unsigned short)(~cksum);

}

bool CICMP::validChkSum(unsigned short *buffer, int size)

{

unsigned long cksum=0;

while(size >1)

{

cksum+=*buffer++;

size-=sizeof(unsigned short);

}

if(size) cksum+=*(unsigned short*)buffer;

cksum=(cksum >> 16)+(cksum&0xffff);

cksum+=(cksum >>16);

return ((unsigned short)cksum == 0xFFFF);

}

int CICMP::CreateRawSocket()

{

this->m_iSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

if(this->m_iSocket < 0)

{

perror(“socket”);

return -1;

}

return 0;

}

long CICMP::TimeNow()

{

struct timeval now;

long lPassed;

gettimeofday(&now, 0);

lPassed = now.tv_sec * 1000000 + now.tv_usec;

/* now.tv_sec in second */

/* now.tv_usec in 1/1000000 second */

return lPassed;

}

int CICMP::Ping(const char* szIp,int pkgSize)

{

struct sockaddr_in descAddr;

bzero(&descAddr, sizeof(descAddr));

descAddr.sin_family = AF_INET;

u_long lHostIp;

struct hostent* h = NULL;

/* check host format */

if ( ( lHostIp = inet_addr(szIp) ) != INADDR_NONE )

{

/* is available ip adress */

//cout << “is available ip address” << endl;

descAddr.sin_addr.s_addr = lHostIp;

}

else if ( h = gethostbyname(szIp))

{

/* is available host name */

/* from hosts file of local host */

/* or from DNS */

//cout << “is available host name” << endl;

bcopy(h->h_addr, &descAddr.sin_addr, h->h_length);

}

else

{

/* bad ip adress or host name */

/* exit */

fprintf( stderr, “bad IP or host\n” );

return -1;

}

cout << “ping ” << szIp<< endl;

//begin Ping

int iPacketSize = 0;

char* buf = new char[MAXBUFFLEN];

memset(buf,0,MAXBUFFLEN);

/* make the icmp header information */

ICMPHEAD *pIcmpHead = (ICMPHEAD *)buf;

pIcmpHead->icmp_type = ICMP_ECHO;

pIcmpHead->icmp_code = 0;

pIcmpHead->icmp_id = 1;

pIcmpHead->icmp_seq = 1;

pIcmpHead->icmp_chksum = 0;

/* store time information as icmp packet content, 4 bytes */

/* u may store other information instead */

*((long *)pIcmpHead->icmp_data) = TimeNow();

this->m_iSendTime = TimeNow();

//iPacketSize = ICMP_HEADSIZE + 4; /* icmp packet length */

iPacketSize = sizeof(ICMPHEAD) + 3;

/* icmp header check sum */

pIcmpHead->icmp_chksum = this->ChkSum((u_short *)pIcmpHead, iPacketSize );

/* remember the time when send for calculate round trip time */

//lSendTime = time_now();

/* send the icmp packet to des host */

//cout << “send pkg size ” << iPacketSize << endl;

if (sendto(m_iSocket, buf, iPacketSize, 0, (struct sockaddr *)&descAddr, sizeof(descAddr) ) < 0)

{

perror(“send failed”);

delete [] buf;

return -1;

}

delete [] buf;

//usleep(0);

return 0;

}

int CICMP::Receive()

{

struct sockaddr_in fromAddr;

int size = 0;

char* buf = new char[MAXBUFFLEN];

memset(buf,0,MAXBUFFLEN);

int namelen = sizeof(fromAddr);

//cout << “before recvfrom” << endl;

size = recvfrom(this->m_iSocket,buf,MAXBUFFLEN – 1,0,(struct sockaddr *)&fromAddr,(socklen_t*)&namelen);

if(size == -1)

{

cout << “recv ping back failed:” << strerror(errno) << endl;

return -1;

}

IPHEAD* pIpHead = (IPHEAD *)buf;

//cout << “after recvfrom:” << size << endl;

/* get the ip packet lenth */

/* if too small, not the icmp echoreply packet */

/* give it up */

int iIpHeadLen = (int)((pIpHead->ip_verlen & 0x0f) << 2);

if (size < iIpHeadLen + ICMP_HEADSIZE)

{

cout << “recv header is too short,give it up” << endl;

delete buf;

return -1;

}

//int ttl = pIpHead->ip_ttl; /* time to live param */

/* get the icmp header information */

ICMPHEAD *pIcmpHead = (ICMPHEAD *)(buf + iIpHeadLen);

/* not icmp echo reply packet, give it up */

if (pIcmpHead->icmp_type != ICMP_ECHOREPLY)

{

cout << “recv pkg not ICMP reply,give it up” << endl;

delete buf;

return -1;

}

/* not proper icmp sequent number, give it up */

if (pIcmpHead->icmp_id != 1 || pIcmpHead->icmp_seq != 1)

{

cout << “icmp headers id and seq error” << endl;

delete buf;

return -1;

}

long iCurTime = this->TimeNow();

this->m_iRecvTime = iCurTime;

ParseRecv((IPHEAD *)buf,size);

delete buf;

return 0;

}

int CICMP::ParseRecv(IPHEAD * pHeader,int size)

{

memcpy(&m_stIpHead,pHeader,sizeof(IPHEAD));

char* szTmp = (char*)pHeader;

char* ptrIcmp = szTmp + sizeof(IPHEAD);

memcpy(&m_stIcmpHead,ptrIcmp,sizeof(ICMPHEAD));

/* get the data of icmp*/

szTmp += sizeof(ICMPHEAD) – 1;

int iDataLen = size – sizeof(IPHEAD) – sizeof(ICMPHEAD) + 1;

memcpy(m_szIcmpData,szTmp,iDataLen);

return 0;

}

int CICMP::Stat()

{

struct in_addr recvFrom;

memcpy(&recvFrom,&m_stIpHead.ip_src_addr,4);

u_char ipTTL = this->m_stIpHead.ip_ttl;

//int iSendTime = atoi(this->m_szIcmpData);

int iDelayTime = (int)((m_iRecvTime – m_iSendTime)/1000);

char szBuff[100] = {0};

char* szIp = inet_ntoa(recvFrom);

if(szIp == NULL)

{

cout << “get src ip failed” << endl;

return -1;

}

sprintf(szBuff,”recv from %s,delay=%ds,TTL=%d”,szIp,iDelayTime,ipTTL);

cout << szBuff << endl;

return 0;

}
<

#ifndef __SAM_PING_

#define __SAM_PING_

#include “stdlib.h”

#include “string.h”

#include “stdio.h”

#include “fcntl.h”

#include “errno.h”

#include “signal.h”

#include “sys/types.h”

#include “sys/socket.h”

#include “sys/time.h”

#include “netinet/in.h”

#include “arpa/inet.h”

#include “netdb.h”

#define MAXBUFFLEN 200

#define ICMP_ECHO 8 /* icmp echo requir */

#define ICMP_ECHOREPLY 0 /* icmp echo reply */

#define ICMP_HEADSIZE 8 /* icmp packet header size */

#define IP_HEADSIZE 20 /* ip packet header size */

#pragma pack(1)

typedef struct tagIpHead /* icmp packet header */

{

u_char ip_verlen; /* ip version and ip header lenth*/

u_char ip_tos; /* ip type of service */

u_short ip_len; /* ip packet lenghth */

u_short ip_id; /* ip packet identification */

u_short ip_fragoff; /* ip packet fragment and offset */

u_char ip_ttl; /* ip packet time to live */

u_char ip_proto; /* ip packet protocol type */

u_short ip_chksum; /* ip packet header checksum */

u_long ip_src_addr; /* ip source ip adress */

u_long ip_dst_addr; /* ip destination ip adress */

} IPHEAD;

typedef struct tagIcmpHead /* icmp header */

{

u_char icmp_type; /* icmp service type */

/* 8 echo require, 0 echo reply */

u_char icmp_code; /* icmp header code */

u_short icmp_chksum; /* icmp header chksum */

u_short icmp_id; /* icmp packet identification */

u_short icmp_seq; /* icmp packet sequent */

u_char icmp_data[1]; /* icmp data, use as pointer */

} ICMPHEAD;

#pragma pack()

class CICMP

{

private:

int m_iSocket;

int m_iPkgSize;

long m_iSendTime; /*发送ping包的时间戳*/

long m_iRecvTime; /*接收ping包的时间戳*/

long m_iDelay;

IPHEAD m_stIpHead;

ICMPHEAD m_stIcmpHead;

char m_szIcmpData[MAXBUFFLEN];

protected:

long TimeNow();

bool validChkSum(ushort* buffer,int size);

u_short ChkSum(u_short* pIcmpData,int iDataLen);

int ParseRecv(IPHEAD* pHeader,int size);

public:

CICMP(int pkgSize = 0):m_iPkgSize(pkgSize){memset(m_szIcmpData,0,MAXBUFFLEN);}

int CreateRawSocket();

int Ping(const char* szIp,int pkgSize);

int Stat();

int Receive();

};

#endif
<

测试程序

int main(int argc,char** argv)

{

if(argc != 2)

{

cout << “argument: host” << endl;

return 0;

}

char* szIp = argv[1];

CICMP icmp;

if(icmp.CreateRawSocket() != 0)

{

cout << “create socket failed” << endl;

return 0;

}

if(icmp.Ping(szIp,4) != 0)

{

cout << “ping failed” << endl;

return 0;

}

//cout << “send success ,begin to receive” << endl;

if(icmp.Receive() != 0)

{

return 0;

}

icmp.Stat();

return 0;

}
<

猜你喜欢