分享
 
 
 

使用ICMP实现路由跟踪

王朝vc·作者佚名  2006-01-17
窄屏简体版  字體: |||超大  

使用ICMP实现路由跟踪

作者:高超航

下载本文示例源代码

代码运行效果图如下:

作者简介:本人是成都理工大学大四学生,学习计算机通信专业,对网络及编程有着非常浓厚的兴趣,希望能与大家共同探讨。

[摘要] 本文简单介绍了ICMP协议和一种利用ICMP在VC++下实现网络路由跟踪的方法,并给出一了个详细的例子。

[关键字] ICMP 路由跟踪 Visual C++6.0

一、概述

计算机在Internet中传递信息时,必须要经过路由器进行网络路由才能找到目的主机,把信息送到目的主机。路由器中都有一张路由表,表中保存了从本路由器到某一主机的路由信息,路由器就是通过该路由表进行网络寻径的。两台主机之间并没有一条固定的路径(即路由表并不固定),该路径随着网络的变动而作相应的变动,因而我们并不能直接从某一主机上得到去往另一主机的路径,要得到本机与网络上某台主机的网络路径就必须要进行路由跟踪。本文将介绍一种实现路由跟踪的方法。

二、ICMP简介

ICMP即Internet控制报文协议是一种用于特殊用途的报文机制,可以使互联网中的路由器或主机报告差错或提供有关意外情况的信息。

ICMP报文为两级封装,ICMP报文放在IP数据报的数据部分,IP数据报则放在帧的数据中进行网络传输(如下图1所示)。ICMP报文与其他普通报文一样,具有相同的路由选择,并没有特殊的优先权和增加可靠性。

(图1)ICMP报文的封装

在ICMP包头中包含了三个字段:1字节类型域、1字节代码域、2字节校验和。类型域表示了该报文的类型,如:回应请求报文,数据报超时报文等,代码域表示了该类型的几种不同情况,如:当类型为11(超时报文)时,代码为0表示TTL超时,为1表示片重组超时。在实现本文中所述的功能时要发送回应请求报文(类型为8),过程如下:源主机向目的主机发送一个类型为8的回应请求报文,若目的站点收到回应请求报文则把报文IP包头部中的目的IP与源IP地址交换,将类型8改为回应类型0,计算出新的校验和再发往源主机。若源主机收到了该回应报文,则不但说明了目的主机可达,而且说明目的主机与源主机之间的路由器工作正常,源主机和目的主机的IP、ICMP软件运行正常。但若在传输过程中了出现了某些问题,如网络不通等,导致数据被定向到一个无效的目的地,这时相关路由器或目的主机将发回目的不可达报文(类型为3),并在代码中说明该报文的具体情况:是网络不可达还是主机不可达等。若请求报文在传输过程中超时,即TTL被减为0(报文每经过一个路由器TTL都要减1),则该路由器返回一个TTL超时报文(类型为11),报文IP头中源IP地址即为本路由器的IP地址。

三、路由跟踪的实现方法

路由跟踪的实现就是巧妙地利用了ICMP报文的TTL超时报文。其实现过程如下:源主机先向目的主机发送一个回应请求报文(类型8),TTL值设为1,第一个路由器收到后将TTL减1,这样TTL变为0,分组被废除,同时路由器向源主机发送一个TTL超时报文(类型为11),报文的IP包头中的源IP地址就是第一个路由器的地址,源主机就可以通过对该报文进行分析,得到第一个路由器的地址。接着发送TTL等于2的报文得到第二个路由器地址,再发TTL等于3的报文,如此下去直到收到目的主机的回应应答报文(类型为0)或目的不可达报文(类型为3),或者到了最大跳数(要检测路由器个数的最大值)。可以看到,对TTL的设置是实现跟踪的关键,使用函数setsockopt(m_Sock,

IPPROTO_IP, IP_TTL,(LPSTR)&TTL,sizeof(int)) 可以对其进行设置,m_Sock是所创建的套接字,IP_TTL说明是进行TTL设置,TTL即是要设置的TTL值,为一个整形数值

。其实现流程可用下图2表示:

(图2) 流程图

四、程序实现

本文所介绍的程序是使用了Visual C++6.0编写,其过程如下:

1、创建一个新的基于对话框的AppWizard工程,并命名为RouteTrace。

2、在stdafx.h中加入#include "winsock2.h"。

3、打开选择菜单Project->Setting (ALT+F7),进入Project Setting 对话框,在Link下的 Object/library

modules 输入ws2_32.lib,然后点OK。

4、自定义一个ICMP类。点击菜单中的Insert->New Class,进入New Class对话框,在Class type中选择Generic

Class,在Name中输入类名CICMP,然后点OK,这样就新建了一个CICMP类。

5、将对话框设置成如图3所示的样子:

(图3 程序界面)

启动Class Wizard 为各控件添加响应函数和关联变量,控件对应的ID及响应函数或变量为:

控件

ID

响应函数

变量

地址组合框

IDC_COMBO

CComboBox m_comb

最大跳数编辑框

IDC_MAXHOT

int m_maxhot

跟踪按钮

IDC_TRACE

OnTrace()

停止按钮

IDC_STOP

OnStop()

列表框

IDC_LIST

CListCtrl m_list

五、添加代码

在完成了对各控件的设置和类的添加以后就是对代码的编写了,这里给出了新建类CICMP和RouteTraceDlg.cpp的代码,详细代码请参看源程序。

ICMP.cpp文件代码:

// ICMP.cpp: implementation of the CICMP class.

//

//////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "RouteTrace.h"

#include "ICMP.h"

#include "ws2tcpip.h" //实现 IP_TTL 设置的关键

#ifdef _DEBUG

#undef THIS_FILE

static char THIS_FILE[]=__FILE__;

#define new DEBUG_NEW

#endif

//////////////////////////////////////////////////////////////////////

// Construction/Destruction

//////////////////////////////////////////////////////////////////////

CICMP::CICMP()

{

winsock = 0;

m_pIp = NULL;

m_pIcmp = NULL;

m_pIp = (IP_HEAD *)new BYTE[MAX_PACKET];

m_pIcmp = (ICMP_HEAD *)new BYTE[MAX_PACKET];

}

CICMP::~CICMP()

{

delete [] m_pIp;

delete [] m_pIcmp;

}

BOOL CICMP::Initialize()

{

WSADATA wsadata;

if( WSAStartup(MAKEWORD(2, 1),&wsadata) )

{

AfxMessageBox("WSAStartup初始化失败!");

return FALSE;

}

winsock= WSASocket (AF_INET, //建立socket

SOCK_RAW,

IPPROTO_ICMP,

NULL, 0,0);

if(!winsock){

AfxMessageBox( "Socket创建失败!");

return FALSE;

}

int timeout =5000;

setsockopt(winsock,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout, //设置接收超时

sizeof(timeout));

timeout = 5000;

setsockopt(winsock,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout, //设置发送超时

sizeof(timeout));

return TRUE;

}

void CICMP::Uninitialize() //释放Socket

{

if(winsock)

closesocket(winsock);

WSACleanup();

}

USHORT CICMP::CheckSum(USHORT *buffer, int size) //计算校验和

{

unsigned long cksum = 0;

while(size 1) {

cksum+=*buffer++;

size -=sizeof(USHORT);

}

if(size ) {

cksum += *(UCHAR*)buffer;

}

cksum = (cksum 16) + (cksum & 0xffff);

cksum += (cksum 16);

return (USHORT)(~cksum);

}

//--------------------发送ICMP回应请求报文-------------------

BOOL CICMP::SendICMPPack(char *pAddr)

{

sockaddr_in sockAddr;

memset((void *)&sockAddr,0,sizeof(sockAddr));

sockAddr.sin_family = AF_INET;

sockAddr.sin_port = 0;

sockAddr.sin_addr.S_un.S_addr=inet_addr(pAddr);

return SendICMPPack(&sockAddr);

}

BOOL CICMP::SendICMPPack(sockaddr_in *pAddr)

{

//填充ICMP数据各项

int state;

char *p_data;

m_pIcmp-type = ICMP_ECHO;

m_pIcmp-code = 0;

m_pIcmp-ID = (USHORT)GetCurrentProcessId();

m_pIcmp-number = 0;

m_pIcmp-time = GetTickCount();

m_pIcmp-cksum = 0;

//填充数据

p_data = ((char *)m_pIcmp + sizeof(ICMP_HEAD));

memset((char *)p_data,''0'',DEF_PACKET);

//校验和

m_pIcmp-cksum = CheckSum((USHORT *)m_pIcmp,

DEF_PACKET+sizeof(ICMP_HEAD));

//发送数据

state = sendto(winsock,(char *)m_pIcmp,

DEF_PACKET+sizeof(ICMP_HEAD),

NULL,(struct sockaddr *)pAddr,sizeof(sockaddr));

if(state == SOCKET_ERROR) {

if(GetLastError()==WSAETIMEDOUT)

m_strInfo = "连接超时!(发送)";

else

m_strInfo.Format("出现未知发送错误!");

return FALSE;

}

if(state <DEF_PACKET) {

m_strInfo = "发送数据错误!";

return FALSE;

}

memcpy((void *)&m_sockAddr,(void *)pAddr,

sizeof(sockaddr_in));

return TRUE;

}

//----------------------接收数据----------------------------

BOOL CICMP::RecvICMPPack()

{

int state;

int len = sizeof(sockaddr_in);

char * addr;

struct hostent *lpHostent = NULL;

addr = inet_ntoa(m_sockAddr.sin_addr);

state = recvfrom(winsock,(char *)m_pIp,MAX_PACKET,0,

(struct sockaddr*)&m_sockAddr,&len);

if (state == SOCKET_ERROR) {

if (WSAGetLastError() == WSAETIMEDOUT)

{m_strInfo="接收超时,路由跟踪失败!";

routestate=0;

RouteState="路由跟踪失败!";

}

else

m_strInfo = "未知接收错误!";

return FALSE;

}

//分析数据

int ipheadlen;

ipheadlen = m_pIp-HeadLen * 4 ;

if (state < (ipheadlen+MIN_PACKET)){

m_strInfo = "目的地址的响应数据不正确";

return FALSE;

}

ICMP_HEAD * p_icmprev;

p_icmprev = (ICMP_HEAD*)((char *)m_pIp + ipheadlen);

switch (p_icmprev-type)

{

case ICMP_ECHOREPLY: //收到正常回显

{

m_strInfo.Format("接收到%s %d字节响应数据,响应时间:%dms.",

inet_ntoa(m_sockAddr.sin_addr),len,GetTickCount()-p_icmprev-time);

routeaddr=addr;

routestate=0;

RouteState="到达目的主机!";

return TRUE;

break;

}

case ICMP_TTLOUT: // TTL超时

{ routeaddr=inet_ntoa(m_sockAddr.sin_addr);

routestate=1;

RouteState="测试到路由器!";

return TRUE;

break;

}

case ICMP_DESUNREACH: //目的不可达

{m_strInfo = "目的不可达!";

routestate=0;

RouteState="目的不可达!";

return TRUE;

break;

}

default :{ routestate=0;

m_strInfo="未知错误!";

RouteState="不明状态!";

}

}

return TRUE;

}

//----------------设置TTL--------------------

int CICMP::SetTTL(int TTL)

{

int nRet=setsockopt(winsock, IPPROTO_IP, IP_TTL,(LPSTR)&TTL,sizeof(int));

if(nRet==SOCKET_ERROR)

{ CString ttlerr;

ttlerr.Format("设置 TTL 错误!");

AfxMessageBox(ttlerr);

return 0;

}

return 1;

}

RouteTraceDlg.cpp文件代码:// RouteTraceDlg.cpp : implementation file

//

#include "stdafx.h"

#include "RouteTrace.h"

#include "RouteTraceDlg.h"

#include "afxmt.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/////////////////////////////////////////////////////////////////////////////

// CAboutDlg dialog used for App About

struct SubThreadInfo

{

CDialog* pDialog;

CListCtrl*list;

CStatic* state;

CString IPStr;

int Maxhot;

} Info;

CEvent eventStopRoute;

//-----------------路由跟踪线程---------------

UINT ThreadRoute(LPVOID pParam)

{

SubThreadInfo* pInfo = (SubThreadInfo*)pParam;

CRouteTraceDlg* pThreadDlg = (CRouteTraceDlg*)pInfo-pDialog;

CICMP m_icmp;

CString IPStr=pInfo-IPStr;

CString sTTL;

int nTtl;

m_icmp.Initialize();

for(nTtl=1;nTtl<=pInfo-Maxhot;nTtl++)

{

if(m_icmp.SetTTL(nTtl)==0)

return 0;

sTTL.Format("%d",nTtl);

if(m_icmp.SendICMPPack((char *)(LPCSTR)IPStr))

m_icmp.RecvICMPPack();

{

int i=pInfo-list-InsertItem(0,sTTL);

pInfo-list-SetItemText(i,1,m_icmp.routeaddr);

pInfo-list-SetItemText(i,2,m_icmp.RouteState);

pInfo-state-SetWindowText(m_icmp.m_strInfo);

Sleep(100);

}

if(m_icmp.routestate==0) //收到非TTL超时报文则跳出循环

break;

if(WaitForSingleObject(eventStopRoute.m_hObject, 0) == WAIT_OBJECT_0)

break; //收到停止信号则跳出循环

}

pThreadDlg-Routeflag=TRUE;

return 0;

}

…… //系统代码

BOOL CRouteTraceDlg::OnInitDialog()

{

CDialog::OnInitDialog();

…… //系统代码

// TODO: Add extra initialization here

m_list.InsertColumn(0,"标号",LVCFMT_CENTER,60,0);

m_list.InsertColumn(1,"路由器地址",HDF_CENTER,200,0);

m_list.InsertColumn(2,"状态",HDF_CENTER,100,0);

ListView_SetExtendedListViewStyleEx(m_list.m_hWnd, LVS_EX_FULLROWSELECT, 0xFFFFFFFF);

return TRUE; // return TRUE unless you set the focus to a control

}

…… //系统代码

void CRouteTraceDlg::OnTrace()

{

CString str;

UpdateData(TRUE);

CWnd * pWnd;

pWnd = GetDlgItem(IDC_COMBO);

pWnd-GetWindowText(str);

if(str.IsEmpty()) {

MessageBox("请输入地址!");

pWnd-SetFocus();

return;

}

if (m_comb.FindStringExact(-1, str) == CB_ERR)

m_comb.AddString(str);

m_list.DeleteAllItems();

if(Routeflag)

{

Routeflag=FALSE;

Info.IPStr=str;

Info.pDialog=this;

Info.Maxhot=m_maxhot;

Info.list=(&m_list);

Info.state=(&m_statectl);

AfxBeginThread(ThreadRoute, &Info); //在线程中实现路由跟踪

}

}

void CRouteTraceDlg::OnStop()

{

if(!Routeflag)

{

eventStopRoute.SetEvent(); // 发出停止信号

}

}

void CRouteTraceDlg::OnDestroy()

{

CDialog::OnDestroy();

// TODO: Add your message handler code here

m_icmp.Uninitialize();

}

六、结束语

本文所述程序在 Windows 98 下 Visual C++6.0 中调试通过,并在宽带网中测试成功。读者可根据实际需要修改程序中的参数,以实现更强大的功能,比如可以把超时值设置成更合适的值,或设置成可动态输入的形式,示例程序请看本文所附源码,也可以本人主页http://joysea.126.com中下载。

七、参考文献

1. 胡晓军、邓波、高宏伟 编著 Visual C++高级开发范例解析 2002.1 电子工业出版社

2. 周明天、汪文勇 编著 TCP/IP网络原理与技术 1993.12 清华大学出版社

作者信息:

作者:高超航 电话:028-86275288-230

E-mail:feibao@263.sina.com

http://joysea.126.com

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有