Traceroute(路由追踪)的原理及实现

(1)相应的协议和原理

IP协议IP协议是TCP/IP协议族中最核心的部分,它的作用是在两台主机之间传输数据,所有上层协议的数据(HTTP、TCP、UDP等)都会被封装在一个个的IP数据包中被发送到网络上。

ICMP ICMP全称为互联网控制报文协议,它常用于传递错误信息,ICMP协议是IP层的一部分,它的报文也是通过IP数据包来传输的。

TTL :TTL(time-to-live)是IP数据包中的一个字段,它指定了数据包最多能经过几次路由器。从我们源主机发出去的数据包在到达目的主机的路上要经过许多个路由器的转发,在发送数据包的时候源主机会设置一个TTL的值,每经过一个路由器TTL就会被减去一,当TTL为0的时候该数据包会被直接丢弃(不再继续转发),并发送一个超时ICMP报文给源主机。

基于ICMP实现

实现原理:Tracert 程序关键是对 IP 头部生存时间(time to live)TTL 字段的使用,程序实现是向目的主机发送一个 ICMP 回显请求报文,初始时 TTL 等于 1 ,这样当该数据报抵达途中的第一个路由器时,TTL 的值就被减为 0,导致发送超时错误,因此该路由生成一份 ICMP 超时差错报文返回给源主机。随后,主机将数据报的 TTL 值递增 1 ,以便 IP 报能传送到下一个路由器,并由下一个路由器生成 ICMP 超时差错报文返回给源主机。不断重复这个过程,直到数据报达到目的主机或超过跳数限制,到达目的主机后,目的主机返回 ICMP 回显应答报文。这样,源主机只需要对返回的每一份 ICMP 报文进行解析处理,就可以掌握数据报从源主机到达目的主机途中所经过的路由信息。

采用这种方案的实现流程如下:

  1. 客户端发送一个TTL为1的ICMP请求回显数据包,在第一跳的时候超时并返回一个ICMP超时数据包,得到第一跳的地址。
  2. 客户端发送一个TTL为2的ICMP请求回显数据包,得到第二跳的地址。
  3. 客户端发送一个TTL为3的ICMP请求回显数据包,到达目标主机,目标主机返回一个ICMP回显应答,traceroute结束。
  1. 实现的C++代码(VS2017)

#include <iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#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;        //报文IDusSquNo = 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;        //报文IDusSquNo = 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;elsecout << "      " << "<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 = WSASocketW(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;                //代码字段为0pIcmpHeader->id = (USHORT)GetCurrentProcessId();    //ID字段为当前进程号memset(IcmpSendBuf + sizeof(ICMP_HEADER), 'E', DEF_ICMP_DATA_SIZE);//数据字段USHORT usSeqNo = 0;            //ICMP报文序列号int iTTL = 1;            //TTL初始值为1BOOL 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值}system("pause");
}
  1. 运行结果:(可以看到访问相同的网站所经过的路由路径不唯一)

Traceroute(路由追踪)的原理及实现相关推荐

  1. Traceroute(路由追踪) --- 的原理及实现

    现实世界中的网络是由无数的计算机和路由器组成的一张的大网,应用的数据包在发送到服务器之前都要经过层层的路由转发.而Traceroute是一种常规的网络分析工具,用来定位到目标主机之间的所有路由器. 原 ...

  2. 3.ICMP_抓包分析traceroute路由追踪

    一.路由追踪程序traceroute/tracert Traceroute是Linux和Mac OS等系统默认提供的路由追踪小程序,Tracert是Windows系统默认提供的路由追踪小程序.二者的功 ...

  3. traceroute路由追踪-理论

    1.traceroute介绍及基本原理 这篇文章讲的很清楚 TraceRoute程序的实现主要涉及IP头部生存时间(time to live, TTL)字段的使用. 设置TTL字段的目的是为了防止数据 ...

  4. 路由追踪工具 traceroute 使用技巧

    路由追踪工具 traceroute 使用技巧 路由追踪工作原理 路由追踪实例 1. 如何运行 traceroute 2. 禁用 IP 地址和主机名映射 3. 配置回复等待时间 4. 配置每一跳的查询次 ...

  5. 路由追踪traceroute分析

    原文  : http://www.freebuf.com/articles/network/118221.html 一.路由追踪程序traceroute/tracert Traceroute是Linu ...

  6. 路由追踪——traceroute与tracert

    一.路由追踪 (一)路由跟踪,就是获取从主机A到达目标主机B这个过程中所有需要经过的路由设备的转发接口IP. (二)ICMP协议 Internet控制报文协议(internet control mes ...

  7. 10.10 traceroute:追踪数据传输路由状况

    traceroute命令 用于显示网络数据包传输到指定主机的路径信息,追踪数据传输路由状况.默认数据包大小是60字节(IPv4)或80字节(IPv6),用户可另行设置.它与Windows下的trace ...

  8. linux怎么做路由跟踪_linux使用traceroute命令追踪路由

    Linux有一个基础的路由追踪软件:traceroute.# CentOS系统: yum update && yum install traceroute -y # Debian/Ub ...

  9. 什么是路由追踪,路由追踪有什么用?

    当您在访问互联网上的某个网站或服务器时,路由追踪可以帮助您跟踪网络数据包在整个网络中传输的路径和时间,这对于分析网络故障.优化网络性能.确保网络安全等方面非常有用. 路由追踪的工作原理是通过向目标地址 ...

最新文章

  1. 爬虫入门的基本原理,如果你连这些都不知道那你可以放弃爬虫了
  2. 美国国家科学院发布:材料有哪些研究前沿?
  3. 1.1.12 增加页眉横线
  4. 黑马lavarel教程---10、lavarel模型关联
  5. set在python中什么意思_python中set是什么意思
  6. ubuntu source
  7. SQL Server强制使用特定索引 、并行度、锁
  8. 追随自己的价值观:用研经理 Anne Diaz 职业探索之路
  9. VTK:几何对象之Planes
  10. 还不懂你现在学习的编程语言能做什么?还不懂如何进阶?过来看图
  11. C# ASP.NET MVC 微信和支付宝H5支付开发及Demo
  12. 大数高精加减乘除(洛谷P1601、P2142、P1303、P1480题题解,Java语言描述)
  13. IIS6 部署.Net相关程序问题集锦
  14. jQuery性能优化指南(2)
  15. w10计算机管理权限,设置win10管理权限_win10系统如何获取管理员权限
  16. 斐波那契堆python实现——Fibonacci Heaps
  17. pygal:一款好用到爆的 Python 可视化利器,炫酷动态图轻松绘制
  18. IDEA下载源码报错 Cannot reconnect.
  19. 变焦光学系统工作原理及初始结构设计方法
  20. 过压保护芯片,高输入电压(OVP)

热门文章

  1. python docx 修改word文件 不能修改 首行缩进
  2. 06.MTK关机充电动画显示
  3. Django 1.10中文文档-执行查询
  4. Ubuntu 16.04 安装CUDA9.0和cuDNN7.4.1(亲测成功)
  5. Ubuntu 10.04与Win 7大比拼
  6. 牛客练习赛 A-有理数 B-硬币
  7. 沙特阿拉伯埃尔奥拉皇家委员会宣布,两只阿拉伯豹幼崽的出生为延续这种极度濒危的亚种带来极大帮助
  8. 人工智能拯救生命:自杀倾向预测
  9. http://youxiputao.com/articles/3325
  10. 系统启动时在bus系统中注册platform总线