今天小编将给大家分享我在东北大学上研究生的时候学到的关于路由追踪的实现,希望大家看完小编的这篇博文后能够对windows 下的tracert命令有一定的了解。

    1. 实验目的

    Tracert(跟踪路由)是路由跟踪实用程序,用于确定 IP 数据报访问目标所采取的路径。Tracert 命令用 IP 生存时间 (TTL) 字段和 ICMP 错误消息来确定从一个主机到网络上其他主机的路由。

    2. 实验环境

    一台具有登陆外网功能的电脑和连通的网络。

    3. 实验内容和要求

    Windows 系统环境
    CMD命令tracert www.baidu.com

    4. 实验原理

   通过向目标发送不同 IP 生存时间 (TTL) 值的“Internet 控制消息协议 (ICMP)”回应数据包,Tracert诊断程序确定到目标所采取的路由。要求路径上的每个路由器在转发数据包之前至少将数据包上的 TTL 递减 1。数据包上的 TTL 减为 0 时,路由器应该将“ICMP 已超时”的消息发回源系统。
Tracert 先发送 TTL 为 1 的回应数据包,并在随后的每次发送过程将 TTL 递增 1,直到目标响应或 TTL 达到最大值,从而确定路由。通过检查中间路由器发回的“ICMP 已超时”的消息确定路由。某些路由器不经询问直接丢弃 TTL 过期的数据包,这在 Tracert 实用程序中看不到。
Tracert 命令按顺序打印出返回“ICMP 已超时”消息的路径中的近端路由器接口列表。如果使用 -d 选项,则 Tracert 实用程序不在每个 IP 地址上查询 DNS。

    5.探测方式

    tracert是Windows下常用的命令行工具,UNIX下与之对应的是traceroute。若想知道自己的电脑到www.aorb. org 经过了多少个路由器,可在命令行下输入tracert www.aorb. org进行探测,返回结果也许会与ping -R相同,但它是以另一种方式实现的。这种方式并没有像Record route options探测技术中使用IP协议包的Options字段,而是利用了IP协议包中的TTL字段。
    基本思路是这样的:www.aorb. org这台服务器即卖茄子(提供HTTP服务),也卖黄瓜(提供FTP服务),但不卖土豆(未监听的UDP端口)。有位买家,为了知道一封信到达蔬菜供应商www.aorb. org中间需要经过几个邮局(路由器),于是写信给www.aorb. org询问土豆的价格。
    第一步,买家在信封的TTL位置写上数字1,当这封信到达与自己相邻的第一个邮局时,邮局的人把TTL值减1,于是TTL为0,邮局章程规定,把TTL值为0的信丢到垃圾桶里,然后给买家发一封名曰超时的信,信上写了丢信邮局的名字,以告诉买家信被谁丢掉了。
    第二步,买家在信封的TTL位置写上数字2,当这封信到达与自己相邻的第一个邮局时,邮局的人把TTL值减1,现在TTL值为1,邮局章程规定, TTL值不为0的数据报需要继续转发给下一个邮局,于是这封信又被转发到了下一个邮局。当下一个邮局收到这封信时,邮局的人把TTL值减1,于是TTL为 0,邮局章程规定,把TTL值为0的信丢到垃圾桶里,然后给买家发一封名曰超时的信,信上写了丢信邮局的名字,以告诉买家信被谁丢掉了。
通过第一步,买家知道了第一个中转路由器。通过第二步,买家知道了第二个中转路由器。那么买家只需要不断的把TTL值加1,便可根据如上步骤探测出中间经过了哪些路由器。
    第三步,我们假设中间只经过两个邮局信便到达目的地了。接着第二步,买家选在把TTL值设置为3,当这封信到达与自己相邻的第一个邮局时,邮局的人把TTL值减1,现在TTL值为2,邮局章程规定,TTL值不为0的数据报需要继续转发给下一个邮局,于是这封信又被转发到了下一个邮局。当下一个邮局收到这封信时,邮局的人把TTL值减1,于是TTL为1,邮局章程规定,TTL值不为0的数据报需要继续转发给下一个邮局,于是这封信又被转发到了下一个邮局,但下一个邮局已经就是最终目的地www.aorb .org了,尽管www.aorb .org把TTL减1的结果为0,但却不会丢弃它,因为目的地就是 www.aorb. org呀!于是www.aorb .org把这个IP层邮递员送来的信交付给负责卖土豆人,但我们开始说了,www.aorb .org并不卖土豆,于是就回送一个名曰终点不可达(ICMP类型为3,代码为3)的信给买家。
    第四步,当买家收到类型为终点不可达的信时,就不再把那封问土豆价格的信上的TTL加1了,因为他已经知道了到达目的地的全部路由。

    6.源代码

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

#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>

#include <stdio.h>
#include <stdlib.h>

//
// ICMP 消息类型
//
#define ICMP_ECHOREPLY      0
#define ICMP_DESTUNREACH    3
#define ICMP_SRCQUENCH      4
#define ICMP_REDIRECT       5
#define ICMP_ECHO           8
#define ICMP_TIMEOUT       11
#define ICMP_PARMERR       12

#define MAX_HOPS           30

#define ICMP_MIN 8    //ICMP报文最小长度(仅仅头部)

//
// IP分组头部
//
typedef struct iphdr
{
	unsigned int   h_len : 4;        // Length of the header
	unsigned int   version : 4;      // Version of IP
	unsigned char  tos;            // Type of service
	unsigned short total_len;      // Total length of the packet
	unsigned short ident;          // Unique identifier
	unsigned short frag_and_flags; // Flags
	unsigned char  ttl;            // Time to live
	unsigned char  proto;          // Protocol (TCP, UDP etc)
	unsigned short checksum;       // IP checksum
	unsigned int   sourceIP;       // Source IP
	unsigned int   destIP;         // Destination IP
} IpHeader;

//
// ICMP 数据报头部
//
typedef struct _ihdr
{
	BYTE   i_type;               // ICMP message type
	BYTE   i_code;               // Sub code
	USHORT i_cksum;
	USHORT i_id;                 // Unique id
	USHORT i_seq;                // Sequence number
	// This is not the std header, but we reserve space for time
	ULONG timestamp;
} IcmpHeader;

#define DEF_PACKET_SIZE         32
#define MAX_PACKET            1024
//打印用户信息
void usage(char *progname)
{
	printf("usage: %s host-name [max-hops]\n", progname);
	getchar();
	ExitProcess(-1);
}
// 设置存活时间
int set_ttl(SOCKET s, int nTimeToLive)
{
	int     nRet;

	nRet = setsockopt(s, IPPROTO_IP, IP_TTL, (LPSTR)&nTimeToLive,
		sizeof(int));
	if (nRet == SOCKET_ERROR)
	{
		printf("setsockopt(IP_TTL) failed: %d\n",
			WSAGetLastError());
		return 0;
	}
	return 1;
}

// 对回应尽心编译
int decode_resp(char *buf, int bytes, SOCKADDR_IN *from, int ttl)
{
	IpHeader       *iphdr = NULL;			//ip头部
	IcmpHeader     *icmphdr = NULL;			//icmp头部
	unsigned short  iphdrlen;				//ip头部长度
	struct hostent *lpHostent = NULL;		//host实体
	struct in_addr  inaddr = from->sin_addr;//ipv4 网络地址

	iphdr = (IpHeader *)buf;
	// Number of 32-bit words * 4 = bytes
	iphdrlen = iphdr->h_len * 4;

	if (bytes < iphdrlen + ICMP_MIN)		//ip分组长度过段
		printf("Too few bytes from %s\n",
		inet_ntoa(from->sin_addr));

	icmphdr = (IcmpHeader*)(buf + iphdrlen);

	switch (icmphdr->i_type)
	{
	case ICMP_ECHOREPLY:     // Response from destination
		lpHostent = gethostbyaddr((const char *)&from->sin_addr,
			AF_INET, sizeof(struct in_addr));
		if (lpHostent != NULL)
			printf("%2d  %s (%s)", ttl, lpHostent->h_name,
			inet_ntoa(inaddr));
		return 1;
		break;
	case ICMP_TIMEOUT:      // Response from router along the way
		printf("%2d  %s/n", ttl, inet_ntoa(inaddr));
		return 0;
		break;
	case ICMP_DESTUNREACH:  // Can't reach the destination at all
		printf("%2d  %s  reports: Host is unreachable\n", ttl,
			inet_ntoa(inaddr));
		return 1;
		break;
	default:
		printf("non-echo type %d recvd\n", icmphdr->i_type);
		return 1;
		break;
	}
	return 0;
}

//检测和
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);
}

//填充ICMP数据
void fill_icmp_data(char * icmp_data, int datasize)
{
	IcmpHeader *icmp_hdr;
	char       *datapart;

	icmp_hdr = (IcmpHeader*)icmp_data;

	icmp_hdr->i_type = 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);
	//
	// Place some junk in the buffer. Don't care about the data...
	//
	memset(datapart, 'E', datasize - sizeof(IcmpHeader));
}

//
// Function: main
// 
int main(int argc, char **argv)
{
	WSADATA         wsd;
	SOCKET          sockRaw;
	HOSTENT         *hp = NULL;
	SOCKADDR_IN     dest, from;

	int     ret;
	int     datasize;
	int     fromlen = sizeof(from);
	int     timeout;
	int     done = 0;
	int     maxhops;
	int     ttl = 1;
	char    *icmp_data;
	char    *recvbuf;
	BOOL    bOpt;
	USHORT  seq_no = 0;

	// Initialize the Winsock2 DLL
	//
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		printf("WSAStartup() failed: %d\n", GetLastError());
		return -1;
	}
	if (argc < 2)
		usage(argv[0]);
	if (argc == 3)
		maxhops = atoi(argv[2]);
	else
		maxhops = MAX_HOPS;
	
	sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP,
		NULL, 0, WSA_FLAG_OVERLAPPED);
	if (sockRaw == INVALID_SOCKET)
	{
		printf("WSASocket() failed: %d\n", WSAGetLastError());
		ExitProcess(-1);
	}
	timeout = 1000;
	ret = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO,
		(char *)&timeout, sizeof(timeout));
	if (ret == SOCKET_ERROR)
	{
		printf("setsockopt(SO_RCVTIMEO) failed: %d\n",
			WSAGetLastError());
		return -1;
	}
	timeout = 1000;
	ret = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO,
		(char *)&timeout, sizeof(timeout));
	if (ret == SOCKET_ERROR)
	{
		printf("setsockopt(SO_SNDTIMEO) failed: %d\n",
			WSAGetLastError());
		return -1;
	}

	ZeroMemory(&dest, sizeof(dest));
	dest.sin_family = AF_INET;
	if ((dest.sin_addr.s_addr = inet_addr(argv[1])) == INADDR_NONE)
	{
		hp = gethostbyname(argv[1]);
		if (hp)
			memcpy(&(dest.sin_addr), hp->h_addr, hp->h_length);
		else
		{
			printf("Unable to resolve %s\n", argv[1]);
			ExitProcess(-1);
		}
	}
	datasize = DEF_PACKET_SIZE;

	datasize += sizeof(IcmpHeader);
	icmp_data = (char *)(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PACKET));
	recvbuf = (char *)(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PACKET));

	if ((!icmp_data) || (!recvbuf))
	{
		printf("HeapAlloc() failed %d\n", GetLastError());
		return -1;
	}
	bOpt = TRUE;
	if (setsockopt(sockRaw, SOL_SOCKET, SO_DONTROUTE, (char *)&bOpt,
		sizeof(BOOL)) == SOCKET_ERROR)
		printf("setsockopt(SO_DONTROUTE) failed: %d\n",
		WSAGetLastError());

	memset(icmp_data, 0, MAX_PACKET);
	fill_icmp_data(icmp_data, datasize);

	printf("/nTracing route to %s over a maximum of %d hops:\n\n",
		argv[1], maxhops);

	for (ttl = 1; ((ttl < maxhops) && (!done)); ttl++)
	{
		int bwrote;

		((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(sockRaw, icmp_data, datasize, 0,
			(SOCKADDR *)&dest, sizeof(dest));
		if (bwrote == SOCKET_ERROR)
		{
			if (WSAGetLastError() == WSAETIMEDOUT)
			{
				printf("%2d  Send request timed out. \n", ttl);
				continue;
			}
			printf("sendto() failed: %d\n", WSAGetLastError());
			return -1;
		}
		ret = recvfrom(sockRaw, recvbuf, MAX_PACKET, 0,
			(struct sockaddr*)&from, &fromlen);
		if (ret == SOCKET_ERROR)
		{
			if (WSAGetLastError() == WSAETIMEDOUT)
			{
				printf("%2d  Receive Request timed out. \n", ttl);
				continue;
			}
			printf("recvfrom() failed: %d \n", WSAGetLastError());
			return -1;
		}
		done = decode_resp(recvbuf, ret, &from, ttl);
		Sleep(1000);
	}
	HeapFree(GetProcessHeap(), 0, recvbuf);
	HeapFree(GetProcessHeap(), 0, icmp_data);

	getchar();
	return 0;
}



    7.实验结果


    

Android 版本 trace追踪路由 安卓路由跟踪_计算机网络