admin管理员组

文章数量:1533099

2024年5月20日发(作者:)

1

Traceroute程序设计与实现

学生姓名: 樊冠宇 指导老师:姜文超

摘 要 本文主要讲述了路由追踪的基本程序设计与实现,并给出了一种基

于IP网络的路由追踪命令Tracert,详细分析了实现路由追踪的基本原理,归纳了

路由追踪的基本流程。Tracert通过ICMP协议和IP header中TTL(存活时间)

利用路由器对数据报存活时间的处理方式来实现路由探测的。首先根据任务书设

计好流程图,然后编写程序代码,运行得到Traceroute的命令窗口。提取tracert

的输出,再结合现有IP数据库及自建地名- 坐标数据库对路由中各节点IP进行定

位,最终实现了动态显示追踪的详细信息和路径。

关键词: IP 地址,ICMP协议,TTL,Tracert路由追踪

1 2

1 引 言

Internet,是目前世界上最大的计算机网络,更确切的说是网络中的网络,

它由遍布全球的几万局域网和数百万台计算机组成,并通过用于异构网络的

TCP/IP协议进行网间通信。互联网中,信息的传送是通过网中许多段的传输介

质和设备从一端到达另一端。每一个连接在Internet上的设备,如主机、路由

器、接入服务器等一般情况下都会有一个独立的IP地址。通过Traceroute我们

可以知道信息从你的计算机到互联网另一端的主机是走的什么路劲。当然每次数

据包由某一同样的出发点到达某一同样的目的地走的路劲可能会不同,但基本上

来说大部分时候所走的路由是相同的。随着Internet(国际互联网)的发展,

越来越多的服务通过网络提供给大众,与此同时,针对互联网的攻击事件也越来

越频繁。所谓路由追踪实际上就是在IP网络上判断从源到达目的所经过的路由

器的IP地址,其基本的实现手段都是向目的地发送数据包以获取经过的路由器

的IP。由于Internet上的路由协议是动态的,所以每次形成的数据包从同一个

出发点到达目的地的路由可能会不一样,但由于路由算法有一定的稳定性,在大

部分时侯所走的路由会是相同的。

1.1 课程设计目的

1.这次课程设计,主要为了加深同学们对计算机网络网络的理解和认识

2.了解信息在计算机网络与网络之间的传送和接收

3.进一步加深了解网络与网络之间的协议

4.理解网络中的IP地址以及路由之间的相关命令。

1.2 课程设计内容

1.已知参数:输入:目的节点IP地址或主机名;输出:从控制台屏幕输出IP

报文由本机出发到达目的主机所经过的路由信息。

2.设计要求:通过原始套接字编程,实现Tracert的基本功能

1 3

2.1初始化Windows Sockets网络环境;

2.2解析命令行参数,构造目的端socket地址;

2.3定义IP、ICMP报文;

2.4接收ICMP差错报文并进行解析。

1.3课程设计要求

(1)按要求编写课程设计报告书,能正确阐述设计结果。

(2)通过课程设计培养学生严谨的科学态度,认真的工作作风和团队协作精神。

(3)学会文献检索的基本方法和综合运用文献的能力。

(4)在老师的指导下,要求每个学生独立完成课程设计的全部内容。

2设计原理

raceroute是一个路由跟踪命令,它通过ICMP协议和IP header中TTL

(存活时间)来实现的。 具体而言就是:发送方发出一个TTL是1的IP

Datagram (事实上每个数据包发送三次,大小为40字节,包括本机的IP 地址,

目的主机的IP 地址以及时间戳),当经由第一个路由器时,路由器将该数据包的

TTL减1,发现此时的TTL为0,将数据包丢失,同时向源主机发送一个ICMP

Time-to-Exceed 报文(包括源主机的IP 地址、路由地址以及路由的相关消息),

源主机收到这个数据包后就知道了这个路由器在这条路径上。同理发送第二个、

第三个......第n个。源主机将每次IP数据报的TTL+1,直到某个数据报到

达了目的地址,此时不知发回一个ICMP Time-to-Exceed,

而是发送一个数据报的响应报文。当源主机收到这样一个报文后便知道数据包已

经到达了目的地。Traceroute提取发 ICMP TTL到期消息设备的IP地址并作域

名解析。每次 ,Traceroute都打印出一系列数据,包括所经过的路由设备的域名

及 IP地址,三个包每次来回所花时间。Traceroute 有一个固定的时间等待响应

(ICMP TTL到期消息)。如果这个时间过了,它将打印出一系列的*号表明:在这

个路径上,这个设备不能在给定的时间内发出ICMP TTL到期消息的响应。然后,

1 4

Traceroute给TTL记数器加1,继续进行。

2.1ICMP简介和基本原理

ICMP(Internet Control Message Protocol),即Internet控制报文协议, 它是

TCP/IP协议族的一个子协议,属于网络层面向无连接的协议,主要用于在主机与

路由器之间传递控制信息,包括报告错误、交换受限控制和状态信息等。当遇到

IP数据无法访问目标、IP路由器无法按当前的传输速率转发数据包等情况时,

会自动发送ICMP消息。ICMP报文被包装成IP数据包传到数据链路层进行传输。

通过ICMP协议,主机和路由器可以报告错误并交换相关的状态信息。ICMP对

于TCP/IP协议的可靠运行是至关重要的。 ICMP报文结构如图1所示:

2.2traceRoute程序的基本原理

路由追踪的主要原理是根据路径上各路由器对数据报的存活时(Time to

Live,TTL)做不同的处理,使其产生超时ICMP消息响应,反馈至源主机,从而获

得此跳路由器或主机的IP。照此再发送下一个TTL经过自增的数据报,直至获得

整个路由中各节点的IP或者接收到错误的消息。详细过程描述如下:

1) 置n = 1。假设该过程中共经过M个路由器。

2) 源主机S向目标主机D发送一个TTL为n的UDP数据报。并设定端口号(一

般大于30 000) 。

3) 路由器(或者网关、主机) Rn 对接收到的数据报的TTL 值n做减1处理。

4) 若n = 0,则丢弃UDP数据报,向源主机S发送ICMP超时报文。

5) 若n ≥ 1,继续向目标主机D发送经过处理的数据报。

6) 源主机S分析返回的ICMP报文, 从中提取出发送者Rn的地址IPn并做

记录。

7) 若收到“端口不可达”的ICMP报文,则发送方即目标主机D,记录其地址

1 5

IPn,追踪完成。

8) 置n = n +1,继续向目标主机D发送TTL为n的数据报。

注意,这里使UDP数据报的端口号大于30000,是因为一般的应用程序不可能

使用如此高的端口号。当然这并非绝对,若出现例外,则源主机会发现等待超时,

于是随机改变此UDP数据报的端口号,再次发送。这样最终可以在目标主机上找

到一个空闲的端口号。另外,这里假设路由器和目标主机没有被配置为“过滤

ICMP”或者做了其他的非常规处理,如果被做了类似的配置,则上面的追踪机制就

无能为力了。

2.3traceRoute实现的功能

IP数据报的首部由两部分构成:固定部分和可变部分。固定部分的长度是

20个字段,可变部分由许多选项构成,最长可达40个字节。虽然选项并不是IP

数据报的必需部分,但选项的处理却是IP软件的必需部分。

在现在的TCP/IP协议中,只定义了六种选项,对于我们进行路由追踪技术

有用的是记录路由选项,一个记录路由选项是用来记录处理IP数据报的互联网

路由器的IP地址。因为首部的最大长度是60个字节,它包括20个字节的基本

首部。这就意味着只剩下40个字节留下给选项部分,所以通过选项字段最多能

够记录9个路由器的IP地址。源站在选项中创建一个位标置(placeholder),

用来填入所经过的各路由器,图2给出了记录路由选项的格式。

图2 记录路由选项

向目的主机发送一个ICMP报文,这种方法只要求使用一个套接字。ICMP即

Internet控制报文协议,是一种用于特殊用途的报文机制,可以使互联网中的

路由器或主机报告差错或提供有关意外情况的信息。尽管UDP和ICMP工作在

1 6

TCP/IP的不同层次上,但他们的封装是类似的。ICMP报文为两级封装ICMP报文

放在IP数据报的数据部分,数据报则放在帧的数据中进行网络传输(如图3所

示)ICMP报文与其他普通报文一样,具有相同的路由选择,并没有特殊的优先

权和增加可靠性。通过路由选项的方法记录路由的实现同UDP数据报是相似的,

这里主要说明通过TTL方法的实现。

图3 ICMP报文的两级封装

3设计步骤

分析本次课程设计的任务书,整个课程设计的过程大致可以分为三个步骤:

第一步主要是设计好流程图;第二步是根据流程图编写程序代码;第三步是在程

序编译通过后,运行程序结果,在对话框中输入要追踪的IP地址,观察路由追

踪命令追踪IP地址在网络中的运行。其中第一步跟第二步是关键,只有完整的

流程图和根据要求编写好正确的程序,才能运行得到正确的结果。下面是整个设

计过程中各个步骤的详细分析。

3.1traceroute流程图

根据要求设计好的流程图如图4所示:

1 7

图4 流程图

3.2traceroute的核心程序

整个设计过程的核心程序代码:

#include "stdafx.h"

#include "MyPing.h"

#include "Ping.h"

#include "MyPingDlg.h"

#ifdef _DEBUG

#undef THIS_FILE

static char THIS_FILE[]=__FILE__;

#define new DEBUG_NEW

#endif

void CPing::Ping(int timeout)

{ m_hSocket = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL,

0,WSA_FLAG_OVERLAPPED);

if (m_hSocket == INVALID_SOCKET)

{AfxMessageBox("socket 创建失败!");

return ;}

Start sending/receiving ICMP packets开始发送/接收ICMP的数据包

//static int nCount = 0;静态的nCount = 0。

1 8

int nCount=0;

while(1)

{int bwrote;if (nCount++ == 4)

break;

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

bwrote = sendto(m_hSocket, icmp_data, datasize, 0,

(struct sockaddr*)&m_addrDest, sizeof(m_addrDest));

if (bwrote == SOCKET_ERROR)

{ if (WSAGetLastError() == WSAETIMEDOUT)

{m_dlg->m_result+="Timed out ! rn";

m_dlg->SetDlgItemText(IDC_EDIT2,m_dlg->m_result);

continue; }

AfxMessageBox("发送数据函数调用错误!");

return ;}

3.3traceroute程序运行结果

在运行程序后得到的对话框中输入 ,再单击

路由跟踪,就可以详细的观察到路由整个的跟踪过程,如图5所示:

图5路由跟踪示意图

1 9

4总结

在整个课程设计过程中,首先得仔细分析课程设计任务书,根据要求编写好

程序代码,然后运行程序,分析得到的结果。在编写代码过程中,遇到许多问题。

开始看到题目不知道该如何下手去做。编写好的代码,在VC++平台上运行时,

总是出现错误,最终在老师的帮助下,成功的解决了该问题。还有在宿舍

运行

tracert后一闪就没了,

也不知道是什么原因,上网查找了解到,必须得先运行CMD,

然后在CMD里运行其他命令。成功的运行tracert后,出现的一连串数字,不知

道是什么意思,结合老师上课所讲的东西并仔细分析才知道,192.168.20.45是

IP地址,0ms是跳到下个IP地址所用的时间。从整体来说这次课程设计是成功

的。但中间存在一些细节问题,程序代码过于复杂,没有很好的用语句解释出程

序中的代码。每次课程设计都让我们学到了很多书本上学不到的东西,如严谨的

做事风格,认真学习的态度,不懂要问的道理。因此,我们应该要认认真真的做

好每一次的课程设计。

5结束语

通过这次课程设计,加强了我们动手、思考和解决问题的能力,让我们对网

络和网络之间的信息和数据的传送以及相关的协议有了更深刻的理解。在这次课

程设计中,tracerroute通过ICMP协议和IP header中TTL(存活时间)

利用路由器对数据报存活时间的处理方式成功的实现路由探测。跟踪到网络中数

据IP地址的移动,但在跟踪过程中经常发生数据的丢失和网络超时。

我认为做课程设计同时也是对课本知识的巩固和加强,由于课本的知识太

多,平时课间的学习并不能很好的理解和运用,而且考试内容有限,所以在这次

课程设计过程中,我们了解了很多网络之间的协议和路由跟踪的命令,并对抽象

的网络有了更多的认识。然而,认识来源于实践,实践是认识的动力和最终目的,

1 10

实践是检验真理的唯一标准,所以这次课程设计对我们来说是受益匪浅。对我们

而言,知识上的收获重要,精神上的丰收更是可喜。挫折是一份财富,经历是一

份拥有,通过这次课程设计让我懂得了理论与实践相结合的重要性。只有理论知

识是远远不够的,必须把平时所学的理论知识与实践结合起来,从理论中得出结

论,才能真正的利用所学的东西为社会服务,从而提高自己的实际动手能力和独

立思考的能力。在设计的过程中遇到这样那样的难题,但是,在老师和同学们的

帮助下,都顺利的解决了,同时在设计的过程中发现了自己的不足之处,对以前

所学过的知识理解的不够深刻,掌握的不够牢固。

这次课程设计,让我学到了很多课内学不到的东西,比如团体合作,出现差

错的随机应变等,那都是受益匪浅。在此,感谢我们指导老师的细心指导,今后,

我会加倍的努力学习。

1 11

参考文献

[1]W Richard Stevens. TCP/ IP Illust rated ,Volum 1 :The Protocols[M]1

北京:机械工业出版社,2002.85296.

[2]W Richard Stevens. UNIX Network ProgrammingVolum1Networking

APIs :Socket s and XTI ( Second

Edition) (影印版) [M] . 北京: 清华大学出版社,2002.

[3][日]井口信和. TCP/ IP 网络工具篇[M] . 吴松芝,董江洪译. 北京:科学出

版社,2003. 1582162.

[4]余青霓,王晓程,周钢,等. 网络入侵检测分析员手册[Z].北京:人民邮电出版

社,2000. 1012109.

[5]JAMES , KEITH Computer Networking A Top2Down Approach

Featuring the Internet (影印版) [M].北京: 高等教育出版社,2001.

[6]李为民,赵迎新,梁济仁,等. 网络连通性测试与故障定位[J ].计算机应

用,2004 ,24(增刊):47249.

[7]张保通.基于路由器的防火墙设计[J ].华北航天工业学院学

报,2002,(2):16219.

附加程序清单:

莫小锋 通信0802班 学号:2

1 12

源程序:

#include "stdafx.h"

#include "TraceRoute.h"

#include "Tracer.h"

#include "TraceRouteDlg.h"

#ifdef _DEBUG

#undef THIS_FILE

static char THIS_FILE[]=__FILE__;

#define new DEBUG_NEW

#endif

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

// Construction/Destruction

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

//IP

struct IPHEADER

{ unsigned int h_len:4; // 首部长度

unsigned int version:4; // 版本

unsigned char tos; // 服务类型

unsigned short total_len; // 报文总长度

unsigned short ident; // 标识

unsigned short frag_and_flags; // 偏移量

unsigned char ttl; // 寿命

unsigned char proto; // 协议

unsigned short checksum; // 首部校验和

unsigned int sourceIP; // 源站IP

unsigned int destIP; // 目的站IP

};

//ICMP首部数据结构

struct ICMPHEADER

{

BYTE i_type; // 类型

BYTE i_code; // 代码

USHORT i_cksum; // 首部校验和

USHORT i_id; // 标识

USHORT i_seq; // 序列号

ULONG timestamp; // 时间戳(选用)

};

CTracer::CTracer()

{

m_nSeq=1;

1 13

icmpData=NULL;

icmpRcvBuf=NULL;

m_hSocket=INVALID_SOCKET;

//初始化socket

WSADATA wsaData;

if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)

{

AfxMessageBox("WSAStartup()出错!");

}

}

CTracer::~CTracer()

{

//关闭Socket

if (m_hSocket!=NULL)

closesocket(m_hSocket);

WSACleanup();

}

//CheckSum

USHORT CTracer::CheckSum(char* pBuffer,int size)

{

USHORT* buffer=(USHORT*)pBuffer;

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

}

//FillAddress

BOOL CTracer::FillAddress(char *addrDest)

{

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

m__family =AF_INET;

if(inet_addr(addrDest)==INADDR_NONE)

1 14

{

//输入的地址为计算机名字

HOSTENT* hp=NULL;

hp=gethostbyname(addrDest);

if(hp)

{

memcpy(&(m__addr),hp->h_addr,hp->h_length);

m__family =hp->h_addrtype ;

}

else

{

AfxMessageBox("获取地址失败!");

return FALSE;

}

}

else

{

m__addr.s_addr=inet_addr(addrDest);

}

return TRUE;

}

//FillICMPData

void CTracer::FillICMPData(char* icmpData,int size)

{

memset(icmpData,0,size);

ICMPHEADER* icmpHeader=NULL;

icmpHeader=(ICMPHEADER*)icmpData;

icmpHeader->i_type =ICMP_ECHO;

icmpHeader->i_code =0;

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

icmpHeader->i_seq =m_nSeq++;

//GetTickCount返回从0点到现在的毫秒数,作时间戳

icmpHeader->timestamp=GetTickCount();

char* datapart=icmpData+sizeof(ICMPHEADER);

memset(datapart,'*',size-sizeof(ICMPHEADER));

//填充校验和

icmpHeader->i_cksum =CheckSum(icmpData,size);

}

//设置数据报的寿命

BOOL CTracer::SetTTL(SOCKET hSocket, int ttl)

{

1 15

int result;

result=setsockopt(hSocket,IPPROTO_IP,IP_TTL,(LPSTR)&ttl,sizeof(ttl));

if(result==SOCKET_ERROR)

{

AfxMessageBox("设置数据报寿命失败!");

TerminateProcess(GetCurrentProcess(),-1);

}

return TRUE;

}

//发送数据报

BOOL CTracer::SendData(char* icmpData,int size)

{

//填充ICMP报头

FillICMPData(icmpData,size);

//发送数据报

int result;

time1=GetTickCount();

result=sendto(m_hSocket,icmpData,size,0,(SOCKADDR*)&m_addrDest,sizeof(

m_addrDest));

if(result==SOCKET_ERROR)

{

if(WSAGetLastError()==WSAETIMEDOUT)

{

((CTraceRouteDlg*)m_pWnd)->InfoAdd("发送超时");

return TRUE;

}

AfxMessageBox("发送报文失败!");

TerminateProcess(GetCurrentProcess(),-1);

}

return FALSE;

}

//接收数据报

BOOL CTracer::RecvData(char* icmpRcvBuf,int* presult)

{

static int count=0;

//总共6次出现接收超时,判断存在连接问题。

if(count>5)

{

AfxMessageBox("连接存在问题!");

TerminateProcess(GetCurrentProcess(),-1);

}

1 16

int fromlen=sizeof(SOCKADDR);

*presult=SOCKET_ERROR;

*presult=recvfrom(m_hSocket,icmpRcvBuf,MAX_PACKET,0,(SOCKADDR*)

&m_addrFrom,&fromlen);

time2=GetTickCount();

if(*presult==SOCKET_ERROR)

{

if(WSAGetLastError()==WSAETIMEDOUT)

{

((CTraceRouteDlg*)m_pWnd)->InfoAdd ("接收超时!");

count++;

return TRUE;

}

AfxMessageBox("接收数据报失败!");

TerminateProcess(GetCurrentProcess(),-1);

}

return FALSE;

}

//处理接收到的数据报

BOOL CTracer::DecodeICMP(char* pBuffer,int bytes,int ttl)

{

IPHEADER *ipHeader=NULL;

ICMPHEADER *icmpHeader=NULL;

unsigned short ipHeaderLen;

HOSTENT *ph=NULL;

in_addr inaddr=m__addr;

ipHeader=(IPHEADER*)pBuffer;

ipHeaderLen=20;

if (bytes

AfxMessageBox("接收数据报长度不正确!");

icmpHeader=(ICMPHEADER*)(pBuffer+20);

switch (icmpHeader->i_type)

{

//目的站点的返回

case ICMP_ECHOREPLY:

ph=gethostbyaddr((const char *)&inaddr,AF_INET, sizeof(in_addr));

if (ph != NULL)

{

CString report;

1 17

("%2d %s (%s)",ttl,ph->h_name,inet_ntoa(inaddr));

((CTraceRouteDlg*)m_pWnd)->InfoAdd(report);

}

return TRUE;

break;

//中途路由器的返回

case ICMP_TIMEOUT:

{

CString report;

("%2d %s %2d ms",ttl,

inet_ntoa(inaddr),(time2-time1));

((CTraceRouteDlg*)m_pWnd)->InfoAdd(report);

return FALSE;

break;

}

//错误:主机不可达

case ICMP_DESTUNREACH:

{

CString report;

("%2d %s 主机不可达",ttl,inet_ntoa(inaddr));

((CTraceRouteDlg*)m_pWnd)->InfoAdd(report);

return TRUE;

break;

}

//收到一个不是回应的报文

default:

{

CString report;

("非回应报文");

((CTraceRouteDlg*)m_pWnd)->InfoAdd(report);

return TRUE;

}

}

return FALSE;

}

void CTracer::SetWnd(CDialog *pWnd)

{

//设置窗口指针

m_pWnd=pWnd;

}

1 18

void CTracer::Trace(char *destAddress)

{

int size=DEF_PACKET_SIZE+sizeof(ICMPHEADER);

//转换地址

if (!FillAddress(destAddress)) return ;

//分配必要的内存空间

icmpData=(char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PA

CKET);

icmpRcvBuf=(char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_

PACKET);

if(!icmpData||!icmpRcvBuf)

{

AfxMessageBox("分配内存空间失败!");

TerminateProcess(GetCurrentProcess(),-1);

}

memset(icmpData,0,MAX_PACKET);

memset(icmpRcvBuf,0,MAX_PACKET);

//初始化套接字

m_hSocket=WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_F

LAG_OVERLAPPED);

if(m_hSocket==INVALID_SOCKET)

{

AfxMessageBox("套接字初始化失败!");

TerminateProcess(GetCurrentProcess(),-1);

}

//设置超时选项

int nTimeOut=1000;

int result;

result=setsockopt(m_hSocket,SOL_SOCKET,SO_RCVTIMEO,(char*)&nTimeOut,si

zeof(nTimeOut));

if(result==SOCKET_ERROR)

{

AfxMessageBox("设置接收超时选项失败!");

TerminateProcess(GetCurrentProcess(),-1);

}

1 19

result=setsockopt(m_hSocket,SOL_SOCKET,SO_SNDTIMEO,(char*)&nTimeOut,si

zeof(nTimeOut));

if(result==SOCKET_ERROR)

{

AfxMessageBox("设置发送超时选项失败!");

TerminateProcess(GetCurrentProcess(),-1);

}

//设置路由不查询路由表选项

BOOL bDontRoute=TRUE;

result=setsockopt(m_hSocket,SOL_SOCKET,SO_DONTROUTE,(char*)&bDontRou

te,sizeof(BOOL));

if(result==SOCKET_ERROR)

{

AfxMessageBox("设置不查询路由表选项失败!");

TerminateProcess(GetCurrentProcess(),-1);

}

for(int ttl=1;ttl

{

//设定数据报的寿命

SetTTL(m_hSocket,ttl);

//发送数据报

if(SendData(icmpData,size)) continue;

//接收数据报

if(RecvData(icmpRcvBuf,&result)) continue;

//处理接收到的数据报

if (DecodeICMP(icmpRcvBuf,result,ttl)) break;

}

HeapFree(GetProcessHeap(), 0, icmpData);

HeapFree(GetProcessHeap(), 0, icmpRcvBuf);

}

本文标签: 路由数据网络报文