--------------------------------------------------
author: Zero1,lingyi.pro#163.com
date: 2008-04-27
--------------------------------------------------
目錄
-------------
1.相關(guān)知識(shí)
2.相關(guān)數(shù)據(jù)結(jié)構(gòu)
3.相關(guān)函數(shù)
4.代碼分析
5.小結(jié)
1.相關(guān)知識(shí)
-----------
ping命令可以查看一個(gè)系統(tǒng)到另一個(gè)系統(tǒng)是否可達(dá),即判斷網(wǎng)絡(luò)連接是否正常。它的工作原理是:向網(wǎng)絡(luò)上的另一臺(tái)主機(jī)發(fā)送ICMP報(bào)文,并等待ICMP回顯應(yīng)答(ECHO_REPLY);如果目標(biāo)系統(tǒng)接收到ICMP報(bào)文,它將返回給發(fā)送者一樣的報(bào)文;同時(shí)ping可以計(jì)算這兩臺(tái)主機(jī)間的往返時(shí)間,以表明兩主機(jī)間的距離。
ps:當(dāng)然現(xiàn)在有些主機(jī)為了隱藏自己,對(duì)于ping發(fā)送來ICMP報(bào)文不返回回顯信息。
想要深入了解ping的工作原理,還得了解ping命令所使用的TCP/IP協(xié)議。
ICMP(Internet Control Message Protocol,網(wǎng)際控制報(bào)文協(xié)議)是為網(wǎng)關(guān)和目標(biāo)主機(jī)提供的一種差錯(cuò)控制機(jī)制,使它們?cè)诎l(fā)生差錯(cuò)是把錯(cuò)誤信息報(bào)告給源發(fā)送方(source sender)。ICMP協(xié)議是IP層的一個(gè)協(xié)議,但是由于差錯(cuò)報(bào)告發(fā)送會(huì)源發(fā)送方的過程中可能要經(jīng)過若干子網(wǎng),因此牽扯到路由選擇問題,所以ICMP報(bào)文通常有IP協(xié)議來發(fā)送。于是ICMP數(shù)據(jù)報(bào)發(fā)送前需要進(jìn)行兩次封裝:首先添加ICMP報(bào)頭形成ICMP報(bào)文,再添加IP報(bào)頭形成IP數(shù)據(jù)報(bào),如下圖所示
----------------
| IP報(bào)頭 |
|---------------------|
| |----------------| |
| | ICMP報(bào)頭 ||
| |-----------------| |
| | |------------| | |
| | | ICMP數(shù)據(jù)報(bào) | | |
| | |------------| | |
| |-----------------| |
|---------------------|
由于IP協(xié)議是一種點(diǎn)對(duì)點(diǎn)的協(xié)議,而不是端對(duì)端的協(xié)議,它提供無連接的數(shù)據(jù)包服務(wù)(通常使用UDP協(xié)議),所以不需要bind()和connect()函數(shù)來綁定和連接端口。用sendto()函數(shù)來發(fā)送數(shù)據(jù)報(bào),接收數(shù)據(jù)使用recvfrom()函數(shù)。(更多信息參看socket編程相關(guān)資料)
2.相關(guān)數(shù)據(jù)結(jié)構(gòu)
----------------
2.1 IP報(bào)頭格式
IP報(bào)頭格式:版本號(hào)(4bit),IP報(bào)頭長度(4),服務(wù)類型(8),數(shù)據(jù)報(bào)長度(16),報(bào)文標(biāo)志ID(16),報(bào)文標(biāo)志F(3),分段偏移量(13),生存時(shí)間(8),協(xié)議號(hào)(8),報(bào)頭校驗(yàn)和(16),源地址(32),目的地址(32),任選項(xiàng)和填充位(若干)。示意圖如下:
--------------------------------------------------------------------------
| 版本號(hào)VER(4) | IP報(bào)頭長度IHL(4) | 服務(wù)類型TOS(8) | 數(shù)據(jù)報(bào)長度TL(16) |
|------------------------------------------------------------------------|
| 報(bào)文標(biāo)志ID(16) | 報(bào)文標(biāo)志F(3) | 分段偏移量 F0(13) |
|------------------------------------------------------------------------|
| 生存時(shí)間(8) | 協(xié)議號(hào)PORT(8) | 報(bào)頭校驗(yàn)和(16) |
|------------------------------------------------------------------------|
| 源地址(32) |
|------------------------------------------------------------------------|
| 目的地址(32) |
|------------------------------------------------------------------------|
| 任選項(xiàng)和填充位(若干) |
--------------------------------------------------------------------------
根據(jù)IP報(bào)頭格式信息,可定義IP報(bào)頭格式數(shù)據(jù)結(jié)構(gòu)如下(更詳細(xì)請(qǐng)閱讀<netinet/ip.h>):
/****************************
* 計(jì)算icmp校驗(yàn)和算法
* 2008-05-02
* **************************/
unsigned short chksum(addr,len)
unsigned short *addr; /* 校驗(yàn)數(shù)據(jù)開始地址(注意是以2字節(jié)為單位) */
int len; /* 校驗(yàn)數(shù)據(jù)的長度大小,以字節(jié)為單位 */
{
int sum = 0; /* 校驗(yàn)和 */
int nleft = len; /* 未累加的數(shù)據(jù)長度 */
unsigned short *p; /* 走動(dòng)的臨時(shí)指針,2字節(jié)為單位 */
unsigned short tmp = 0; /* 奇數(shù)字節(jié)長度時(shí)用到 */
while( nleft > 1)
{
sum += *p++; /* 累加 */
nleft -= 2;
}
if(nleft == 1) /* 奇數(shù)字節(jié)長度 */
{
*(unsigned char *)&tmp = *(&(unsigned char *)p); /* 將最后字節(jié)壓如2字節(jié)的高位 */
sum += tmp;
}
sum += (sum >> 16) + (sum & 0xffff); /* 高位低位相加 */
sum += sum >> 16; /* 上一步溢出時(shí),將溢出位也加到sum中 */
tmp = ~sum; /* 注意類型轉(zhuǎn)換,現(xiàn)在的校驗(yàn)和為16位 */
return tmp;
}
ping程序只用到以下數(shù)據(jù)段:
ip_hl:IP報(bào)頭長度(Internet Header Length),以4字節(jié)為一單位來記錄IP報(bào)頭長度。
ip_ttl:Time to Live,生存期,以秒為單位,指明此IP數(shù)據(jù)包在網(wǎng)絡(luò)上停留的最長時(shí)間,其值由發(fā)送端設(shè)定,每經(jīng)過一個(gè)路由,其值自減1,當(dāng)值為0時(shí),該IP數(shù)據(jù)包將被丟棄。
2.2 ICMP報(bào)頭格式
ICMP分為錯(cuò)誤報(bào)告報(bào)文和查詢報(bào)文兩種。每個(gè)ICMP報(bào)頭均包含類型、編碼和校驗(yàn)和三項(xiàng),長度分別為8位、8位和16位,其他選項(xiàng)隨ICMP功能的不同而不同(詳見《TCP/IP協(xié)議詳解-卷1:協(xié)議》p59)。
ICMP報(bào)頭格式示意圖如下:
----------------------------------------------
| 類型(8) | 代碼(8) | 校驗(yàn)和(16) |
|--------------------------------------------|
| (不同類型代碼有不同的內(nèi)容) |
----------------------------------------------
ping命令中只用到"回顯應(yīng)答"(ICMP_ECHO)和"回顯請(qǐng)求"(ICMP_ECHOREPLY)兩種。Linux下定義為:
#define ICMP_ECHO 0
#define ICMP_ECHOREPLY 8
這兩種ICMP類型的報(bào)頭格式示意圖如下:
--------------------------------------------------------------
| 類型TYPE(0或8) | 編碼CODE(沒有使用) | 校驗(yàn)和SHECKSUM(16) |
|-------------------------------------------------------------|
| 標(biāo)志符Identifier | 序列號(hào)Sequence |
---------------------------------------------------------------
Linux下ICMP數(shù)據(jù)結(jié)構(gòu)的定義如下(詳見<netinet/ip_icmp.h>):
struct icmp
{
u_int8_t icmp_type; /* 消息類型 */
u_int8_t icmp_code; /* 編碼 */
u_int16_t icmp_cksum; /* 校驗(yàn)和 */
union
{
u_char ih_pptr; /* ICMP_PARAMPROB */
struct in_addr ih_gwaddr; /* 網(wǎng)關(guān)地址 */
struct ih_idseq /* 顯示數(shù)據(jù)報(bào) */
{
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;
/* 選項(xiàng)及64位數(shù)據(jù) */
}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
};
/****************************
* 計(jì)算icmp校驗(yàn)和算法
* 2008-05-02
* **************************/
unsigned short chksum(addr,len)
unsigned short *addr; /* 校驗(yàn)數(shù)據(jù)開始地址(注意是以2字節(jié)為單位) */
int len; /* 校驗(yàn)數(shù)據(jù)的長度大小,以字節(jié)為單位 */
{
int sum = 0; /* 校驗(yàn)和 */
int nleft = len; /* 未累加的數(shù)據(jù)長度 */
unsigned short *p; /* 走動(dòng)的臨時(shí)指針,2字節(jié)為單位 */
unsigned short tmp = 0; /* 奇數(shù)字節(jié)長度時(shí)用到 */
while( nleft > 1)
{
sum += *p++; /* 累加 */
nleft -= 2;
}
if(nleft == 1) /* 奇數(shù)字節(jié)長度 */
{
*(unsigned char *)&tmp = *(&(unsigned char *)p); /* 將最后字節(jié)壓如2字節(jié)的高位 */
sum += tmp;
}
sum += (sum >> 16) + (sum & 0xffff); /* 高位低位相加 */
sum += sum >> 16; /* 上一步溢出時(shí),將溢出位也加到sum中 */
tmp = ~sum; /* 注意類型轉(zhuǎn)換,現(xiàn)在的校驗(yàn)和為16位 */
return tmp;
}