您的位置:首页 > 编程语言

ping源代码完全解析

2013-03-06 14:15 190 查看
--------------------------------------------------

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报头格式数据结构如下(更详细请阅读<netinet/ip.h>):


/****************************


* 计算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数据结构的定义如下(详见<netinet/ip_icmp.h>):


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


};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐