搜索
您的当前位置:首页编程实现发送ICMP协议数据包课案

编程实现发送ICMP协议数据包课案

来源:世旅网


ICMP原理与代码及分析

专业: 班级: 学号: 姓名:

一、 ICMP协议简介

ICMP全称Internet Control Message Protocol,中文名为因特网控制报文协议。它工作在OSI的网络层,向数据通讯中的源主机报告错误。ICMP可以实现故障隔离和故障恢复。网络本身是不可靠的,在网络传输过程中,可能会发生许多突发事件并导致数据传输失败。网络层的IP协议是一个无连接的协议,它不会处理网络层传输中的故障,而位于网络层的ICMP协议却恰好弥补了IP的缺限,它使用IP协议进行信息传递,向数据包中的源端节点提供发生在网络层的错误信息反馈。ICMP的报头长8字节,结构如图1所示。

比特0 7 8 15 16 比特31

类型(0或8) 代码(0) 首部其余部分(未使用) 数据 (图1 ICMP报头结构)

    

类型:标识生成的错误报文,它是ICMP报文中的第一个字段;

代码:进一步地限定生成ICMP报文。该字段用来查找产生错误的原因; 校验和:存储了ICMP所使用的校验和值。 未使用:保留字段,供将来使用,起值设为0

数据:包含了所有接受到的数据报的IP报头。还包含IP数据报中前8个字节的数据;

检验和 ICMP协议提供的诊断报文类型如表1所示。

种类 差错报文 类型 3 4 11 12 5 查询报文 8或0 13或14 17或18 10或9 描述 目的端不可达 源点抑制 超时 参数问题 改变路由 回显请求或应答 时间戳请求或应答 地址掩码请求或应答 路由询问和通告 (表1 ICMP诊断报文类型)

ICMP提供多种类型的消息为源端节点提供网络层的故障信息反馈,它的报文类型可以归纳为以下5个大类:

 诊断报文(类型8,代码0;类型0,代码0);  目的不可达报文(类型3,代码0-15);  重定向报文(类型5,代码0-4);

 超时报文(类型11,代码0-1);  信息报文(类型12-18)。

二、编程实现发送ICMP协议数据包

代码功能:实现发送ICMP协议数据包,从而实现DOS下ping命令功能。

1.代码简要分析

代码执行步骤:

(1) 创建协议类型为IPPROTO_ICMP的原始套接字,设置套接字的属性。 (2) 创建并初始化ICMP封包。

(3) 调用sendto函数向远程主机发送ICMP的请求。 (4) 调用recvfrom函数接受ICMP响应。 初始化ICMP头时先初始化消息的类型和代码域,之后应该是回显请求头。程序首先定义了ICMP头的数据结构ICMP_HDR。

typedef struct icmp_hdr {

unsigned char icmp_type; // 消息类型 unsigned char icmp_code; // 代码 unsigned short icmp_checksum; // 校验和 // 下面是回显头

unsigned short icmp_id; // 用来惟一标识此请求的ID号,通常设置为进程ID unsigned short icmp_sequence; // 序列号 unsigned long icmp_timestamp; // 时间戳 } ICMP_HDR, *PICMP_HDR;

2.完整代码及解析

#include \"../common/initsock.h\" #include \"../common/protoinfo.h\" #include \"../common/comm.h\" #include CInitSock theSock;

typedef struct icmp_hdr {

unsigned char icmp_type; // 消息类型 unsigned char icmp_code; // 代码 unsigned short icmp_checksum; // 校验和 // 下面是回显头

unsigned short icmp_id; // 用来惟一标识此请求的ID号,通常设置为进程ID unsigned short icmp_sequence; // 序列号 unsigned long icmp_timestamp; // 时间戳

} ICMP_HDR, *PICMP_HDR; int main() {

// 目的IP地址,即要Ping的IP地址

char szDestIp[] = \"192.168.1.104\"; // 127.0.0.1 // 创建原始套节字

SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); // 设置接收超时

SetTimeout(sRaw, 1000, TRUE); // 设置目的地址

SOCKADDR_IN dest;

dest.sin_family = AF_INET; dest.sin_port = htons(0);

dest.sin_addr.S_un.S_addr = inet_addr(szDestIp); // 创建ICMP封包

char buff[sizeof(ICMP_HDR) + 32];

ICMP_HDR* pIcmp = (ICMP_HDR*)buff; // 填写ICMP封包数据

pIcmp->icmp_type = 8; // 请求一个ICMP回显 pIcmp->icmp_code = 0;

pIcmp->icmp_id = (USHORT)::GetCurrentProcessId(); pIcmp->icmp_checksum = 0; pIcmp->icmp_sequence = 0; // 填充数据部分,可以为任意

memset(&buff[sizeof(ICMP_HDR)], 'E', 32); // 开始发送和接收ICMP封包

USHORT nSeq = 0; char recvBuf[1024]; SOCKADDR_IN from; int nLen = sizeof(from); while(TRUE) { static int nCount = 0; int nRet; if(nCount++ == 4) break; pIcmp->icmp_checksum = 0; pIcmp->icmp_timestamp = ::GetTickCount(); pIcmp->icmp_sequence = nSeq++; pIcmp->icmp_checksum = checksum((USHORT*)buff, sizeof(ICMP_HDR) + 32); nRet = ::sendto(sRaw, buff, sizeof(ICMP_HDR) + 32, 0, (SOCKADDR *)&dest, sizeof(dest)); if(nRet == SOCKET_ERROR) { printf(\" sendto() failed: %d \\n\ return -1; }

nRet = ::recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen); if(nRet == SOCKET_ERROR) { if(::WSAGetLastError() == WSAETIMEDOUT) { printf(\" timed out\\n\"); continue; } printf(\" recvfrom() failed: %d\\n\ return -1; }

// 下面开始解析接收到的ICMP封包

int nTick = ::GetTickCount();

if(nRet < sizeof(IPHeader) + sizeof(ICMP_HDR)) { printf(\" Too few bytes from %s \\n\}

// 接收到的数据中包含IP头,IP头大小为20个字节,所以加20得到ICMP头

ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + 20); // (ICMP_HDR*)(recvBuf + sizeof(IPHeader)); if(pRecvIcmp->icmp_type != 0) // 回显 { printf(\" nonecho type %d recvd \\n\ return -1; } if(pRecvIcmp->icmp_id != ::GetCurrentProcessId()) { printf(\" someone else's packet! \\n\"); return -1; } printf(\" %d bytes from %s:\ printf(\" icmp_seq = %d. \ printf(\" time: %d ms\ printf(\" \\n\"); ::Sleep(1000); }

return 0; }

三、代码结果分析

运行代码结果:(源地址:192.168.1.101 目的地址:192.168.1.104)

DOS下使用ping命令 ping 192.168.1.104结果:(源地址:192.168.1.101 目的地址:192.168.1.104)

由图可以分析出代码实现功能与ping命令功能类似。即代码会构建一个固定格式的ICMP请求数据包,然后由ICMP协议将这个数据包连同地址“192.168.1.104”一起交给IP层协议,IP层协议将以地址“192.168.1.104”作为目的地址,本机IP地址192.168.1.101作为源地址,加上一些其他的控制信息,构建一个IP数据包,并想办法得到192.168.1.104的MAC地址(物理地址,这是数据链路层协议构建数据链路层的传输单元——帧所必需的),以便交给数据链路层构建一个数据帧。关键就在这里,IP层协议通过机器B的IP地址和自己的子网掩码,发现它跟自己属同一网络,就直接在本网络内查找这台机器的MAC,如果以前两机有过通信,在A机的ARP缓存表应该有B机IP与其MAC的映射关系,如果没有,就发一个ARP请求广播,得到B机的MAC,一并交给数据链路层。后者构建一个数据帧,目的地址是IP层传过来的物理地址,源地址则是本机的物理地址,还要附加上一些控制信息,依据以太网的介质访问规则,将它们传送出去。

主机B收到这个数据帧后,先检查它的目的地址,并和本机的物理地址对比,如符合,则接收;否则丢弃。接收后检查该数据帧,将IP数据包从帧中提取出来,交给本机的IP层协议。同样,IP层检查后,将有用的信息提取后交给ICMP协议,后者处理后,马上构建一个ICMP应答包,发送给主机A,其过程和主机A发送ICMP请求包到主机B一模一样。 由wireshark抓包,我们可以更清楚的看到两者的相似处。

Wireshark抓到代码的数据包:(源地址:192.168.1.101 目的地址:192.168.1.104)

Wireshark抓到ping命令的数据包:(源地址:192.168.1.101 目的地址:192.168.1.104)

代码和ping命令都使用ICMP协议,并且都是源地址请求一次,目的地址回应一次。而且wireshark判断两者都实现ping功能。 如果是目的网络与主机网络不通 代码结果:

Ping命令结果:

因篇幅问题不能全部显示,请点此查看更多更全内容

Top