TraceRoute实现

article/2025/10/14 5:03:57

网络课上老师布置了第二个作业,写一个TraceRoute的程序。

Traceroute的工作原理:
Traceroute程序的设计是利用ICMP及IP header的TTL(Time To Live)栏位(field)。首先,traceroute送出一个TTL是1的IP datagram(其实,每次送出的为3个40字节的包,包括源地址,目的地址和包发出的时间标签)到目的地,当路径上的第一个路由器(router)收到这个datagram时,它将TTL减1。此时,TTL变为0了,所以该路由器会将此datagram丢掉,并送回一个ICMP time exceeded消息(包括发IP包的源地址,IP包的所有内容及路由器的IP地址),traceroute 收到这个消息后,便知道这个路由器存在于这个路径上,接着traceroute 再送出另一个TTL是2 的datagram,发现第2 个路由器...... traceroute 每次将送出的datagram的TTL 加1来发现另一个路由器,这个重复的动作一直持续到某个datagram 抵达目的地。当datagram到达目的地后,该主机并不会送回ICMP time exceeded消息,因为它已是目的地了,那么traceroute如何得知目的地到达了呢?
Traceroute在送出UDP datagrams到目的地时,它所选择送达的port number 是一个一般应用程序都不会用的号码(30000 以上),所以当此UDP datagram 到达目的地后该主机会送回一个「ICMP port unreachable」的消息,而当traceroute 收到这个消息时,便知道目的地已经到达了。所以traceroute 在Server端也是没有所谓的Daemon 程式。
Traceroute提取发 ICMP TTL到期消息设备的IP地址并作域名解析。每次 ,Traceroute都打印出一系列数据,包括所经过的路由设备的域名及 IP地址,三个包每次来回所花时间。
Traceroute 有一个固定的时间等待响应(ICMP TTL到期消息)。如果这个时间过了,它将打印出一系列的*号表明:在这个路径上,这个设备不能在给定的时间内发出ICMP TTL到期消息的响应。然后,Traceroute给TTL记数器加1,继续进行。

TraceRoute.h如下:

#ifndef _ITRACERT_H_
#define _ITRACERT_H_
#pragma pack(1)
//IP数据报头
typedef struct
{unsigned char hdr_len :4;  // length of the headerunsigned char version :4;  // version of IPunsigned char tos;   // type of serviceunsigned short total_len;  // total length of the packetunsigned short identifier;  // unique identifierunsigned short frag_and_flags; // flagsunsigned char ttl;   // time to liveunsigned char protocol;  // protocol (TCP, UDP etc)unsigned short checksum;  // IP checksumunsigned long sourceIP;  // source IP addressunsigned long destIP;   // destination IP address
} IP_HEADER;
//ICMP数据报头
typedef struct
{BYTE type;  //8位类型BYTE code;  //8位代码USHORT cksum;  //16位校验和USHORT id;   //16位标识符USHORT seq;  //16位序列号
} ICMP_HEADER;
//解码结果
typedef struct
{USHORT usSeqNo;   //包序列号DWORD dwRoundTripTime; //往返时间in_addr dwIPaddr;  //对端IP地址
} DECODE_RESULT;
#pragma pack()
//ICMP类型字段
const BYTE ICMP_ECHO_REQUEST = 8; //请求回显
const BYTE ICMP_ECHO_REPLY  = 0; //回显应答
const BYTE ICMP_TIMEOUT   = 11; //传输超时
const DWORD DEF_ICMP_TIMEOUT = 3000; //默认超时时间,单位ms
const int DEF_ICMP_DATA_SIZE = 32; //默认ICMP数据部分长度
const int MAX_ICMP_PACKET_SIZE = 1024; //最大ICMP数据报的大小
const int DEF_MAX_HOP = 30;    //最大跳站数
USHORT GenerateChecksum(USHORT* pBuf, int iSize);
BOOL DecodeIcmpResponse(char* pBuf, int iPacketSize, DECODE_RESULT& stDecodeResult);
#endif // _ITRACERT_H_

TraceRoute.cpp如下:

#include <iostream>
#include <iomanip>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include "TraceRoute.h"
#pragma comment(lib,"ws2_32")
using namespace std;
int main(int argc, char* argv[])
{//检查命令行参数if (argc != 2){cerr << "\nUsage: itracert ip_or_hostname\n";return -1;}//初始化winsock2环境WSADATA wsa;if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0){cerr << "\nFailed to initialize the WinSock2 DLL\n"<< "error code: " << WSAGetLastError() << endl;return -1;}//将命令行参数转换为IP地址u_long ulDestIP = inet_addr(argv[1]);if (ulDestIP == INADDR_NONE){//转换不成功时按域名解析hostent* pHostent = gethostbyname(argv[1]);if (pHostent){ulDestIP = (*(in_addr*)pHostent->h_addr).s_addr;//输出屏幕信息cout << "\nTracing route to " << argv[1] << " [" << inet_ntoa(*(in_addr*)(&ulDestIP)) << "]"<< " with a maximum of " << DEF_MAX_HOP << " hops.\n" << endl;}else //解析主机名失败{cerr << "\nCould not resolve the host name " << argv[1] << '\n'<< "error code: " << WSAGetLastError() << endl;WSACleanup();return -1;}}else{//输出屏幕信息cout << "\nTracing route to " << argv[1] << " with a maximum of " << DEF_MAX_HOP << " hops.\n" << endl;}//填充目的Socket地址sockaddr_in destSockAddr;ZeroMemory(&destSockAddr, sizeof(sockaddr_in));destSockAddr.sin_family = AF_INET;destSockAddr.sin_addr.s_addr = ulDestIP;//使用ICMP协议创建Raw SocketSOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);if (sockRaw == INVALID_SOCKET){cerr << "\nFailed to create a raw socket\n"<< "error code: " << WSAGetLastError() << endl;WSACleanup();return -1;}//设置端口属性int iTimeout = DEF_ICMP_TIMEOUT;if (setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&iTimeout, sizeof(iTimeout)) == SOCKET_ERROR){cerr << "\nFailed to set recv timeout\n"<< "error code: " << WSAGetLastError() << endl;closesocket(sockRaw);WSACleanup();return -1;}//创建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包ICMP_HEADER* pIcmpHeader = (ICMP_HEADER*)IcmpSendBuf;pIcmpHeader->type = ICMP_ECHO_REQUEST;pIcmpHeader->code = 0;pIcmpHeader->id = (USHORT)GetCurrentProcessId();memset(IcmpSendBuf+sizeof(ICMP_HEADER), 'E', DEF_ICMP_DATA_SIZE);//开始探测路由DECODE_RESULT stDecodeResult;BOOL bReachDestHost = FALSE;USHORT usSeqNo = 0;int iTTL = 1;int iMaxHop = DEF_MAX_HOP;while (!bReachDestHost && iMaxHop--){//设置IP数据报头的ttl字段setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char*)&iTTL, sizeof(iTTL));//输出当前跳站数作为路由信息序号cout << setw(3) << iTTL << flush;//填充ICMP数据报剩余字段((ICMP_HEADER*)IcmpSendBuf)->cksum = 0;((ICMP_HEADER*)IcmpSendBuf)->seq = htons(usSeqNo++);((ICMP_HEADER*)IcmpSendBuf)->cksum = GenerateChecksum((USHORT*)IcmpSendBuf, sizeof(ICMP_HEADER)+DEF_ICMP_DATA_SIZE);//记录序列号和当前时间stDecodeResult.usSeqNo = ((ICMP_HEADER*)IcmpSendBuf)->seq;stDecodeResult.dwRoundTripTime = GetTickCount();//发送ICMP的EchoRequest数据报if (sendto(sockRaw, IcmpSendBuf, sizeof(IcmpSendBuf), 0, (sockaddr*)&destSockAddr, sizeof(destSockAddr)) == SOCKET_ERROR){//如果目的主机不可达则直接退出if (WSAGetLastError() == WSAEHOSTUNREACH)cout << '\t'<< "Destination host unreachable.\n"<< "\nTrace complete.\n" << endl;closesocket(sockRaw);WSACleanup();return 0;}//接收ICMP的EchoReply数据报//因为收到的可能并非程序所期待的数据报,所以需要循环接收直到收到所要数据或超时sockaddr_in from;int iFromLen = sizeof(from);int iReadDataLen;while (1){//等待数据到达iReadDataLen = recvfrom(sockRaw, IcmpRecvBuf, MAX_ICMP_PACKET_SIZE, 0, (sockaddr*)&from, &iFromLen);if (iReadDataLen != SOCKET_ERROR) //有数据包到达{//解码得到的数据包,如果解码正确则跳出接收循环发送下一个EchoRequest包if (DecodeIcmpResponse(IcmpRecvBuf, iReadDataLen, stDecodeResult)){if (stDecodeResult.dwIPaddr.s_addr == destSockAddr.sin_addr.s_addr)bReachDestHost = TRUE;cout << '\t' << inet_ntoa(stDecodeResult.dwIPaddr) << endl;break;}}else if (WSAGetLastError() == WSAETIMEDOUT) //接收超时,打印星号{cout << setw(9) << '*' << '\t' << "Request timed out." << endl;break;}else{cerr << "\nFailed to call recvfrom\n"<< "error code: " << WSAGetLastError() << endl;closesocket(sockRaw);WSACleanup();return -1;}}//TTL值加1iTTL++;}//输出屏幕信息cout << "\nTrace complete.\n" << endl;closesocket(sockRaw);WSACleanup();return 0;
}
//产生网际校验和
USHORT GenerateChecksum(USHORT* pBuf, int iSize) 
{unsigned long cksum = 0;while (iSize>1) {cksum += *pBuf++;iSize -= sizeof(USHORT);}if (iSize) cksum += *(UCHAR*)pBuf;cksum = (cksum >> 16) + (cksum & 0xffff);cksum += (cksum >> 16);return (USHORT)(~cksum);
}
//解码得到的数据报
BOOL DecodeIcmpResponse(char* pBuf, int iPacketSize, DECODE_RESULT& stDecodeResult)
{//检查数据报大小的合法性IP_HEADER* pIpHdr = (IP_HEADER*)pBuf;int iIpHdrLen = pIpHdr->hdr_len * 4;if (iPacketSize < (int)(iIpHdrLen+sizeof(ICMP_HEADER)))return FALSE;//按照ICMP包类型检查id字段和序列号以确定是否是程序应接收的Icmp包ICMP_HEADER* pIcmpHdr = (ICMP_HEADER*)(pBuf+iIpHdrLen);USHORT usID, usSquNo;if (pIcmpHdr->type == ICMP_ECHO_REPLY){usID = pIcmpHdr->id;usSquNo = pIcmpHdr->seq;}else if(pIcmpHdr->type == ICMP_TIMEOUT){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;usSquNo = pInnerIcmpHdr->seq;}elsereturn FALSE;if (usID != (USHORT)GetCurrentProcessId() || usSquNo !=stDecodeResult.usSeqNo) return FALSE;//处理正确收到的ICMP数据报if (pIcmpHdr->type == ICMP_ECHO_REPLY ||pIcmpHdr->type == ICMP_TIMEOUT){//返回解码结果stDecodeResult.dwIPaddr.s_addr = pIpHdr->sourceIP;stDecodeResult.dwRoundTripTime = GetTickCount()-stDecodeResult.dwRoundTripTime;//打印屏幕信息if (stDecodeResult.dwRoundTripTime)cout << setw(6) << stDecodeResult.dwRoundTripTime << " ms" << flush;elsecout << setw(6) << "<1" << " ms" << flush;return TRUE;}return FALSE;
}

运行截图1(查看本机到华科网络实验室的路由信息)如下:


运行截图2(查看本机到清华大学网站的路由信息)如下:


运行截图3(查看本机到某互联网公司的DNS服务器的路由信息)如下:



http://chatgpt.dhexx.cn/article/wIlSnrsr.shtml

相关文章

tcp client

Mina 自定义硬件通讯协议框架搭建(TCP Client) 2018.03.04 18:49:29字数 1057阅读 2323 Apache MINA 是一个能够帮助用户开发高性能和高伸缩性网络应用程序的框架。它通过Java nio技术基于TCP/IP和UDP/IP协议提供了抽象的、事件驱动的、异步的API。 使用背景 大三读完,出去…

traceroute的工作原理

MyySophia5个月前 traceroute的工作原理 是利用ICMP差错控制报文中的TTL超时会回向源点发送一个时间超时报文。例如A 主机 traceroute B主机&#xff0c;A会封装一些分组&#xff0c;这些分组很特殊&#xff0c;例如第一个分组的TTL设置为1 &#xff0c;第二个分组的TTL设置为…

TCPTRACE的使用说明

主要的配置项介绍 Listen on Port &#xff1a;本机监听的端口&#xff0c;后面请求服务时使用&#xff1b; Destination Server &#xff1a;目标服务器的地址 Destination Port &#xff1a;目标端口 完成后&#xff0c;在地址栏中输入 http://127.0.0.1:8080(或 http://loca…

tcptrace

http://download.csdn.net/tag/tcptrace TcpTrace 0.8.1.717 http://www.soft82.com/download/windows/tcptrace/ 用tcpTrace查看SOAP请求/应答消息 使用说明 12. 五月 2009 23:17 by 螵蛸in Server技术 // Tags: tcpTrace, SOAP // 评论 (0) 对于开发Web服务应用…

tracert/traceroute原理

一、路由追踪程序traceroute/tracert Traceroute是Linux和Mac OS等系统默认提供的路由追踪小程序&#xff0c;Tracert是Windows系统默认提供的路由追踪小程序。二者的功能相同&#xff0c;都能探测数据包从源地址到目的地址经过的路由器的IP地址。Traceroute/Tracert的实现都借…

tcpTrace的使用

tcpTrace是一款小巧的获取请求报文和响应报文的工具&#xff0c;使用非常简单。 比如我现在有一个服务地址是http://localhost:8080/springmvc/handle.do,那么我们在tcpTrace中的配置如下&#xff1a; listen on port&#xff1a;8081&#xff08;这个端口号可以自己设定&…

Tracetcp/Tcptrace的使用

Tracetcp是一个类似于Tracert的工具&#xff0c;可以直接在命令后加端口进行指定端口测试。 使用Tracetcp要求 1. 安装winpcap &#xff0c; 下载链接&#xff1a;https://www.winpcap.org/install/ 2.下载tracetcp软件&#xff0c;下载链接&#xff1a; https://github.com…

traceroute详解

traceroute详解 1.traceroute基本概念 traceroute (Windows系统下是tracert) 命令利用ICMP 协议定位您的计算机和目标计算机之间的所有路由器。TTL值可以反映数据包经过的路由器或网关的数量&#xff0c;通过操纵独立ICMP呼叫报文的TTL值和观察该报文被抛弃的返回信息&#x…

如何使用TCP Traceroute

与发送UDP或ICMP ECHO数据包的传统跟踪路由不同&#xff0c;TCP跟踪路由使用TCP数据包&#xff0c;因此可以绕过最常见的防火墙过滤器。 请遵循以下说明以运行TCP Traceroute&#xff1a; 对于Windows用户对于Mac用户对于Linux用户 对于Windows用户&#xff0c; Windows没有…

TCP/IP 网络:Traceroute程序

Traceroute是一个用来探索TCP/IP协议的工具&#xff0c;他通过ICMP协议可以让我们看到IP数据报从一台主机传送到另一台主机所经过的所有路由。 使用方法&#xff1a; traceroute [参数] [主机名] windows下命令为 tracert [] [] [-n]:显示的地址是用数字表示而不是符号[-v]…

hdfs创建文件报错 mkdir: Cannot create directory /Flink. Name node is in safe mode.

据资料是说hdfs刚刚启动&#xff0c;还在验证和适配&#xff0c;所以进入安全模式&#xff0c;等一会儿就好了&#xff0c;然后我等了几分钟并没有好 然后找到了解决安全模式的办法&#xff1a; 用户可以通过dfsadmin -safemode value 来操作安全模式&#xff0c;参数value…

network_tcp三次握手

TCP是TCP/IP的传输层控制协议&#xff0c;提供可靠的连接服务&#xff0c;采用三次握手确认建立一个连接: 首先需要了解几个名词&#xff1a;tcp标志位,有6种分别为&#xff1a;SYN(synchronous建立联机) 、ACK(acknowledgement 确认) 、PSH(push传送) 、FIN(finish结束)、 RS…

mysql1396错误

波尔&#xff0c;被控制的电脑通讯端口是多少&#xff1f; 1222nervSNIR&Dnetwork 1239nmsdNMSD 1243Sub-7木马 1245Vodoo 1248hermes 1269MavericksMatrix 1492FTP99CMP(BackOriffice.FTP) 1509StreamingServer 1524ingreslock后门 1313bmc_patroldb 1314pdps 1321pipPIP …

NetFlow

&#xfeff;&#xfeff; NetFlow是一种数据交换方式。Netflow提供网络流量的会话级视图&#xff0c;记录下每个TCP/IP事务的信息。也许它不能象tcpdump那样提供网络流量的完整记录&#xff0c;但是当汇集起来时&#xff0c;它更加易于管理和易读。Netflow由Cisco创造。 工作原…

failed to create network error response from daemon filed to setup ip tables问题

问题 今天在环境上搭建平台&#xff0c;执行docker-compose up -d 报错 Error response from daemon: Failed to Setup IP tables: Unable to enable SKIP DNAT rule: (iptables failed: iptables --wait -t nat -I DOCKER -i br-b649822bbcff -j RETURN: iptables: No chai…

Devtools 热部署

文章目录 前言使用步骤 1.引入库2.配置总结 前言 在实际开发过程中&#xff0c;每次修改代码就得将项目重启&#xff0c;重新部署&#xff0c;对于一些大型应用来说&#xff0c;重启时间需要花费大量的时间成本。对于一个后端开发者来说&#xff0c;重启过程确实很难受啊 一、使…

内网渗透的那些net命令|Net config|Net

Net命令 Net命令是一个命令行命令,Net命令有很多函数用于实用和核算计算机之间的NetBIOS连接,可以查看我们的管理网络环境,服务,用户,登陆等信息内容 Net使用方法 显示当前域的计算机列表net view 查看指定计算机的共享资源列表net view \test 查看共享的资源net share 查看…

failed to load response dataRequest content was evicted from inspector cache

在项目中&#xff0c;我用谷歌浏览器查看后台返回的json数据&#xff0c;但是发现前端页面已经接收成功&#xff0c;并且渲染了对应json数据了&#xff0c;但是network里面的response却报错&#xff1a; 调整对应json数据后发现&#xff0c;当后台返回前端的数据超过了一定大…

火狐浏览器提示响应已被截断(有效解决)

产生问题如下&#xff1a;JSON传递数据超过1M 解决方案&#xff1a; 第一步&#xff1a;地址栏输入about:config 第二步&#xff1a;devtools.netmonitor.responseBodyLimit 改为0&#xff0c;相当于禁用大小限制&#xff0c;保存之后即可。

failed to load response data:Request content was evicted from inspector cache

在项目中&#xff0c;我用谷歌浏览器查看后台返回的json数据&#xff0c;但是发现前端页面已经接收成功&#xff0c;并且渲染了对应json数据了&#xff0c;但是network里面的response却报错&#xff1a; 调整对应json数据后发现&#xff0c;当后台返回前端的数据超过了一定大…