linux下ping命令的c++实现

big_anana 2020-09-04 13:47:44 2407
一 原理

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 <iostream>
#include <string>

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 header's 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;
}
声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 点赞 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
big_anana
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区