93-接收 ICMP 报文

本文介绍如何通过编写程序解析ICMP报文。通过创建原始套接字接收IP数据报并筛选出ICMP协议的数据包,进而提取并解析ICMP报文。文中还通过ping命令产生的ICMP报文作为实例进行解析。

如果你练习过前面的接收 IP 数据报的程序,相信写出这个不会很难。

1. 程序路径

本文使用的程序托管在 gitos 上:http://git.oschina.net/ivan_allen/unp

如果你已经 clone 过这个代码了,请使用 git pull 更新一下。本节程序所使用的程序路径是 unp/program/icmp/basic.

2. 程序编写思路

因为这一次我们只想要 ICMP 协议,因此可以只接收 IP 数据报中协议字段为 1 的 IP 数据报。具体就是创建下面这样的原始套接字:

// IPPROTO_ICMP 的宏定义值就是 1
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

通过编写获取 IP 数据报的实验我们已经掌握了拿到了 IP 数据报的方法,接下来,就是从 IP 数据报中拿出它的数据部分。

// 假设我们拿到的 ip 数据报保存在指针 struct ip *ip 中,长度为 len.


// 计算 ip 首部长度
iphlen = ip->ip_hl << 2;

// 计算 ip 数据报中数据起始地址,ip 首部地址 + ip 首部长度:
char *buf = (char*)ip + iphlen;

// buf 的地址就是 ICMP 报文的首地址,强制转换到 struct icmp* 类型中
struct icmp *icmp = (struct icmp*)buf;

// 这样一来我们就拿到了 icmp 报文了,后面该打印的就打印去吧。

3. 实验

为了能产生 ICMP 报文,我在 flower 主机上使用 ping 命令发送了两个报文。


这里写图片描述
图1 运行结果

ping 命令发送的每一个 ICMP 报文大小都是 64 字节,这 64 字节表示 ICMP 首部长度 + ICMP 数据部分的长度。从图 1 中可以看到,数据部分是 60 字节(每行 16 字节),加上 type+code+checksum 一共 4 字节,刚好 64 字节。

实际上,ping 命令发送的报文是请求回显 ICMP 报文,细心的同学可以观察到图 1 中的 type 字段的值是 8,code 字段的值是 0,根据 ICMP 协议文档描述,这是一个请求回显报文。

下一篇文章会详细讨论请求回显报文是干嘛的。

4. 总结

  • 掌握解析 ICMP 报文的方法
<think>嗯,用户问的是如何通过Socket接收ICMP报文。首先,我需要回忆一下C#中Socket编程的相关知识,特别是关于ICMP的部分。ICMP是网络层协议,通常用于传递控制消息,比如ping命令就是用的ICMP Echo请求和响应。在C#中,要接收ICMP报文,需要使用原始套接字(Raw Socket),因为ICMP不像TCP或UDP那样有高层API支持。 首先,创建Socket的时候,应该选择正确的协议类型。ICMP属于ProtocolType.Icmp,所以在Socket的构造函数里要指定SocketType.Raw和ProtocolType.Icmp。不过,可能需要注意权限问题,因为在某些操作系统上,创建原始套接字需要管理员权限。比如在Windows上,普通用户可能无法直接创建Raw Socket,需要以管理员身份运行程序。 接下来,绑定到本地终结点。这里应该使用IPAddress.Any表示接收所有网络接口的数据,端口号对于ICMP来说其实是没有的,但ICMP报文并不使用端口,所以这里可能随便指定一个端口,或者直接使用0,不过实际代码中可能需要更准确的处理。 然后,接收数据。使用Socket的Receive方法,接收到的字节数组需要解析。ICMP报文的结构需要了解,通常包括类型、代码、校验和等字段。比如,类型8是Echo请求,0是Echo响应。所以解析的时候需要按照ICMP报文结构来读取相应的字段。 在代码示例中,用户可能需要注意异常处理,比如权限不足的问题,这时候应该捕获SocketException,并提示需要管理员权限。另外,接收的数据缓冲区要足够大,比如4096字节,以容纳可能的ICMP报文。 另外,ICMP报文IP层的一部分,所以接收到的数据可能包含IP头。因此,在解析ICMP报文时,可能需要先跳过IP头。IP头的长度通常是20字节,但可能会有选项字段,所以需要根据IP头的IHL字段来计算实际长度。这可能增加了代码的复杂性,但示例中可能为了简化,假设IP头是20字节,直接跳过。 还有,校验和的计算可能需要验证,但在示例中可能被省略了,因为用户可能只需要接收并显示基本信息。此外,处理接收到的数据时,需要注意字节顺序(网络字节序为主机字节序的转换),比如校验和字段可能需要使用NetworkToHostOrder方法转换。 最后,用户可能需要一个无限循环来持续接收报文,所以代码中使用while(true)循环,并在接收到数据后解析并输出相关信息。 可能相关的问题包括如何发送ICMP报文、如何处理其他协议如TCP/UDP、原始套接字的使用限制,以及如何解析IP头等。这些都是用户在实现ICMP接收后可能遇到的后续问题。</think><< 在C#中接收ICMP报文需要借助`Socket`类并配置为原始套接字模式。以下是实现代码和分步解释: ```csharp using System; using System.Net; using System.Net.Sockets; using System.Text; class IcmpReceiver { public static void Main() { try { // 创建原始套接字(需要管理员权限) Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp); // 绑定到本地所有IP地址 socket.Bind(new IPEndPoint(IPAddress.Any, 0)); // 接收缓冲区 byte[] buffer = new byte[4096]; while (true) { // 接收ICMP数据包 int bytesReceived = socket.Receive(buffer); // 解析IP头(前20字节) int ipHeaderLength = (buffer[0] & 0x0F) * 4; // 计算实际IP头长度 int icmpStartIndex = ipHeaderLength; // ICMP数据开始位置 // 解析ICMPIcmpPacket packet = ParseIcmpPacket(buffer, icmpStartIndex, bytesReceived); Console.WriteLine($"Type: {packet.Type}"); Console.WriteLine($"Code: {packet.Code}"); Console.WriteLine($"Checksum: {packet.Checksum}"); Console.WriteLine($"Data: {Encoding.ASCII.GetString(packet.Data)}"); } } catch (SocketException ex) { Console.WriteLine($"需要管理员权限!错误:{ex.SocketErrorCode}"); } } private static IcmpPacket ParseIcmpPacket(byte[] buffer, int start, int length) { return new IcmpPacket { Type = buffer[start], Code = buffer[start + 1], Checksum = BitConverter.ToUInt16(buffer, start + 2), Data = new ArraySegment<byte>(buffer, start + 4, length - start - 4).ToArray() }; } } public class IcmpPacket { public byte Type { get; set; } // ICMP类型(8=请求,0=应答) public byte Code { get; set; } // 子类型代码 public ushort Checksum { get; set; } public byte[] Data { get; set; } // 载荷数据 } ``` **实现原理:** 1. 需要创建原始套接字(SocketType.Raw)并指定ProtocolType.Icmp 2. 必须绑定到IPAddress.Any以接收所有网络接口的流量 3. 接收到的数据包含IP头(前20字节)和ICMP报文 4. 通过解析第二个字节的低4位(buffer[0] & 0x0F)计算实际IP头长度 5. ICMP报文格式: - 字节0:类型(Type) - 字节1:代码(Code) - 字节2-3:校验和(Checksum) - 字节4+:数据载荷 **注意:** - 需要以管理员权限运行(Windows系统限制原始套接字访问) - ICMP不基于端口,因此绑定时的端口参数无实际意义 - 接收到的数据包含IP头,需要正确跳过IP头才能解析ICMP报文 - 校验和验证需要自行实现(示例未包含)
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值