资源描述
通信网络设计课程设计 S15 第 16 页 共 16 页
到达远程主机的路由探测程序的设计
摘 要 :本文主要讲述了路由追踪的基本程序设计与实现,并给出了一种基于IP网络的路由追踪命令Tracert,详细分析了实现路由追踪的基本原理,归纳了路由追踪的基本流程。Tracert通过ICMP协议和IP header中TTL(存活时间)利用路由器对数据报存活时间的处理方式来实现路由探测的。首先根据任务书设计好流程图,然后编写程序代码,运行得到Traceroute的命令窗口。提取tracert的输出,再结合现有IP数据库及自建地名- 坐标数据库对路由中各节点IP进行定位,最终实现了动态显示追踪的详细信息和路径。
关键词: IP 地址,ICMP协议,TTL,Tracert路由追踪
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的基本功能
2.1初始化Windows Sockets网络环境;
2.2解析命令行参数,构造目的端socket地址;
2.3定义IP、ICMP报文;
2.4接收ICMP差错报文并进行解析。
1.3课程设计要求
能探测到到达远程主机的路由;
能将探测到的路由信息显示到屏幕窗口内。
2设计原理
traceroute是一个路由跟踪命令,它通过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到期消息的响应。然后,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,记录其地址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工作在TCP/IP的不同层次上,但他们的封装是类似的。ICMP报文为两级封装ICMP报文放在IP数据报的数据部分,数据报则放在帧的数据中进行网络传输(如图3所示)ICMP报文与其他普通报文一样,具有相同的路由选择,并没有特殊的优先权和增加可靠性。通过路由选项的方法记录路由的实现同UDP数据报是相似的,这里主要说明通过TTL方法的实现。
图3 ICMP报文的两级封装
3设计步骤
分析本次课程设计的任务书,整个课程设计的过程大致可以分为三个步骤:第一步主要是设计好流程图;第二步是根据流程图编写程序代码;第三步是在程序编译通过后,运行程序结果,在对话框中输入要追踪的IP地址,观察路由追踪命令追踪IP地址在网络中的运行。其中第一步跟第二步是关键,只有完整的流程图和根据要求编写好正确的程序,才能运行得到正确的结果。下面是整个设计过程中各个步骤的详细分析。
3.1traceroute流程图
根据要求设计好的流程图如图4所示:
3.2traceroute的核心程序
//计算网际校验和函数
USHORT checksum( USHORT *pBuf, int iSize )
//对数据包进行解码
BOOL DecodeIcmpResponse(char * pBuf, int iPacketSize,
DECODE_RESULT &DecodeResult,
BYTE ICMP_ECHO_REPLY, BYTE ICMP_TIMEOUT)
//主函数
void main()
3.3traceroute程序运行结果
4总结
在整个课程设计过程中,首先得仔细分析课程设计任务书,根据要求编写好程序代码,然后运行程序,分析得到的结果。在编写代码过程中,遇到许多问题。开始看到题目不知道该如何下手去做。编写好的代码,在VC++平台上运行时,总是出现错误,最终在老师的帮助下,成功的解决了该问题。还有在宿舍运行tracert后一闪就没了,也不知道是什么原因,上网查找了解到,必须得先运行CMD,然后在CMD里运行其他命令。成功的运行tracert后,出现的一连串数字,不知道是什么意思,结合老师上课所讲的东西并仔细分析才知道,192.168.20.45是IP地址,0ms是跳到下个IP地址所用的时间。从整体来说这次课程设计是成功的。但中间存在一些细节问题,程序代码过于复杂,没有很好的用语句解释出程序中的代码。每次课程设计都让我们学到了很多书本上学不到的东西,如严谨的做事风格,认真学习的态度,不懂要问的道理。因此,我们应该要认认真真的做好每一次的课程设计。
通过这次课程设计,加强了我们动手、思考和解决问题的能力,让我们对网络和网络之间的信息和数据的传送以及相关的协议有了更深刻的理解。在这次课程设计中,tracerroute通过ICMP协议和IP header中TTL(存活时间)利用路由器对数据报存活时间的处理方式成功的实现路由探测。跟踪到网络中数据IP地址的移动,但在跟踪过程中经常发生数据的丢失和网络超时。
我认为做课程设计同时也是对课本知识的巩固和加强,由于课本的知识太多,平时课间的学习并不能很好的理解和运用,而且考试内容有限,所以在这次课程设计过程中,我们了解了很多网络之间的协议和路由跟踪的命令,并对抽象的网络有了更多的认识。然而,认识来源于实践,实践是认识的动力和最终目的,实践是检验真理的唯一标准,所以这次课程设计对我们来说是受益匪浅。对我们而言,知识上的收获重要,精神上的丰收更是可喜。挫折是一份财富,经历是一份拥有,通过这次课程设计让我懂得了理论与实践相结合的重要性。只有理论知识是远远不够的,必须把平时所学的理论知识与实践结合起来,从理论中得出结论,才能真正的利用所学的东西为社会服务,从而提高自己的实际动手能力和独立思考的能力。
这次课程设计,让我学到了很多课内学不到的东西,比如团体合作,出现差错的随机应变等,那都是受益匪浅。在此,感谢我们指导老师的细心指导,今后,我会加倍的努力学习。
源程序:
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
using namespace std;
#pragma comment(lib, "Ws2_32.lib")
//IP报头
typedef struct IP_HEADER
{
unsigned char hdr_len:4; //4位头部长度
unsigned char version:4; //4位版本号
unsigned char tos; //8位服务类型
unsigned short total_len; //16位总长度
unsigned short identifier; //16位标识符
unsigned short frag_and_flags; //3位标志加13位片偏移
unsigned char ttl; //8位生存时间
unsigned char protocol; //8位上层协议号
unsigned short checksum; //16位校验和
unsigned long sourceIP; //32位源IP地址
unsigned long destIP; //32位目的IP地址
} IP_HEADER;
//ICMP报头
typedef struct ICMP_HEADER
{
BYTE type; //8位类型字段
BYTE code; //8位代码字段
USHORT cksum; //16位校验和
USHORT id; //16位标识符
USHORT seq; //16位序列号
} ICMP_HEADER;
//报文解码结构
typedef struct DECODE_RESULT
{
USHORT usSeqNo; //序列号
DWORD dwRoundTripTime; //往返时间
in_addr dwIPaddr; //返回报文的IP地址
}DECODE_RESULT;
//计算网际校验和函数
USHORT checksum( USHORT *pBuf, int iSize )
{
unsigned long cksum = 0;
while( iSize > 1 )
{
cksum += *pBuf++;
iSize -= sizeof(USHORT);
}
if( iSize )//如果 iSize 为正,即为奇数个字节
{
cksum += *(UCHAR *)pBuf; //则在末尾补上一个字节,使之有偶数个字节
}
cksum = ( cksum >> 16 ) + ( cksum&0xffff );
cksum += ( cksum >> 16 );
return (USHORT)( ~cksum );
}
//对数据包进行解码
BOOL DecodeIcmpResponse(char * pBuf, int iPacketSize, DECODE_RESULT &DecodeResult,
BYTE ICMP_ECHO_REPLY, BYTE ICMP_TIMEOUT)
{
//检查数据报大小的合法性
IP_HEADER* pIpHdr = ( IP_HEADER* )pBuf;
int iIpHdrLen = pIpHdr->hdr_len * 4; //ip报头的长度是以4字节为单位的
//若数据包大小 小于 IP报头 + ICMP报头,则数据报大小不合法
if ( iPacketSize < ( int )( iIpHdrLen + sizeof( ICMP_HEADER ) ) )
return FALSE;
//根据ICMP报文类型提取ID字段和序列号字段
ICMP_HEADER *pIcmpHdr = ( ICMP_HEADER * )( pBuf + iIpHdrLen );//ICMP报头 = 接收到的缓冲数据 + IP报头
USHORT usID, usSquNo;
if( pIcmpHdr->type == ICMP_ECHO_REPLY ) //ICMP回显应答报文
{
usID = pIcmpHdr->id; //报文ID
usSquNo = pIcmpHdr->seq; //报文序列号
}
else if( pIcmpHdr->type == ICMP_TIMEOUT )//ICMP超时差错报文
{
char * pInnerIpHdr = pBuf + iIpHdrLen + sizeof( ICMP_HEADER ); //载荷中的IP头
int iInnerIPHdrLen = ( ( IP_HEADER * )pInnerIpHdr )->hdr_len * 4; //载荷中的IP头长
ICMP_HEADER * pInnerIcmpHdr = ( ICMP_HEADER * )( pInnerIpHdr + iInnerIPHdrLen );//载荷中的ICMP头
usID = pInnerIcmpHdr->id; //报文ID
usSquNo = pInnerIcmpHdr->seq; //序列号
}
else
{
return false;
}
//检查ID和序列号以确定收到期待数据报
if( usID != ( USHORT )GetCurrentProcessId() || usSquNo != DecodeResult.usSeqNo )
{
return false;
}
//记录IP地址并计算往返时间
DecodeResult.dwIPaddr.s_addr = pIpHdr->sourceIP;
DecodeResult.dwRoundTripTime = GetTickCount() - DecodeResult.dwRoundTripTime;
//处理正确收到的ICMP数据报
if ( pIcmpHdr->type == ICMP_ECHO_REPLY || pIcmpHdr->type == ICMP_TIMEOUT )
{
//输出往返时间信息
if(DecodeResult.dwRoundTripTime)
cout<<" "<<DecodeResult.dwRoundTripTime<<"ms"<<flush;
else
cout<<" "<<"<1ms"<<flush;
}
return true;
}
void main()
{
//初始化Windows sockets网络环境
WSADATA wsa;
WSAStartup( MAKEWORD(2,2), &wsa );
char IpAddress[255];
cout<<"请输入一个IP地址或域名:";
cin>>IpAddress;
//得到IP地址
u_long ulDestIP = inet_addr( IpAddress );
//转换不成功时按域名解析
if( ulDestIP == INADDR_NONE )
{
hostent * pHostent = gethostbyname( IpAddress );
if( pHostent )
{
ulDestIP = ( *( in_addr* )pHostent->h_addr).s_addr;
}
else
{
cout<<"输入的IP地址或域名无效!"<<endl;
WSACleanup();
return;
}
}
cout<<"Tracing roote to "<<IpAddress<<" with a maximum of 30 hops.\n"<<endl;
//填充目的端socket地址
sockaddr_in destSockAddr;
ZeroMemory( &destSockAddr, sizeof( sockaddr_in ) );
destSockAddr.sin_family = AF_INET;
destSockAddr.sin_addr.s_addr = ulDestIP;
//创建原始套接字
SOCKET sockRaw = WSASocket( AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED );
//超时时间
int iTimeout = 3000;
//设置接收超时时间
setsockopt( sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char *)&iTimeout, sizeof( iTimeout ) );
//设置发送超时时间
setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char *)&iTimeout,sizeof(iTimeout));
//构造ICMP回显请求消息,并以TTL递增的顺序发送报文
//ICMP类型字段
const BYTE ICMP_ECHO_REQUEST = 8; //请求回显
const BYTE ICMP_ECHO_REPLY = 0; //回显应答
const BYTE ICMP_TIMEOUT = 11; //传输超时
//其他常量定义
const int DEF_ICMP_DATA_SIZE = 32; //ICMP报文默认数据字段长度
const int MAX_ICMP_PACKET_SIZE = 1024; //ICMP报文最大长度(包括报头)
const DWORD DEF_ICMP_TIMEOUT = 3000; //回显应答超时时间
const int DEF_MAX_HOP = 30; //最大跳站数
//填充ICMP报文中每次发送时不变的字段
char IcmpSendBuf[ sizeof( ICMP_HEADER ) + DEF_ICMP_DATA_SIZE ];//发送缓冲区
memset( IcmpSendBuf, 0, sizeof( IcmpSendBuf ) ); //初始化发送缓冲区
char IcmpRecvBuf[ MAX_ICMP_PACKET_SIZE ]; //接收缓冲区
memset( IcmpRecvBuf, 0, sizeof( IcmpRecvBuf ) ); //初始化接收缓冲区
ICMP_HEADER * pIcmpHeader = ( ICMP_HEADER* )IcmpSendBuf;
pIcmpHeader->type = ICMP_ECHO_REQUEST; //类型为请求回显
pIcmpHeader->code = 0; //代码字段为0
pIcmpHeader->id = (USHORT)GetCurrentProcessId(); //ID字段为当前进程号
memset( IcmpSendBuf + sizeof( ICMP_HEADER ), 'E', DEF_ICMP_DATA_SIZE );//数据字段
USHORT usSeqNo = 0; //ICMP报文序列号
int iTTL = 1; //TTL初始值为1
BOOL bReachDestHost = FALSE; //循环退出标志
int iMaxHot = DEF_MAX_HOP; //循环的最大次数
DECODE_RESULT DecodeResult; //传递给报文解码函数的结构化参数
while( !bReachDestHost && iMaxHot-- )
{
//设置IP报头的TTL字段
setsockopt( sockRaw, IPPROTO_IP, IP_TTL, (char *)&iTTL, sizeof(iTTL) );
cout<<iTTL<<flush; //输出当前序号,flush表示将缓冲区的内容马上送进cout,把输出缓冲区刷新
//填充ICMP报文中每次发送变化的字段
((ICMP_HEADER *)IcmpSendBuf)->cksum = 0; //校验和先置为0
((ICMP_HEADER *)IcmpSendBuf)->seq = htons(usSeqNo++); //填充序列号
((ICMP_HEADER *)IcmpSendBuf)->cksum =
checksum( ( USHORT * )IcmpSendBuf, sizeof( ICMP_HEADER ) + DEF_ICMP_DATA_SIZE ); //计算校验和
//记录序列号和当前时间
DecodeResult.usSeqNo = ( ( ICMP_HEADER* )IcmpSendBuf )->seq; //当前序号
DecodeResult.dwRoundTripTime = GetTickCount(); //当前时间
//发送TCP回显请求信息
sendto( sockRaw, IcmpSendBuf, sizeof(IcmpSendBuf), 0, (sockaddr*)&destSockAddr, sizeof(destSockAddr) );
//接收ICMP差错报文并进行解析处理
sockaddr_in from; //对端socket地址
int iFromLen = sizeof(from);//地址结构大小
int iReadDataLen; //接收数据长度
while(1)
{
//接收数据
iReadDataLen = recvfrom( sockRaw, IcmpRecvBuf, MAX_ICMP_PACKET_SIZE, 0, (sockaddr*)&from, &iFromLen );
if( iReadDataLen != SOCKET_ERROR )//有数据到达
{
//对数据包进行解码
if(DecodeIcmpResponse( IcmpRecvBuf, iReadDataLen, DecodeResult, ICMP_ECHO_REPLY, ICMP_TIMEOUT ) )
{
//到达目的地,退出循环
if( DecodeResult.dwIPaddr.s_addr == destSockAddr.sin_addr.s_addr )
bReachDestHost = true;
//输出IP地址
cout<<'\t'<<inet_ntoa( DecodeResult.dwIPaddr )<<endl;
break;
}
}
else if( WSAGetLastError() == WSAETIMEDOUT ) //接收超时,输出*号
{
cout<<" *"<<'\t'<<"Request timed out."<<endl;
break;
}
else
{
break;
}
}
iTTL++; //递增TTL值
}
}
目 录
第一章 总论 5
1.1项目概要 5
1.2可行性研究报告编制依据 8
1.3综合评价 8
第二章 项目背景及必要性 11
2.1项目建设背景 11
2.2项目建设的必要性 13
第三章 建设条件 16
3.1项目区概况 16
3.2项目建设条件优劣势分析 21
第四章 市场分析与销售方案 26
4.1市场分析 26
4.2销售策略、营销方案和模式 29
4.3风险分析 30
第五章 项目建设方案 32
5.1建设任务和规模 32
5.2建设规划和布局 32
5.3工艺(技术)方案 32
5.4建设内容 35
5.5实施进度安排 36
第六章 环境影响评价 38
6.1环境影响 38
6.2环境保护与治理措施 38
第七章 项目组织与管理 40
7.1组织机构与职能划分 40
7.2劳动定员 40
7.3经营管理模式 41
7.4经营管理措施 41
7.5技术培训 42
7.6劳动保护、安全卫生与消防 42
第八章 投资概算与资金来源 44
8.1投资概算依据 44
8.2投资概算 44
8.3资金来源 46
第九章 财务评价 47
9.1财务评价依据 47
9.2销售收入、销售税金和附加估算 47
9.3总成本及经营成本估算 48
9.4财务效益分析 49
9.5不确定性分析 50
9.6财务评价结论 51
第十章 社会效益评价 52
10.1社会评价基本结论 52
10.2农业产业化经营 52
10.3农民增收、农业增效评价 54
10.4其它社会影响 55
第十一章 可行性研究结论与建议 56
11.1可行性研究结论 56
11.2问题与建议 56
展开阅读全文