当前位置:首页 > 技术与方案 > 网络知识相关

应用篇路由追踪

(2012-09-08 20:01:50)

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

| ICMP | TCP(SCTP) |


|... | TCP(SCTP) |

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


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

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

通过利用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; //超时值

 

//直到找到目标主机, 或达到最大跳数(HOPS)

 


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


in.sin_addr.S_un.S_addr = ipback; // 由函数返回的IP地址
// 查找主机名
hHost = gethostbyaddr((char*) sin.sin_addr, 4, PF_INET);
//这里可以输出hHost的内容


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

ing函数的代码

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

SOCKET sockRaw;


int bread,datasize;

char *icmp_data;

const int MAX_PACKET = 1024;

//初始化Socket
ockRaw = WSASocket (AF_INET,
SOCK_RAW,
IPPROTO_ICMP,
NULL, 0, WSA_FLAG_OVERLAPPED);

if (sockRaw == INVALID_SOCKET)


 

// 设置IP包头的ttl字段
read = setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char *) ttl, sizeof(int));

// 设置接受超时为100ms
read = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*) timeout,sizeof(timeout));

//禁止用Nagle算法缓存数据
read = setsockopt(sockRaw, SOL_SOCKET, TCP_NODELAY, (const char*) killnagle, sizeof(int));


// 设置发送超时为100ms
read = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*) timeout,


//下面的代码生成ICMP包


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



if (hp != NULL)


 

//初始化dest


// 设置包长度
datasize = DEF_PACKET_SIZE;

// 计算包大小
datasize += sizeof(IcmpHeader);

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


// 释放内存,退出
 

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


((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();

wrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*) dest, sizeof(dest));

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

 

// 接受数据包
read = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*) from,

//计算总时间
ms = GetTickCount() - tc;

// 得到返回的路由器
ipback = from.sin_addr.s_addr;


函数fill_icmp_data()的源代码

//这个结构下面将用到

BYTE i_type;
BYTE i_code;
USHORT i_cksum;


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

void fill_icmp_data(char * icmp_data, int datasize){

char *datapart;

 

datapart = icmp_data + sizeof(IcmpHeader); //计算数据域的开始地址
// 初试化数据域
memset(datapart,'E', datasize - sizeof(IcmpHeader));

更多
关闭窗口 打印 
网站首页    -    联系我们    -   收藏本站    -    网站地图                                                               客户服务热线:0571-85023000
本网站所有网页信息已申请知识产权和著作权保护,版权归四海光纤公司所有,未经授权禁止任何人复制或镜像,违者必究。
公司主营:杭州光纤光缆视频会议系统,是专业的通信网络工程、视频会议系统建设专家

中华人民共和国备案号:浙ICP备10018243号