汽车ac键不亮:ping源代码完全解析

来源:百度文库 编辑:偶看新闻 时间:2024/05/06 05:24:03
--------------------------------------------------
author:    Zero1,lingyi.pro#163.com
date:        2008-04-27
--------------------------------------------------
目录
-------------
1.相关知识
2.相关数据结构
3.相关函数
4.代码分析
5.小结
1.相关知识
-----------
ping命令可以查看一个系统到另一个系统是否可达,即判断网络连接是否正常。它的工作原理是:向网络上的另一台主机发送ICMP报文,并等待ICMP回显应答(ECHO_REPLY);如果目标系统接收到ICMP报文,它将返回给发送者一样的报文;同时ping可以计算这两台主机间的往返时间,以表明两主机间的距离。
ps:当然现在有些主机为了隐藏自己,对于ping发送来ICMP报文不返回回显信息。
想要深入了解ping的工作原理,还得了解ping命令所使用的TCP/IP协议。
ICMP(Internet Control Message Protocol,网际控制报文协议)是为网关和目标主机提供的一种差错控制机制,使它们在发生差错是把错误信息报告给源发送方(source sender)。ICMP协议是IP层的一个协议,但是由于差错报告发送会源发送方的过程中可能要经过若干子网,因此牵扯到路由选择问题,所以ICMP报文通常有IP协议来发送。于是ICMP数据报发送前需要进行两次封装:首先添加ICMP报头形成ICMP报文,再添加IP报头形成IP数据报,如下图所示
----------------
|       IP报头       |
|---------------------|
|  |----------------| |
| |    ICMP报头   ||
| |-----------------| |
| |  |------------| | |
| | | ICMP数据报 | | |
| |  |------------| | |
| |-----------------| |
|---------------------|
由于IP协议是一种点对点的协议,而不是端对端的协议,它提供无连接的数据包服务(通常使用UDP协议),所以不需要bind()和connect()函数来绑定和连接端口。用sendto()函数来发送数据报,接收数据使用recvfrom()函数。(更多信息参看socket编程相关资料)
2.相关数据结构
----------------
2.1 IP报头格式
IP报头格式:版本号(4bit),IP报头长度(4),服务类型(8),数据报长度(16),报文标志ID(16),报文标志F(3),分段偏移量(13),生存时间(8),协议号(8),报头校验和(16),源地址(32),目的地址(32),任选项和填充位(若干)。示意图如下:
--------------------------------------------------------------------------
| 版本号VER(4) | IP报头长度IHL(4) | 服务类型TOS(8) | 数据报长度TL(16) |
|------------------------------------------------------------------------|
|     报文标志ID(16)   |   报文标志F(3)   |    分段偏移量 F0(13)      |
|------------------------------------------------------------------------|
|     生存时间(8)    |     协议号PORT(8)    |     报头校验和(16)         |
|------------------------------------------------------------------------|
|                           源地址(32)                                    |
|------------------------------------------------------------------------|
|                           目的地址(32)                                |
|------------------------------------------------------------------------|
|                        任选项和填充位(若干)                              |
--------------------------------------------------------------------------
根据IP报头格式信息,可定义IP报头格式数据结构如下(更详细请阅读):
/****************************
*  计算icmp校验和算法
*  2008-05-02
* **************************/

unsigned short chksum(addr,len)
    unsigned short *addr;  /* 校验数据开始地址(注意是以2字节为单位) */
    int len;                /* 校验数据的长度大小,以字节为单位 */
{
    int sum = 0;        /* 校验和 */
    int nleft = len;    /* 未累加的数据长度 */
    unsigned short *p;  /* 走动的临时指针,2字节为单位 */
    unsigned short tmp = 0; /* 奇数字节长度时用到 */
   while( nleft > 1)
   {
       sum += *p++;    /* 累加 */
       nleft -= 2;
    }
   if(nleft == 1)      /* 奇数字节长度 */
   {
       *(unsigned char *)&tmp = *(&(unsigned char *)p); /* 将最后字节压如2字节的高位 */
       sum += tmp;
   }
    sum += (sum >> 16) + (sum & 0xffff);    /* 高位低位相加 */
    sum += sum >> 16;                       /* 上一步溢出时,将溢出位也加到sum中 */
    tmp = ~sum;     /* 注意类型转换,现在的校验和为16位 */
    return tmp;
}
ping程序只用到以下数据段:
ip_hl:IP报头长度(Internet Header Length),以4字节为一单位来记录IP报头长度。
ip_ttl:Time to Live,生存期,以秒为单位,指明此IP数据包在网络上停留的最长时间,其值由发送端设定,每经过一个路由,其值自减1,当值为0时,该IP数据包将被丢弃。
2.2 ICMP报头格式
ICMP分为错误报告报文和查询报文两种。每个ICMP报头均包含类型、编码和校验和三项,长度分别为8位、8位和16位,其他选项随ICMP功能的不同而不同(详见《TCP/IP协议详解-卷1:协议》p59)。
ICMP报头格式示意图如下:
----------------------------------------------
|   类型(8)  |  代码(8)  |  校验和(16)  |
|--------------------------------------------|
|          (不同类型代码有不同的内容)          |
----------------------------------------------
ping命令中只用到"回显应答"(ICMP_ECHO)和"回显请求"(ICMP_ECHOREPLY)两种。Linux下定义为:
#define ICMP_ECHO    0
#define ICMP_ECHOREPLY    8
这两种ICMP类型的报头格式示意图如下:
--------------------------------------------------------------
| 类型TYPE(0或8) | 编码CODE(没有使用) | 校验和SHECKSUM(16) |
|-------------------------------------------------------------|
|        标志符Identifier      |     序列号Sequence       |
---------------------------------------------------------------
Linux下ICMP数据结构的定义如下(详见):
struct icmp
{
    u_int8_t icmp_type;        /* 消息类型 */
    u_int8_t icmp_code;        /* 编码 */
    u_int16_t icmp_cksum;    /* 校验和 */
    union
    {
        u_char ih_pptr;        /* ICMP_PARAMPROB */
        struct in_addr ih_gwaddr;    /* 网关地址 */
        struct ih_idseq        /* 显示数据报 */
        {
            u_int16_t icd_id;    /*  */
            u_int16_t icd_seq;    /*  */
        }ih_idseq;

        u_int32_t ih_void;        /*  */

        /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery(rffc1191) */
        struct ih_pmtu
        {
            u_int16_t ipm_void;        /*  */
            u_int16_t ipm_nextmtu;    /*  */
        }ih_pmtu;

        struct ih_rtradv
        {
            u_int8_t irt_num_addr;    /*  */
            u_int8_t irt_wpa;        /*  */
            u_int16_t irt_lifetime;    /*  */
        }ih_rtradv;
    }icmp_hun;

    #define icmp_pptr    icmp_hun.ih_pptr
    #define icmp_gwaddr    icmp_hun.ih_gwaddr
    #define icmp_id    icmp_hun.ih_idseq.icd_id
    #define icmp_seq    icmp_hun.ih_idseq.icd_seq
    #define icmp_void    icmp_hun.ih_void
    #define icmp_pmvoid    icmp_hun.ih_pmtu.ipm_void
    #define icmp_nextmtu    icmp_hun.ih_pmtu.ipm_nextmtu
    #define icmp_num_addrs    icmp_hun.ih_rtradv.irt_num_addrs
    #define icmp_wpa    icmp_hun.ih_rtradv.irt_wpa
    #define icmp_lifetime    icmp_hun.ih_rtradv.irt_lifetime

    union
    {
        struct
        {
            u_int32_t its_otime;
            u_int32_t its_rtime;
            u_int32_t its_ttime;
        }id_ts;

        struct
        {
            struct ip idi_ip;
            /* 选项及64位数据 */
        }id_ip;

        struct icmp_ra_addr id_radv;
        u_int32_t id_mask;    /**/
        u_int8_t id_data[1];
    }icmp_dun;

    #define icmp_otime    icmp_dun.id_ts.its_otime
    #define icmp_rtime    icmp_dun.id_ts.its_rtime
    #edfine icmp_ttime    icmp_dun.id_ts.its_ttime
    #define icmp_ip    icmp_dun.id_ip.idi_ip
    #edfine icmp_radv    icmp_dun.id_radv
    #define icmp_mask    icmp_dun.id_mask
    #define icmp_data    icmp_dun.id_data
};
/****************************
*  计算icmp校验和算法
*  2008-05-02
* **************************/
unsigned short chksum(addr,len)
unsigned short *addr;  /* 校验数据开始地址(注意是以2字节为单位) */
int len;                /* 校验数据的长度大小,以字节为单位 */
{
int sum = 0;        /* 校验和 */
int nleft = len;    /* 未累加的数据长度 */
unsigned short *p;  /* 走动的临时指针,2字节为单位 */
unsigned short tmp = 0; /* 奇数字节长度时用到 */
while( nleft > 1)
{
sum += *p++;    /* 累加 */
nleft -= 2;
}
if(nleft == 1)      /* 奇数字节长度 */
{
*(unsigned char *)&tmp = *(&(unsigned char *)p); /* 将最后字节压如2字节的高位 */
sum += tmp;
}
sum += (sum >> 16) + (sum & 0xffff);    /* 高位低位相加 */
sum += sum >> 16;                       /* 上一步溢出时,将溢出位也加到sum中 */
tmp = ~sum;     /* 注意类型转换,现在的校验和为16位 */
return tmp;
}