ICMP ping IPV4 IPV6 实现

#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);
};


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值