查看: 1528|回复: 7
打印 上一主题 下一主题

透析ICMP协议(五): 应用篇路由追踪

[复制链接]

1023

主题

3

听众

359

积分

设计实习生

Rank: 2

纳金币
335582
精华
0

最佳新人

跳转到指定楼层
楼主
发表于 2011-8-31 08:01:09 |只看该作者 |倒序浏览

原理简介:

--------

   通过前四节的介绍, 可能大家对ICMP的应用有了初步的了解. 不过开始本节之前我对ICMP协议再从宏观上做些介绍. 大家都知道ICMP是为于ISO的第三层---网络层。 既是它同IP协议为于同一层, 然而大家可能也只到,ICMP协议要用到IP协议, 所以有一些书上说ICMP位ISO的第四层, 那是错误的。 同样这样那些书上这样画的的例子也是错误的,

我就发现某外资通讯公司的资料上有这样两种错误的画法

   --------------------------        

   | ICMP | TCP(SCTP)  |

   --------------------------

   |         IP                   |

   --------------------------

   

   ---------------------------

   |...        | TCP(SCTP)   |

   ---------------------------

   | ICMP |   IP                |

   ----------------------------
其实如上的画法是错误的, 正确地画法应为:

   ---------------------        

   |...   | TCP(SCTP)   |

   ---------------------

   | ICMP |                 |

   ----------                |

   |       IP                  |

   ---------------------
接下来,让我们来说明怎样实现追踪路由的功能, 大家通过我的第一节的阅读可能已经了解了超时报文的具体内容(参见透析ICMP协议(一):  协议原理), 它在如果网关在处理数据报时发现生存周期域(ttl)为零,此数据报必须抛弃。网关同时必须通过超时信息通知源主机。这是它的报文的具体结构:

    0                   1                   2                   3

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |     Type(11)  |     Code(0/1) |          Checksum             |

   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                             unused                            |

   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |      Internet Header + 64 bits of Original Data Datagram      |

   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
通过利用setsockopt()函数设置ICMP包的IP包头中的ttl字段便可以达到这种效果。 具体过程如下, 假设你的IP到达目标地址需要过n个路由器(n>1)。 则

1. 初始化第一个ICMP包,并设置IP包头中的TTL为1, 则得到第一个数据路由器发回的超时报文

2. 一般情况下:初始化第i(i<n)个ICMP包,并设置IP包头中的TTL为i, 则得到第一个数据路由器发回的超时报文
剩下的问题为如何确定超时ICMP报文的路由器IP地址得到它的机器名的信息。 这个问题可能很多读者都会求, 用gethostbyaddr()可以得到答案。
经过理论的论证后, 让我们看看如何实现。

  
具体实现:(具体如何初试化ICMP的数据包上节已有详细的介绍,这里只是补充路由追踪的代码)

--------

主要代码如下:
unsigned long ipback = 0; //超时报文的IP的初试值

unsigned long ms = 0; //超时值

struct hostent *hHost;

char m_address[256];
//直到找到目标主机, 或达到最大跳数(HOps

while (ipback != ipfinal){  

hHost = 0;


//对到目标主机中间的某个路由器发放ping的报文(ttl为1~N-1之间)

if (Ping(m_address,ttl,ipback,ms))

{

  sin.sin_family = AF_INET;

  sin.sin_addr.S_un.S_addr = ipback; // 由函数返回的IP地址

    // 查找主机名

  hHost = gethostbyaddr((char*)&sin.sin_addr, 4, PF_INET);

  //这里可以输出hHost的内容

}

ttl++;

if (ttl > MAX_HOPS)  //达到最大跳数

{

  break;

}

}
==================

ping函数的代码

==================

int Ping(const char * host, int ttl, unsigned long& ipback, unsigned long& ms)

{

SOCKET sockRaw;

struct sockaddr_in dest,from;

struct hostent * hp;

int bread,datasize;

int fromlen = sizeof(from);

int timeout = 100;

char *dest_ip;

char *icmp_data;

char *recvbuf;

unsigned int addr=0;

const int MAX_PACKET = 1024;
//初始化Socket

sockRaw = WSASocket (AF_INET,

      SOCK_RAW,

      IPPROTO_ICMP,

      NULL, 0, WSA_FLAG_OVERLAPPED);
if (sockRaw == INVALID_SOCKET)

{

  // 错误

}
   // 设置IP包头的ttl字段

bread = setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(int));

if(bread == SOCKET_ERROR)

{

  // 错误

}
// 设置接受超时为100ms

bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));

if(bread == SOCKET_ERROR)

{

  // 错误

}
//禁止用Nagle算法缓存数据

bread = setsockopt(sockRaw, SOL_SOCKET, TCP_NODELAY, (const char*)&killnagle, sizeof(int));

if (bread == SOCKET_ERROR)

{

  // 错误

}
timeout = 1000;

// 设置发送超时为100ms

bread = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,

       sizeof(timeout));

if(bread == SOCKET_ERROR)

{

  // 错误

}
//下面的代码生成ICMP包

memset(&dest,0,sizeof(dest));

hp = gethostbyname(host);

if (!hp)

{

  addr = inet_addr(host);

}

if ((!hp)  && (addr == INADDR_NONE) )

{

  // 错误

}

if (hp != NULL)

  memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);

else

  dest.sin_addr.s_addr = addr;
//初始化dest

if (hp)

  dest.sin_family = hp->h_addrtype;

else

  dest.sin_family = AF_INET;
dest_ip = inet_ntoa(dest.sin_addr);
// 设置包长度

datasize = DEF_PACKET_SIZE;
// 计算包大小

datasize += sizeof(IcmpHeader);
icmp_data = (char *)new[MAX_PACKET]; //分配内存,可以用new 和 delete

recvbuf = (char *)new[MAX_PACKET];
if (!icmp_data)

{

  // 释放内存,退出

}
if (!recvbuf)

{

  // 释放内存,退出 }

}
memset(icmp_data,0,MAX_PACKET);

fill_icmp_data(icmp_data,datasize); // 这个函数用来填充ICMP的数据包
int bwrote;

((IcmpHeader*)icmp_data)->i_cksum = 0;

((IcmpHeader*)icmp_data)->timestamp = GetTickCount(); // 存入当前时间值

((IcmpHeader*)icmp_data)->i_seq = seq_no++;
// 计算校验和

((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize);
  // 为了最后计算ICMP包回来的总时间

unsigned long tc = GetTickCount();

//发送数据包

bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest, sizeof(dest));
if (bwrote == SOCKET_ERROR)

{

  // 错误

}

if (bwrote < datasize ) //发送字节数对否

{

}
// 接受数据包

bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from,

     &fromlen);

//计算总时间

ms = GetTickCount() - tc;
if (bread == SOCKET_ERROR)

{

  // 错误

}
// 得到返回的路由器

ipback = from.sin_addr.s_addr;
return 1;

}
===============================

函数fill_icmp_data()的源代码

===============================

//这个结构下面将用到

typedef struct _ihdr {

  BYTE i_type;

  BYTE i_code;

  USHORT i_cksum;

  USHORT i_id;

  USHORT i_seq;

  ULONG timestamp; /* 这不是ICMP包的一部分, 只是为了计算时间 */

}IcmpHeader;
void fill_icmp_data(char * icmp_data, int datasize){
  IcmpHeader *icmp_hdr;

  char *datapart;
  icmp_hdr = (IcmpHeader*)icmp_data;
  icmp_hdr->i_type = ICMP_ECHO;

  icmp_hdr->i_code = 0;

  icmp_hdr->i_id = (USHORT)GetCurrentProcessId();

  icmp_hdr->i_cksum = 0;

  icmp_hdr->i_seq = 0;

  

  datapart = icmp_data + sizeof(IcmpHeader);  //计算数据域的开始地址

  // 初试化数据域

  memset(datapart,'E', datasize - sizeof(IcmpHeader));
}
分享到: QQ好友和群QQ好友和群 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
转播转播0 分享淘帖0 收藏收藏0 支持支持0 反对反对0
回复

使用道具 举报

Asen    

867

主题

0

听众

1万

积分

外协人员

Rank: 7Rank: 7Rank: 7

纳金币
17488
精华
1
沙发
发表于 2011-8-31 10:01:00 |只看该作者
回复

使用道具 举报

462

主题

1

听众

31万

积分

首席设计师

Rank: 8Rank: 8

纳金币
2
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

板凳
发表于 2012-1-19 23:01:27 |只看该作者
年末感慨实在是多,三言两语道不完!最让我揪心的还是你,行李备好了没?火车票买了没?别感动,我只是问问,自己的事情还是要自己做滴!哈哈。
回复

使用道具 举报

1023

主题

3

听众

359

积分

设计实习生

Rank: 2

纳金币
335582
精华
0

最佳新人

地板
发表于 2012-2-19 23:31:32 |只看该作者
路过、路过、快到鸟,列位请继续...ing
回复

使用道具 举报

5969

主题

1

听众

39万

积分

首席设计师

Rank: 8Rank: 8

纳金币
-1
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

5#
发表于 2012-5-28 23:25:55 |只看该作者
楼主收集的可真全哦
回复

使用道具 举报

1023

主题

3

听众

359

积分

设计实习生

Rank: 2

纳金币
335582
精华
0

最佳新人

6#
发表于 2012-7-21 23:21:44 |只看该作者
不错哦,谢谢楼主
回复

使用道具 举报

5969

主题

1

听众

39万

积分

首席设计师

Rank: 8Rank: 8

纳金币
-1
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

7#
发表于 2012-9-10 23:23:42 |只看该作者
谢谢楼主,真是太实用了
回复

使用道具 举报

1446

主题

3

听众

2万

积分

资深设计师

Rank: 7Rank: 7Rank: 7

纳金币
30927
精华
3

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

8#
发表于 2012-9-14 16:42:12 |只看该作者
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

手机版|纳金网 ( 闽ICP备2021016425号-2/3

GMT+8, 2024-11-12 01:55 , Processed in 0.159691 second(s), 29 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部