tracert跟踪路由C语言源代码

#define  WIN32_LEAN_AND_MEAN

#include <winsock2.h>                     //使用原始套接字需要WinSock2的支持
#include <ws2tcpip.h>                     //进行IPPROTO_IP级别设置时用到
#include <stdio.h>
#include <stdlib.h>
#define ICMP_ECHO       8                 //发送Ping请求时的ICMP报文类型
#define ICMP_ECHOREPLY  0                 //接收Ping回复时的ICMP报文类型
#define ICMP_TIMEOUT    11                //ICMP超时报文类型
#define ICMP_MIN        8                 //Minimum 8-byte ICMP packet (header)
#define MAX_PACKET      1024              //Max ICMP packet size
#define DEICMP_PACKSIZE 44                //Defaut ICMP PACKET SIZE
char    lpdest[16];                       //用来存放目的IP地址
DWORD   cStartTickCount;                  //用来存放发送包的起始时间

#pragma comment( lib, "ws2_32.lib" )    

typedef struct _icmphdr                   //ICMP头部定义,被封装在IP包中
{
	BYTE   i_type;                        //报文类型
	BYTE   i_code;                        //代码
	USHORT i_cksum;                       //校验和
	USHORT i_id;                          //标识符
	USHORT i_seq;                         //序号	
}IcmpHeader;
//初始化ICMP头部
void FillICMPData(char *icmp_data,int datasize)
{
	IcmpHeader *icmp_hdr=NULL;
	char       *datapart=NULL;
	icmp_hdr=(IcmpHeader *)icmp_data;
	icmp_hdr->i_type=ICMP_ECHO;     //request an ICMP echo
	icmp_hdr->i_code=0;
	icmp_hdr->i_id=(USHORT)GetCurrentProcessId();
	icmp_hdr->i_cksum=0;
	icmp_hdr->i_seq=0;
	datapart=icmp_data+sizeof(IcmpHeader);
	memset(datapart,'E',datasize-sizeof(IcmpHeader));
}
//校验和函数
USHORT 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);;
}
int DecodeIPHeader(char *buf,int bytes,struct sockaddr_in *from)
{
	IcmpHeader      *icmphdr=NULL;
	DWORD           tick;
	static int      icmpcount=1;
	unsigned short  iphdrlen;
	//判断接收操作是否超时
	if(!buf)
	{
		printf("%2d:        ***.***.***.***        Request timed out.\n",icmpcount++);
		return 1;
	}
	tick=GetTickCount();
	iphdrlen=(buf[0] & 0x0f)*4;
	icmphdr=(IcmpHeader *)(buf+iphdrlen);
	if(bytes<iphdrlen+ICMP_MIN)
	{
		printf("Too few bytes from %s\n",inet_ntoa(from->sin_addr));
		return 0;
	}
	//判断接收的ICMP报文是否为超时报文
	if(icmphdr->i_type==ICMP_TIMEOUT&&icmphdr->i_code==0)
	{
		printf("%2d:        %-15s       %4dms\n",icmpcount++,inet_ntoa(from->sin_addr),tick-cStartTickCount);	
		return 0;
	}
	//判断接收的ICMP报文是否为回复报文
	else if(icmphdr->i_type==ICMP_ECHOREPLY&&icmphdr->i_id==GetCurrentProcessId())
	{
		printf("%2d:        %-15s       %4dms\n",icmpcount++,inet_ntoa(from->sin_addr),tick-cStartTickCount);
		printf("Trace complete!\n");
		return 1;
	}
	//其他类型,表示不可达
	else
	{
		printf("%2d:        Destination host is unreachable!\n",icmpcount++);
		return 1;
	}
}
int main()
{
	WSADATA            wsaData;
	SOCKET             sockRaw=INVALID_SOCKET;
	struct sockaddr_in dest,
		from;
	int                i,bread,fromlen=sizeof(from),timeout=1000,ret;	
	struct hostent    *hp=NULL;
	char              *icmp_data=NULL,*recvbuf=NULL;
	USHORT             seq_no=0;

	printf("Destination Address(IP/Host name):");
	scanf("%s",lpdest);	
	if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
	{
		printf("WSAStartup() failed:%d\n",GetLastError());
		return -1;
	}

	//创建套接字
	sockRaw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED);
	if(sockRaw==INVALID_SOCKET)
	{
		printf("WSASocket() failed:%d\n",WSAGetLastError());
		return -1;
	}
	//对锁定套接字设置超时
	bread=setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout));
	if(bread==SOCKET_ERROR)
	{
		printf("setsockopt(SO_RCVTIMEO) failed:%d\n",WSAGetLastError());
		return -1;
	}
	timeout=1000;
	bread=setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(timeout));
	if(bread==SOCKET_ERROR)
	{
		printf("setsockopt(SO_SNDTIMEO) failed:%d\n",WSAGetLastError());
		return -1;
	}
	//解析目标地址,将主机名转化为IP地址
	memset(&dest,0,sizeof(dest));
	dest.sin_family=AF_INET;
	if((dest.sin_addr.S_un.S_addr=inet_addr(lpdest))==INADDR_NONE)
	{
		if((hp=gethostbyname(lpdest))!=NULL)
		{
			memcpy(&(dest.sin_addr.S_un.S_addr),hp->h_addr_list[0],hp->h_length);
			dest.sin_family=hp->h_addrtype;
			printf("dest.sin_addr=%s\n",inet_ntoa(dest.sin_addr));
		}
		else
		{
			printf("gethostbyname() failed:%d\n",WSAGetLastError());
			return -1;
		}
	}
	//Create the ICMP pakcet
	icmp_data= (char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET);
	recvbuf  = (char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET);
	if(!icmp_data)
	{
		printf("HeapAlloc() failed: %d\n",GetLastError());
		return -1;
	}
	memset(icmp_data,0,MAX_PACKET);
	FillICMPData(icmp_data,DEICMP_PACKSIZE);
	printf("Hop          IP Address           Time elapsed\n");
	//开始发送/接收ICMP报文
	for(i=1;i<=255;i++)
	{
		int bwrote;
		//设置IP包的生存期
		ret=setsockopt(sockRaw,IPPROTO_IP,IP_TTL,(char *)&i,sizeof(int));
		if(ret==SOCKET_ERROR)
		{
			printf("setsockopt(IP_TTL) failed:%d\n",WSAGetLastError());
		}
		((IcmpHeader *)icmp_data)->i_cksum =0;		
		((IcmpHeader *)icmp_data)->i_seq=seq_no++;     //Sequence number of ICMP packets
		((IcmpHeader *)icmp_data)->i_cksum=checksum((USHORT *)icmp_data,DEICMP_PACKSIZE);
		//发送ICMP包请求查询
		cStartTickCount=GetTickCount();
		bwrote=sendto(sockRaw,icmp_data,DEICMP_PACKSIZE,0,(struct sockaddr *)&dest,sizeof(dest));
		if(bwrote==SOCKET_ERROR)
		{
			if(WSAGetLastError()==WSAETIMEDOUT)
			{
				printf("timed out\n");
				continue;
			}
			printf("sendto() failed:%d\n",WSAGetLastError());
			return -1;
		}
		if(bwrote<DEICMP_PACKSIZE)
		{
			printf("Wrote %d bytes\n",bwrote);
		}
		//接收ICMP回复包
		bread=recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr *)&from,&fromlen);
		if(bread==SOCKET_ERROR)
		{
			if(WSAGetLastError()==WSAETIMEDOUT)
			{
				DecodeIPHeader(NULL,0,NULL);
				continue;
			}
			printf("recvfrom() failed:%d\n",WSAGetLastError());
			return -1;
		}
		if(DecodeIPHeader(recvbuf,bread,&from))
			break;
		Sleep(1000);
	}

	system("pause");

	if(sockRaw!=INVALID_SOCKET)
		closesocket(sockRaw);

	HeapFree(GetProcessHeap(),0,recvbuf);
	HeapFree(GetProcessHeap(),0,icmp_data);
	WSACleanup();

	

	return 0;
}


 

测试如下:

 


 


 

`tracert`命令是一个用于追踪数据包从发送设备到目标设备所经过的路径的工具。它通过向目标地址发送一系列包含TTL (Time To Live) 标志的数据包,并记录下每个数据包到达目的地所需的往返时间(RTT),以此绘制出一条通路。 然而,`tracert` 的跟踪过程并不完全等同于实际网络数据包的路由过程: ### 1. **协议限制** `tracert` 使用的是 ICMP 协议,这是为了在 IP 层测试连通性和了解路径信息。但在实际生产环境中,大多数流量使用的协议可能是 TCP 或 UDP 等其他协议。因此,虽然 `tracert` 能够揭示一些路由器的存在及其顺序,但它并不能反映实际应用层如何选择路径或在哪些点上可能发生的数据封装、解封装及处理过程。 ### 2. **动态路由调整** 实际的互联网路由是由众多自治系统(AS)之间的动态路由协议控制的,如 BGP(Border Gateway Protocol)、OSPF(Open Shortest Path First)等。这些协议允许网络动态地改变其路由策略,以适应网络流量的变化、新链路的加入或故障的恢复。而 `tracert` 提供的信息基于静态缓存数据,无法实时反应这些动态变化。 ### 3. **路径优化与负载均衡** 网络中的数据传输路径通常受到各种因素的影响,包括路径长度、带宽、拥塞情况以及服务质量(QoS)。实际的路由决策可能会优先考虑性能最优或成本最低的路径,这在 `tracert` 中是无法体现的。此外,在某些情况下,如数据中心内部的私有IP网络,`tracert` 甚至可能无法提供有效的路由信息,因为其依赖的ICMP协议在此环境下可能不受支持或者被禁止。 ### 4. **网络安全与隐私** 实际的网络流量往往涉及安全机制,例如防火墙规则、内容过滤、加密通信等。`tracert` 不会涉及这类操作,它只能查看经过的基本路由信息。这意味着实际路由中的一些关键细节,比如特定端口是否被监听、是否进行了加密传输等,都无法通过 `tracert` 探测出来。 综上所述,尽管`tracert` 工具非常有用,可以作为诊断网络连接问题的一个起点,但它提供的信息是有限的,只反映了基本的路由层次结构,而忽略了实际应用层面的复杂性、动态性以及安全性考量。对于更深入的网络分析和故障排查,可能需要结合更多的网络监控工具和技术来进行综合评估。
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值