ICMP协议实现
提供了ICMP(Internet控制消息协议)Socket的实现, 支持Ping和Traceroute等网络诊断功能。
遵循的国际标准
本实现严格遵循以下网络诊断协议与相关标准规范:
ICMP 核心协议规范:
ICMP 扩展与更新:
ICMP 参数注册:
IPv4 协议标准:
ICMP 消息类型
根据 RFC 792 和 IANA 注册表,ICMP 消息类型分类如下:
| 类型值 | 名称 | 用途 | 本实现支持 |
| 0 | Echo Reply | Ping 响应 | ✓ |
| 3 | Destination Unreachable | 目标不可达 | - |
| 5 | Redirect | 路由重定向 | - |
| 8 | Echo Request | Ping 请求 | ✓ |
| 11 | Time Exceeded | TTL 超时(Traceroute 依赖) | ✓ |
| 13 | Timestamp | 时间戳请求 | - |
| 14 | Timestamp Reply | 时间戳响应 | - |
ICMP 头部格式
根据 RFC 792 §2,ICMP 头部由以下字段组成:
| 字段 | 大小 | 说明 |
| Type | 8 位 | 消息类型(0=Echo Reply, 8=Echo Request, 11=TTL Exceeded) |
| Code | 8 位 | 消息子类型(通常为 0) |
| Checksum | 16 位 | 整个 ICMP 消息的校验和(RFC 1071 算法) |
| ID | 16 位 | 标识符,用于匹配请求和响应 |
| Sequence | 16 位 | 序列号,用于匹配请求和响应 |
**Echo Request/Reply 消息**(RFC 792 §3.2):
- 数据部分可选,内容任意
- 响应的标识符和序列号必须与请求匹配
- 数据部分原样返回
**Time Exceeded 消息**(RFC 792 §3.3):
- Code 0:TTL 在传输中耗尽
- Code 1:分片重组超时
- 包含原始数据包的 IP 头部和前 8 字节
校验和算法
根据 RFC 1071,ICMP 校验和计算步骤:
- 将校验和字段清零
- 将数据按 16 位字累加
- 将进位加到低 16 位
- 结果取反码
Ping 操作原理
Ping 使用 ICMP Echo Request/Reply 消息测试网络连通性:
**工作流程**:
- 发送方构造 ICMP Echo Request(Type=8),包含 ID 和序列号
- 目标主机收到后返回 ICMP Echo Reply(Type=0)
- 发送方根据 ID 和序列号匹配响应
- 计算往返时间(RTT = 接收时间 - 发送时间)
**测量指标**:
| 指标 | 说明 |
| RTT | 往返时间(毫秒) |
| Reply TTL | 响应数据包的剩余 TTL 值 |
| Reply Size | 响应数据大小(字节) |
Traceroute 操作原理
Traceroute 利用 IP TTL 和 ICMP Time Exceeded 探测网络路径:
**工作流程**:
- 发送 TTL=1 的 UDP 或 ICMP Echo Request 数据包
- 第一跳路由器丢弃数据包(TTL=0),返回 ICMP Time Exceeded(Type=11)
- 发送方从 Time Exceeded 消息中提取路由器 IP 地址
- 逐步增加 TTL(2, 3, ...),重复以上步骤
- 当数据包到达目标主机时,返回 ICMP Echo Reply(Type=0)或 UDP Port Unreachable
**跳点信息**:
| 字段 | 说明 |
| address | 该跳路由器的 IP 地址 |
| rtt[3] | 三次探测的往返时间 |
| reached | 是否已到达目标 |
使用示例
Ping 操作:
if (dest) {
if (result.success) {
println(
"{}: bytes={} time={}ms TTL={}",
dest->to_string(), result.reply_size,
result.rtt.count(), result.reply_ttl);
}
}
ping_result ping(const ip_address &dest, milliseconds timeout, uint16_t sequence=0, const void *data=nullptr, size_t data_len=0)
执行Ping操作
static optional< ip_address > parse(const string &host, ports port=ports{}) noexcept
从字符串解析IP地址
void println(Args &&... args)
打印多个值并换行
duration< int64_t, milli > milliseconds
毫秒持续时间
Traceroute 操作:
for (size_t i = 0; i < hops.size(); ++i) {
println(
"{}: {} (reached={})", i + 1,
hops[i].address.is_valid() ? hops[i].address.to_string() : "*",
hops[i].reached);
}
vector< traceroute_hop > traceroute(const ip_address &dest, int max_hops=30, milliseconds probe_timeout=milliseconds(1000), int probes_per_hop=3)
执行Traceroute操作
- 注解
- Ping 和 Traceroute 是网络诊断中最常用的工具, Ping 用于测试连通性和延迟,Traceroute 用于分析网络路径。
- 警告
- 原始套接字操作需要 root/管理员权限。 某些网络环境可能过滤 ICMP 数据包,导致 Ping/Traceroute 结果不准确。 Traceroute 的中间路由器可能不响应 ICMP Time Exceeded(显示为 *)。
- 参见
- https://www.rfc-editor.org/rfc/rfc792.html
-
https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
-
https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol
-
https://en.wikipedia.org/wiki/Ping_(networking_utility)
-
https://en.wikipedia.org/wiki/Traceroute