#include <fcntl.h>
#include <netdb.h>
#include <iostream>
#include <netinet/icmp6.h>
//#include "ProcessInfo.h"
#include "IpCheckPing.h"
using namespace std;
#define LogitPing(level_Info, ...) printf(__VA_ARGS__)
//#define LogitPing(level_Info, ...)
CIpCheckPing::CIpCheckPing(int iPacketNum, int iMicrosecondsTimeOut)
{
m_iMicrosecondsTimeOut = iMicrosecondsTimeOut;
m_iPacketNum = iPacketNum;
m_iSocket = 0;
m_iMyIdent = 0;
m_iSeq = 0;
m_strIp = "";
m_pRequestIp = NULL;
m_strDeviceId = "";
memset(&m_requestIpHints, 0, sizeof(m_requestIpHints));
}
CIpCheckPing::~CIpCheckPing(void)
{
if (m_pRequestIp != NULL)
{
freeaddrinfo(m_pRequestIp);
}
close(m_iSocket);
//printf("%d\n",m_iSocketFD);
}
int CIpCheckPing::SetSocketOpt()
{
if (m_iSocket > 0)
{
close(m_iSocket);
}
memset(&m_requestIpHints, 0, sizeof(m_requestIpHints));
m_requestIpHints.ai_socktype = SOCK_RAW;
m_requestIpHints.ai_family = m_strIp.find(":") != string::npos ? AF_INET6 : AF_INET;
m_requestIpHints.ai_protocol = m_requestIpHints.ai_family == AF_INET ? IPPROTO_ICMP : IPPROTO_ICMPV6;
m_requestIpHints.ai_flags = 0;
int iRet = getaddrinfo(m_strIp.c_str(), NULL, &m_requestIpHints, &m_pRequestIp);
if (iRet != 0)
{
LogitPing(level_Info, "[--deviceid:%s,device_ip:%s getaddrinfo: %s]", m_strDeviceId.c_str(), m_strIp.c_str(), gai_strerror(iRet));
return -1;
}
m_iSocket = socket(m_pRequestIp->ai_family, m_pRequestIp->ai_socktype, m_pRequestIp->ai_protocol);
if (m_iSocket < 0)
{
LogitPing(level_Info, "[--deviceid:%s,device_ip:%s m_iSocket:%d err: %s]", m_strDeviceId.c_str(), m_strIp.c_str(), m_iSocket, strerror(errno));
return -1;
}
//设置为非阻塞
int on = 1;
int flags = fcntl(m_iSocket, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(m_iSocket, F_SETFL, flags);
struct timeval timeout = { 5, 0 }; // 5s
setsockopt(m_iSocket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
setsockopt(m_iSocket, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
if (m_requestIpHints.ai_family ==AF_INET6)
{
//获取校验和的位置
int sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
//显示的声明让内核自动计算校验和(虽然默认是打开的)
setsockopt(m_iSocket, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
}
return 0;
}
int CIpCheckPing::GetResult(SPingDeviceStauts *pHost)
{
//struct protoent *proto;
if (SetSocketOpt() != 0)
{
return -1;
}
struct timeval tvStartTime;
struct timeval tvEndTime; /* current time (pseudo) */
pHost->m_strDeviceId = m_strDeviceId;
pHost->m_iNumPacketsSend = 0;
pHost->m_iNumPacketsRecvd = 0;
pHost->m_bIsAlive = false;
for (int i = 0; i < m_iPacketNum; i++)
{
m_iSeq = i + 1;
memset(&tvStartTime, 0, sizeof(tvStartTime));
gettimeofday(&tvStartTime, NULL);
int iRet = SendPing(m_iSeq);
if (iRet == 0)
{
pHost->m_iNumPacketsSend += 1;
}
int iReply = -1;
while (iReply == -1)
{
iReply = WaitForReply();
//超时退出
if (iReply == 0)
{
break;
}
memset(&tvEndTime, 0, sizeof(tvEndTime));
gettimeofday(&tvEndTime, NULL);
//开始收报到现在的时间差> P_DEFAULT_TIMEOUT ,收到恢复或者 超过超时事件就退出
if (TimeValDiff(&tvEndTime, &tvStartTime) > m_iMicrosecondsTimeOut)
{
break;
}
}
if (iReply == 1)
{
memset(&tvEndTime, 0, sizeof(tvEndTime));
gettimeofday(&tvEndTime, NULL);
pHost->m_iNumPacketsRecvd = pHost->m_iNumPacketsRecvd + 1;
pHost->m_dDelay = TimeValDiff(&tvEndTime, &tvStartTime) / 1000;
LogitPing(level_Info, "%d bytes from %s : icmp_seq=%d time=%.3f ms \r\n", 64, m_strIp.c_str(), m_iSeq, pHost->m_dDelay);
}
}
if (pHost->m_iNumPacketsRecvd > 0)
{
pHost->m_bIsAlive = true;
return 0;
}
return -1;
}
/*
* return
* 0:超时 -1:需要重新接收 1: 正常
*/
int CIpCheckPing::WaitForReply()
{
if (SocketCanRead() == -1)
{
return P_TIMEOUT;
}
char pRevBuf[4096] = { 0 };
struct sockaddr responseIp;
int iRecvBytes = recvfrom(m_iSocket, pRevBuf, 4096, 0, &responseIp, &m_pRequestIp->ai_addrlen);
if (iRecvBytes <= 0)
{
return P_TIMEOUT; /* timeout */
}
if (m_requestIpHints.ai_family == AF_INET)
{
struct ip *stipThis = (struct ip *)pRevBuf;
int ipHlen = (stipThis->ip_hl & 0x0F) << 2;
if (iRecvBytes < ipHlen + ICMP_MINLEN)
{
LogitPing(level_Info, "[--deviceid:%s,device_ip:%s iRecvResult:%d<%d]", m_strDeviceId.c_str(), m_strIp.c_str(), iRecvBytes, ipHlen + ICMP_MINLEN);
return P_MISMATCH;/* too short */
}
struct icmp *icmp_rep = (struct icmp *)(pRevBuf + ipHlen);
//只关注echo replies
if (icmp_rep->icmp_type != ICMP_ECHOREPLY || ntohs(icmp_rep->icmp_id) != m_iMyIdent || ntohs(icmp_rep->icmp_seq) != m_iSeq)
{
LogitPing(level_Info, "[--deviceid:%s,device_ip:%s icp->icmp_type:%d!=%d icp->icmp_id:%d ident:%d icmp_rep->icmp_seq:%d iSeq:%d]",
m_strDeviceId.c_str(), m_strIp.c_str(), icmp_rep->icmp_type, ICMP_ECHOREPLY, ntohs(icmp_rep->icmp_id), m_iMyIdent, ntohs(icmp_rep->icmp_seq), m_iSeq);
return P_MISMATCH; // packet received, but not the one we are looking for!
}
}
else
{
if (iRecvBytes < sizeof(struct icmp6_hdr))
{
LogitPing(level_Info, "[--deviceid:%s,device_ip:%s iRecvResult:%d<%d]", m_strDeviceId.c_str(), m_strIp.c_str(), iRecvBytes, sizeof(struct icmp6_hdr));
return P_MISMATCH; /* too short */
}
struct icmp6_hdr *icp = (struct icmp6_hdr *)pRevBuf;
if (icp->icmp6_type != ICMP6_ECHO_REPLY || ntohs(icp->icmp6_id) != m_iMyIdent || ntohs(icp->icmp6_seq) != m_iSeq)
{
LogitPing(level_Info, "[--deviceid:%s,device_ip:%s icp->icmp_type:%d!=%d icp->icmp6_id:%d ident:%d icp->icmp6_seq:%d iSeq:%d]",
m_strDeviceId.c_str(), m_strIp.c_str(), icp->icmp6_type, ICMP_ECHOREPLY, ntohs(icp->icmp6_id), m_iMyIdent, ntohs(icp->icmp6_seq), m_iSeq);
return P_MISMATCH;
}
}
if (AddrCompare(m_pRequestIp->ai_addr, &responseIp) != 0)
{
char buf[256] = { 0 };
const char *res;
if (m_pRequestIp->ai_family == AF_INET6)
{
res = inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&responseIp)->sin6_addr, buf, sizeof(buf));
}
else
{
res = inet_ntop(AF_INET, &((struct sockaddr_in *)&responseIp)->sin_addr, buf, sizeof(buf));
}
LogitPing(level_Info, "[--deviceid:%s,device_ip:%s replay ip %s is not expect]", m_strDeviceId.c_str(), m_strIp.c_str(), res == NULL ? "" : res);
return P_MISMATCH;
}
return 1;
}
int CIpCheckPing::SetDevice(const char *sIp, const char *sDeviceId)
{
m_strDeviceId = sDeviceId;
m_iMyIdent = getpid() & 0xFFFF; //pthread_self();
m_strIp = sIp;
return 0;
}
int CIpCheckPing::SendPing(unsigned int iIndex)
{
int pingPacketSize = 56 + ICMP_MINLEN;
char *pData = new char[pingPacketSize];
memset(pData, 0x00, pingPacketSize);
if (m_requestIpHints.ai_family == AF_INET)
{
//cout<<"sizeof(struct icmp):"<<sizeof(struct icmp)<<endl; --sizeof(struct icmp):28
//设定包大小 64个包
struct icmp *pIcmp = (struct icmp *)pData;
pIcmp->icmp_type = ICMP_ECHO;
pIcmp->icmp_code = 0;
pIcmp->icmp_seq = htons(iIndex);
pIcmp->icmp_id = htons(m_iMyIdent);
pIcmp->icmp_cksum = 0;
for (int n = ((char*)&pIcmp->icmp_data - (char*)pIcmp); n < pingPacketSize; ++n)
{
pData[n] = n ;
}
pIcmp->icmp_cksum = InCheckSum((u_short *)pIcmp, pingPacketSize);
}
else
{
//cout<<"sizeof(struct icmp6_hdr):"<<sizeof(struct icmp6_hdr)<<endl; //sizeof(struct icmp6_hdr):8
struct icmp6_hdr *pIcmp6 = (struct icmp6_hdr *)pData;
pIcmp6->icmp6_type = ICMP6_ECHO_REQUEST;
pIcmp6->icmp6_code = 0;
pIcmp6->icmp6_id = htons(m_iMyIdent);
pIcmp6->icmp6_seq = htons(iIndex);
//The IPv6 stack calculates the checksum for us
pIcmp6->icmp6_cksum = 0;
for (int n = sizeof(struct icmp6_hdr); n < pingPacketSize; ++n)
{
pData[n] = n ;
}
}
//cout<<"sizeof (struct sockaddr_in6):"<<sizeof (struct sockaddr_in6)<< "(int) m_pRequestIp->ai_addrlen:"<<(int) m_pRequestIp->ai_addrlen<<endl;
int iBytes = sendto(m_iSocket, pData, pingPacketSize, 0, m_pRequestIp->ai_addr, (int)m_pRequestIp->ai_addrlen);
delete [] pData;
if (iBytes < 0)
{
perror("发送失败");
return -1;
}
return 0;
}
unsigned short CIpCheckPing::InCheckSum(u_short *upIcp, int iLen)
{
unsigned long sum;
/* initialize sum to zero and loop until length (in words) is 0 */
for (sum = 0; iLen > 1; iLen -= 2) /* sizeof() returns number of bytes, we're interested in number of words */
{
sum += *upIcp++; /* add 1 word of buffer to sum and proceed to the next */
}
/* we may have an extra byte */
if (iLen == 1)
{
sum += (char) * upIcp;
}
sum = (sum >> 16) + (sum & 0xFFFF); /* add high 16 to low 16 */
sum += (sum >> 16); /* add carry */
return ~sum;
}
int CIpCheckPing::AddrCompare(struct sockaddr *pRequestIp, struct sockaddr *pResponseIp)
{
if (pRequestIp->sa_family != pResponseIp->sa_family)
{
return pRequestIp->sa_family - pResponseIp->sa_family;
}
else
{
if (pRequestIp->sa_family == AF_INET)
{
return ((struct sockaddr_in *)pRequestIp)->sin_addr.s_addr - ((struct sockaddr_in *)pResponseIp)->sin_addr.s_addr;
}
else if (pRequestIp->sa_family == AF_INET6)
{
return memcmp(&((struct sockaddr_in6 *)pRequestIp)->sin6_addr,
&((struct sockaddr_in6 *)pResponseIp)->sin6_addr,
sizeof(((struct sockaddr_in6 *)pResponseIp)->sin6_addr));
}
}
return 0;
}
//return -1 :数据不可读 0:数据可读
int CIpCheckPing::SocketCanRead()
{
char szTmp[100] = { 0 };
int iRet;
fd_set readSet;
struct timeval sTv;
if (m_iMicrosecondsTimeOut < 1000000)
{
sTv.tv_sec = 0;
sTv.tv_usec = m_iMicrosecondsTimeOut;
}
else
{
sTv.tv_sec = m_iMicrosecondsTimeOut / 1000000;
sTv.tv_usec = m_iMicrosecondsTimeOut % 1000000;
}
select_again:
FD_ZERO(&readSet);
FD_SET(m_iSocket, &readSet);
// ((void)(((&ReadSet)->fds_bits)[((m_iSocket)/(8*(int)sizeof(__fd_mask)))]|=((__fd_mask)(1UL<<((m_iSocket)%(8*(int)sizeof(__fd_mask)))))))
iRet = select(m_iSocket + 1, &readSet, NULL, NULL, &sTv);
if (iRet < 0)
{
if (errno == EINTR)
{
LogitPing(level_Info, "[--deviceid:%s,device_ip:%s select_again errmsg:%s]", m_strDeviceId.c_str(), m_strIp.c_str(), strerror(errno));
goto select_again;
}
LogitPing(level_Info, "[--deviceid:%s,device_ip:%s errmsg:%s]", m_strDeviceId.c_str(), m_strIp.c_str(), strerror(errno));
return -1;
}
if (iRet == 0)
{
LogitPing(level_Info, "[--deviceid:%s,device_ip:%s select time out]", m_strDeviceId.c_str(), m_strIp.c_str());
return -1;
}
if (FD_ISSET(m_iSocket, &readSet))
{
return 0;
}
return -1;
}
double CIpCheckPing::TimeValDiff(struct timeval *stmFirst, struct timeval *stmNext)
{
return ((stmFirst->tv_sec - stmNext->tv_sec) * 1000000 + stmFirst->tv_usec - stmNext->tv_usec);
}
int main(int arg, char **argv)
{
string m_strIp = "fe80::215:5dff:fec9:85d2";
m_strIp = "192.168.83.0";
CIpCheckPing ping(10, 2000000);
SPingDeviceStauts *pSecond = new SPingDeviceStauts();
ping.SetDevice(m_strIp.c_str(), "123444");
//返回为0 就为ping正常
int iPingRet = ping.GetResult(pSecond);
cout << "===============" << endl;
cout << iPingRet << endl;
delete pSecond;
return iPingRet;
}
#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#define P_MISMATCH -1
#define P_TIMEOUT 0
struct SPingDeviceStauts
{
string m_strDeviceId; /*id of device*/
int m_iNumPacketsRecvd; /*number of ping reply packets*/
bool m_bIsAlive; /*是否ping通*/
int m_iNumPacketsSend; /*number of packets sended*/
double m_dDelay;
};
class CIpCheckPing
{
private:
string m_strIp;
struct addrinfo m_requestIpHints, *m_pRequestIp;
string m_strDeviceId;
int m_iSocket;
unsigned long m_iMyIdent;
int m_iSeq;
//单个包最大等待事件,单位微秒
int m_iMicrosecondsTimeOut;
//ping次数
int m_iPacketNum;
private:
int SendPing(unsigned int iIndex);
unsigned short InCheckSum(u_short *upThis, int iLen);
int WaitForReply();
double TimeValDiff(struct timeval *stmFirst, struct timeval *stmNext);
int SocketCanRead();
int AddrCompare(struct sockaddr *pRequestIp, struct sockaddr *pResponseIp);
int SetSocketOpt();
public:
CIpCheckPing(int iPacketNum, int iMicrosecondsTimeOut);
~CIpCheckPing(void);
int SetDevice(const char *sIp, const char *sDeviceId);
int GetResult(SPingDeviceStauts *pHost);
};