定量预测方法有哪些:TraceRoute实现原理

来源:百度文库 编辑:中财网 时间:2024/05/03 05:31:17

   TraceRoute实现原理 http://blog.csdn.net/microtong/archive/2008/11/04/3220450.aspx

本程序实现Windows下tracert的功能,程序使用原始套接字发送ICMP回显请求报文,本接收ICMP超时差错报文,和ICMP回显应答报文,得到每一跳的路由器IP地址和往返时间。

  1. //TraceRoute.h
  2. #ifndef _ITRACERT_H_
  3. #define _ITRACERT_H_
  4. #pragma pack(1)
  5. //IP数据报头
  6. typedef struct
  7. {
  8. unsigned char hdr_len :4; // length of the header
  9. unsigned char version :4; // version of IP
  10. unsigned char tos; // type of service
  11. unsigned short total_len; // total length of the packet
  12. unsigned short identifier; // unique identifier
  13. unsigned short frag_and_flags; // flags
  14. unsigned char ttl; // time to live
  15. unsigned char protocol; // protocol (TCP, UDP etc)
  16. unsigned short checksum; // IP checksum
  17. unsigned long sourceIP; // source IP address
  18. unsigned long destIP; // destination IP address
  19. } IP_HEADER;
  20. //ICMP数据报头
  21. typedef struct
  22. {
  23. BYTE type; //8位类型
  24. BYTE code; //8位代码
  25. USHORT cksum; //16位校验和
  26. USHORT id; //16位标识符
  27. USHORT seq; //16位序列号
  28. } ICMP_HEADER;
  29. //解码结果
  30. typedef struct
  31. {
  32. USHORT usSeqNo; //包序列号
  33. DWORD dwRoundTripTime; //往返时间
  34. in_addr dwIPaddr; //对端IP地址
  35. } DECODE_RESULT;
  36. #pragma pack()
  37. //ICMP类型字段
  38. const BYTE ICMP_ECHO_REQUEST = 8; //请求回显
  39. const BYTE ICMP_ECHO_REPLY = 0; //回显应答
  40. const BYTE ICMP_TIMEOUT = 11; //传输超时
  41. const DWORD DEF_ICMP_TIMEOUT = 3000; //默认超时时间,单位ms
  42. const int DEF_ICMP_DATA_SIZE = 32; //默认ICMP数据部分长度
  43. const int MAX_ICMP_PACKET_SIZE = 1024; //最大ICMP数据报的大小
  44. const int DEF_MAX_HOP = 30; //最大跳站数
  45. USHORT GenerateChecksum(USHORT* pBuf, int iSize);
  46. BOOL DecodeIcmpResponse(char* pBuf, int iPacketSize, DECODE_RESULT& stDecodeResult);
  47. #endif // _ITRACERT_H_
  48. //TraceRoute.cpp
  49. /*----------------------------------------------------------
  50. 功能说明:该程序简单实现了Windows操作系统的tracert命令功能,
  51. 可以输出IP报文从本机出发到达目的主机所经过的路由信息。
  52. -----------------------------------------------------------*/
  53. #include
  54. #include
  55. #include
  56. #include
  57. #include
  58. #include "TraceRoute.h"
  59. #pragma comment(lib,"ws2_32")
  60. using namespace std;
  61. int main(int argc, char* argv[])
  62. {
  63. //检查命令行参数
  64. if (argc != 2)
  65. {
  66. cerr << "/nUsage: itracert ip_or_hostname/n";
  67. return -1;
  68. }
  69. //初始化winsock2环境
  70. WSADATA wsa;
  71. if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
  72. {
  73. cerr << "/nFailed to initialize the WinSock2 DLL/n"
  74. << "error code: " << WSAGetLastError() << endl;
  75. return -1;
  76. }
  77. //将命令行参数转换为IP地址
  78. u_long ulDestIP = inet_addr(argv[1]);
  79. if (ulDestIP == INADDR_NONE)
  80. {
  81. //转换不成功时按域名解析
  82. hostent* pHostent = gethostbyname(argv[1]);
  83. if (pHostent)
  84. {
  85. ulDestIP = (*(in_addr*)pHostent->h_addr).s_addr;
  86. //输出屏幕信息
  87. cout << "/nTracing route to " << argv[1]
  88. << " [" << inet_ntoa(*(in_addr*)(&ulDestIP)) << "]"
  89. << " with a maximum of " << DEF_MAX_HOP << " hops./n" << endl;
  90. }
  91. else //解析主机名失败
  92. {
  93. cerr << "/nCould not resolve the host name " << argv[1] << '/n'
  94. << "error code: " << WSAGetLastError() << endl;
  95. WSACleanup();
  96. return -1;
  97. }
  98. }
  99. else
  100. {
  101. //输出屏幕信息
  102. cout << "/nTracing route to " << argv[1]
  103. << " with a maximum of " << DEF_MAX_HOP << " hops./n" << endl;
  104. }
  105. //填充目的Socket地址
  106. sockaddr_in destSockAddr;
  107. ZeroMemory(&destSockAddr, sizeof(sockaddr_in));
  108. destSockAddr.sin_family = AF_INET;
  109. destSockAddr.sin_addr.s_addr = ulDestIP;
  110. //使用ICMP协议创建Raw Socket
  111. SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);
  112. if (sockRaw == INVALID_SOCKET)
  113. {
  114. cerr << "/nFailed to create a raw socket/n"
  115. << "error code: " << WSAGetLastError() << endl;
  116. WSACleanup();
  117. return -1;
  118. }
  119. //设置端口属性
  120. int iTimeout = DEF_ICMP_TIMEOUT;
  121. if (setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&iTimeout, sizeof(iTimeout)) == SOCKET_ERROR)
  122. {
  123. cerr << "/nFailed to set recv timeout/n"
  124. << "error code: " << WSAGetLastError() << endl;
  125. closesocket(sockRaw);
  126. WSACleanup();
  127. return -1;
  128. }
  129. //创建ICMP包发送缓冲区和接收缓冲区
  130. char IcmpSendBuf[sizeof(ICMP_HEADER)+DEF_ICMP_DATA_SIZE];
  131. memset(IcmpSendBuf, 0, sizeof(IcmpSendBuf));
  132. char IcmpRecvBuf[MAX_ICMP_PACKET_SIZE];
  133. memset(IcmpRecvBuf, 0, sizeof(IcmpRecvBuf));
  134. //填充待发送的ICMP包
  135. ICMP_HEADER* pIcmpHeader = (ICMP_HEADER*)IcmpSendBuf;
  136. pIcmpHeader->type = ICMP_ECHO_REQUEST;
  137. pIcmpHeader->code = 0;
  138. pIcmpHeader->id = (USHORT)GetCurrentProcessId();
  139. memset(IcmpSendBuf+sizeof(ICMP_HEADER), 'E', DEF_ICMP_DATA_SIZE);
  140. //开始探测路由
  141. DECODE_RESULT stDecodeResult;
  142. BOOL bReachDestHost = FALSE;
  143. USHORT usSeqNo = 0;
  144. int iTTL = 1;
  145. int iMaxHop = DEF_MAX_HOP;
  146. while (!bReachDestHost && iMaxHop--)
  147. {
  148. //设置IP数据报头的ttl字段
  149. setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char*)&iTTL, sizeof(iTTL));
  150. //输出当前跳站数作为路由信息序号
  151. cout << setw(3) << iTTL << flush;
  152. //填充ICMP数据报剩余字段
  153. ((ICMP_HEADER*)IcmpSendBuf)->cksum = 0;
  154. ((ICMP_HEADER*)IcmpSendBuf)->seq = htons(usSeqNo++);
  155. ((ICMP_HEADER*)IcmpSendBuf)->cksum = GenerateChecksum((USHORT*)IcmpSendBuf, sizeof(ICMP_HEADER)+DEF_ICMP_DATA_SIZE);
  156. //记录序列号和当前时间
  157. stDecodeResult.usSeqNo = ((ICMP_HEADER*)IcmpSendBuf)->seq;
  158. stDecodeResult.dwRoundTripTime = GetTickCount();
  159. //发送ICMP的EchoRequest数据报
  160. if (sendto(sockRaw, IcmpSendBuf, sizeof(IcmpSendBuf), 0,
  161. (sockaddr*)&destSockAddr, sizeof(destSockAddr)) == SOCKET_ERROR)
  162. {
  163. //如果目的主机不可达则直接退出
  164. if (WSAGetLastError() == WSAEHOSTUNREACH)
  165. cout << '/t' << "Destination host unreachable./n"
  166. << "/nTrace complete./n" << endl;
  167. closesocket(sockRaw);
  168. WSACleanup();
  169. return 0;
  170. }
  171. //接收ICMP的EchoReply数据报
  172. //因为收到的可能并非程序所期待的数据报,所以需要循环接收直到收到所要数据或超时
  173. sockaddr_in from;
  174. int iFromLen = sizeof(from);
  175. int iReadDataLen;
  176. while (1)
  177. {
  178. //等待数据到达
  179. iReadDataLen = recvfrom(sockRaw, IcmpRecvBuf, MAX_ICMP_PACKET_SIZE,
  180. 0, (sockaddr*)&from, &iFromLen);
  181. if (iReadDataLen != SOCKET_ERROR) //有数据包到达
  182. {
  183. //解码得到的数据包,如果解码正确则跳出接收循环发送下一个EchoRequest包
  184. if (DecodeIcmpResponse(IcmpRecvBuf, iReadDataLen, stDecodeResult))
  185. {
  186. if (stDecodeResult.dwIPaddr.s_addr == destSockAddr.sin_addr.s_addr)
  187. bReachDestHost = TRUE;
  188. cout << '/t' << inet_ntoa(stDecodeResult.dwIPaddr) << endl;
  189. break;
  190. }
  191. }
  192. else if (WSAGetLastError() == WSAETIMEDOUT) //接收超时,打印星号
  193. {
  194. cout << setw(9) << '*' << '/t' << "Request timed out." << endl;
  195. break;
  196. }
  197. else
  198. {
  199. cerr << "/nFailed to call recvfrom/n"
  200. << "error code: " << WSAGetLastError() << endl;
  201. closesocket(sockRaw);
  202. WSACleanup();
  203. return -1;
  204. }
  205. }
  206. //TTL值加1
  207. iTTL++;
  208. }
  209. //输出屏幕信息
  210. cout << "/nTrace complete./n" << endl;
  211. closesocket(sockRaw);
  212. WSACleanup();
  213. return 0;
  214. }
  215. //产生网际校验和
  216. USHORT GenerateChecksum(USHORT* pBuf, int iSize)
  217. {
  218. unsigned long cksum = 0;
  219. while (iSize>1)
  220. {
  221. cksum += *pBuf++;
  222. iSize -= sizeof(USHORT);
  223. }
  224. if (iSize)
  225. cksum += *(UCHAR*)pBuf;
  226. cksum = (cksum >> 16) + (cksum & 0xffff);
  227. cksum += (cksum >> 16);
  228. return (USHORT)(~cksum);
  229. }
  230. //解码得到的数据报
  231. BOOL DecodeIcmpResponse(char* pBuf, int iPacketSize, DECODE_RESULT& stDecodeResult)
  232. {
  233. //检查数据报大小的合法性
  234. IP_HEADER* pIpHdr = (IP_HEADER*)pBuf;
  235. int iIpHdrLen = pIpHdr->hdr_len * 4;
  236. if (iPacketSize < (int)(iIpHdrLen+sizeof(ICMP_HEADER)))
  237. return FALSE;
  238. //按照ICMP包类型检查id字段和序列号以确定是否是程序应接收的Icmp包
  239. ICMP_HEADER* pIcmpHdr = (ICMP_HEADER*)(pBuf+iIpHdrLen);
  240. USHORT usID, usSquNo;
  241. if (pIcmpHdr->type == ICMP_ECHO_REPLY)
  242. {
  243. usID = pIcmpHdr->id;
  244. usSquNo = pIcmpHdr->seq;
  245. }
  246. else if(pIcmpHdr->type == ICMP_TIMEOUT)
  247. {
  248. char* pInnerIpHdr = pBuf+iIpHdrLen+sizeof(ICMP_HEADER); //载荷中的IP头
  249. int iInnerIPHdrLen = ((IP_HEADER*)pInnerIpHdr)->hdr_len * 4;//载荷中的IP头长
  250. ICMP_HEADER* pInnerIcmpHdr = (ICMP_HEADER*)(pInnerIpHdr+iInnerIPHdrLen);//载荷中的ICMP头
  251. usID = pInnerIcmpHdr->id;
  252. usSquNo = pInnerIcmpHdr->seq;
  253. }
  254. else
  255. return FALSE;
  256. if (usID != (USHORT)GetCurrentProcessId() || usSquNo !=stDecodeResult.usSeqNo)
  257. return FALSE;
  258. //处理正确收到的ICMP数据报
  259. if (pIcmpHdr->type == ICMP_ECHO_REPLY ||
  260. pIcmpHdr->type == ICMP_TIMEOUT)
  261. {
  262. //返回解码结果
  263. stDecodeResult.dwIPaddr.s_addr = pIpHdr->sourceIP;
  264. stDecodeResult.dwRoundTripTime = GetTickCount()-stDecodeResult.dwRoundTripTime;
  265. //打印屏幕信息
  266. if (stDecodeResult.dwRoundTripTime)
  267. cout << setw(6) << stDecodeResult.dwRoundTripTime << " ms" << flush;
  268. else
  269. cout << setw(6) << "<1" << " ms" << flush;
  270. return TRUE;
  271. }
  272. return FALSE;
  273. }