You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Ruby中查看UDP消息对应的ICMP错误报文?

Ruby 非root权限下捕获ICMP错误实现Traceroute

针对你遇到的recvmsg(Socket::MSG_ERRQUEUE)阻塞但Wireshark能抓到ICMP包的问题,可以从以下几个方向排查和修复:

1. 确认套接字错误接收选项的正确配置

确保针对IPv4/IPv6设置了对应的错误接收选项:

  • IPv4配置:
    sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_RECVERR, 1)
    
  • IPv6配置:
    sock.setsockopt(Socket::IPPROTO_IPV6, Socket::IPV6_RECVERR, 1)
    

注意必须在对应协议层级设置,不能混用IPPROTO_IP和IPV6_RECVERR。

2. 给套接字添加超时,避免永久阻塞

默认recvmsg是阻塞模式,即使有ICMP包,也可能因为内核队列的问题导致无法立即读取。给套接字设置接收超时:

# 设置1秒接收超时,格式为[秒, 微秒]的数组,打包成long类型
timeout = [1, 0]
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeout.pack("l_2"))

之后调用recvmsg时,超时会抛出Errno::EAGAIN或Errno::EWOULDBLOCK,可以捕获并处理。

3. 正确解析错误队列中的控制信息

ICMP错误报文会被封装在控制信息(cmsg)中,需要解析IP_RECVERR类型的控制数据:

begin
  # 接收数据,指定MSG_ERRQUEUE标志
  msg, flags, _, control = sock.recvmsg(1024, Socket::MSG_ERRQUEUE)
  
  control&.each do |cmsg|
    # 筛选IPv4的ICMP错误控制信息
    next unless cmsg.cmsg_level == Socket::IPPROTO_IP && cmsg.cmsg_type == Socket::IP_RECVERR
    
    # 解析sock_extended_err结构体(Linux下)
    err_info = cmsg.data.unpack("i!i!i!i!a16a16")
    # 提取来源IP地址
    src_ip = Socket.unpack_sockaddr_in(err_info[4])[1]
    puts "第一跳IP: #{src_ip}"
  end
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
  # 超时,未收到错误报文
end

IPv6的解析逻辑类似,只需将IPPROTO_IP换成IPPROTO_IPV6,对应IPV6_RECVERR类型。

4. 确保UDP发送包的目标端口有效

发送的UDP包需要指向一个大概率未开放的端口(比如traceroute常用的33434-33534区间),这样中间路由才会返回ICMP超时错误:

target = "8.8.8.8"
port = 33434
sock.send("\x00" * 32, 0, Socket.sockaddr_in(port, target))

内容的提问来源于stack exchange,提问作者Marek Küthe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 01:10:24