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

2008-02-22 12:38:04来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

原理简介:
--------
通过前四节的介绍, 可能大家对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);

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:透析ICMP协议(四): 应用篇ping(RAW Socket)

下一篇:生成树协议(STP)