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

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. 运行结果:(可以看到访问相同的网站所经过的路由路径不唯一

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/256646.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

路由追踪——traceroute与tracert

一、路由追踪   &#xff08;一&#xff09;路由跟踪&#xff0c;就是获取从主机A到达目标主机B这个过程中所有需要经过的路由设备的转发接口IP。 &#xff08;二&#xff09;ICMP协议   Internet控制报文协议&#xff08;internet control message protocol&#xff09;&a…

利用路由追踪工具收集路由信息

预备知识 了解traceroute、Dmitry、Itrace、Tcptraceroute、tctrace等工具的使用方法。 实验 1、此环境用bt5、Linux&#xff08;可能需要装Java&#xff09; 2、打开目标主机linux&#xff0c;输入用户名&#xff1a;root&#xff0c;密码&#xff1a;123456.如图&#xff1…

【Linux】关于路由跟踪指令traceroute

本文所有命令实施的大前提是&#xff1a;你现在能够上网。 稍有计算机常识的人都知道ping命令&#xff0c;是用来检查自己的主机是否与目标地址接通&#xff0c;自己的主机与目标地址的通讯包通讯速率&#xff0c;所谓的通讯包也就是那些什么TCP/IP&#xff0c;UDP包&#xff…

Linux Command traceroute 路由追踪

Linux Command traceroute 路由追踪 tags: 网络, 分析 文章目录 Linux Command traceroute 路由追踪1. 介绍2. 工作原理&#xff1a;3. 命令格式&#xff1a;4. 命令功能&#xff1a;5. 用法5.1 最常用的用法5.2 跳数设置5.3 显示IP地址&#xff0c;不查主机名5.4 探测包使用的…

路由跟踪Tracert

路由跟踪Tracert 指令&#xff1a;tracert“address” 作用&#xff1a;是查看数据包从你的电脑出发后&#xff0c;经过了哪些设备最终到达目的地址&#xff1b; 原理&#xff1a; 通过向目标发送不同 IP 生存时间 (TTL) 值的“Internet 控制消息协议 (ICMP)”回应数据包&am…

路由追踪的实现

今天小编将给大家分享我在东北大学上研究生的时候学到的关于路由追踪的实现&#xff0c;希望大家看完小编的这篇博文后能够对windows 下的tracert命令有一定的了解。 1&#xff0e; 实验目的 Tracert&#xff08;跟踪路由&#xff09;是路由跟踪实用程序&#xff0c;用于确定 I…

31.traceroute命令进行路由跟踪

路由跟踪功能是用来识别一个设备到另一个设备的网络路径。在一个简单的网络上&#xff0c;这个网络路径可能只经过一个路由器&#xff0c;甚至一个都不经过。但是在复杂的网络中&#xff0c;数据包可能要经过数十个路由器才会到达最终目的地。在通信过程中&#xff0c;可以通过…

路由追踪traceroute分析

原文 &#xff1a; http://www.freebuf.com/articles/network/118221.html 一、路由追踪程序traceroute/tracert Traceroute是Linux和Mac OS等系统默认提供的路由追踪小程序&#xff0c;Tracert是Windows系统默认提供的路由追踪小程序。二者的功能相同&#xff0c;都能探测数据…

路由跟踪(tracert、traceroute)简析

WINDOWS平台命令&#xff1a; tracert LINUX平台命令&#xff1a; traceroute 简介&#xff1a; Tracert&#xff08;traceroute&#xff09; 命令用 IP 生存时间 (TTL) 字段和 ICMP 错误消息来确定从一个主机到网络上其他主机的路由。用来跟踪到目的IP所经过路径 原理简…

【Rust 日报】2023-05-28 一个构建在TCP上的聊天工具

tcp-chat&#xff1a;构建在TCP上的简单快速轻量的聊天工具 tcp-chat通过TCP进行通信&#xff0c;该项目的目的是了解并行性和底层网络通信。 前端工具&#xff1a;Solid、Tauri、Vite 后端工具&#xff1a;Rust、Tokio、Serde GitHub: https://github.com/gatomod/tcp-chat ez…

设计模式之~命令模式

定义&#xff1a; 命令模式&#xff08;Command&#xff09;&#xff0c;将一个请求封装为一个对象&#xff0c;从而使你可用不同的请求对客户进行参数化&#xff1b;对请求排队或记录请求日志&#xff0c;以及支持可撤销的操作。 为什么需要命令模式? 在我们的软件开发系统中…

c++ 11标准模板(STL) std::map(九)

定义于头文件<map> template< class Key, class T, class Compare std::less<Key>, class Allocator std::allocator<std::pair<const Key, T> > > class map;(1)namespace pmr { template <class Key, class T, clas…

Linux常用命令——gzexe命令

在线Linux命令查询工具 gzexe 用来压缩可执行文件 补充说明 gzexe命令用来压缩可执行文件&#xff0c;压缩后的文件仍然为可执行文件&#xff0c;在执行时进行自动解压缩。当您去执行被压缩过的执行文件时&#xff0c;该文件会自动解压然后继续执行&#xff0c;和使用一般的…

设计模式之~组合模式

组合模式&#xff1a; 将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 结构图&#xff1a; 实例&#xff1a; 透明方式&#xff1a; leaf中也有add和remove叫做透明方式&#xff0c;在component中声明所有用来管…

玩具租借小程序

玩具是儿童的必需品&#xff0c;家长每年都会给孩子购买少则几百多则几千的玩具&#xff0c;但是由于孩子对玩具喜新 厌旧&#xff0c;一个几百元的玩具几天就不感兴趣了&#xff0c;而市场上层出不穷的高价玩具及孩子对玩具永不满足的渴求&#xff0c;都在促使家长对"玩具…

租号网程序源码 无错版本完整无缺

介绍&#xff1a; 注意事项&#xff1a; 安装租号程序 需要WIN08服务器 iis7.0 sq2012 NET4.6 程序直接放入设置好的 网站根目录即可 (SA密码设置yixiaoka888) 网盘下载地址&#xff1a; http://kekewl.cc/tJoeIk6qPlJ 图片&#xff1a;

在线游戏租号声优陪玩网站源码+威客平台源码/亲测

正文: 由于标题太长了&#xff0c;所以本文标题简略写了&#xff0c;下方图片才是程序完整标题: 这系统界面美观&#xff0c;功能非常齐全&#xff0c;已对接支付&#xff0c;亲测可用&#xff0c;有详细的安装教程&#xff0c;全部放压缩包里了&#xff0c;有兴趣自己去看。 …

麒麟子出了一款免费3D角色虚拟摇杆控制器!这也太好用了

不需要编写代码&#xff0c;通过简单的拖拽和配置&#xff0c;就可以使你在手机上控制摄像机旋转和操作角色。 EasyJoystick EasyCamera EasyTouch 以上效果就 麒麟子 最近在 Cocos Store 分享的 KylinsEasyController 组件源码&#xff0c;它包含以下主要特性&#xff1a; 虚拟…

租赁小程序源码芝麻免押租机平台手机租赁代运营

租赁小程序 手机租赁 小程序代运营 租赁 免押租 代运营 上征信 银行风控 芝麻免押 支付宝服务商 支付宝租赁服务商 租赁平台源码 租赁小程序源码 租机平台源码 手机租赁源码 租赁小程序源码 芝麻免押租 租赁平台 小程序源码

如何在雷电模拟器里使用YiLu代理的动态ip?

1在易路“程序”页面里随意添加一个应用&#xff0c;请不要添加“雷电”到YiLu程序里&#xff1b; 2.YiLu设置&#xff1a; ① 点击YiLu“设置”页面&#xff1b; ② 选择“YiLu便携代理引擎”&#xff1b; ③ 选择“仅代理YiLu程序选项卡中的应用程序&#xff0c;其他通过您…