WinAPI - 为什么当超时设置低于 1000 毫秒时 ICMPSendEcho2Ex 报告错误超时?

Tes*_*ler 6 networking winapi kernel windbg ping

编辑:我开始将此作为 PowerShell / .Net 问题提出,但在互联网上找不到任何参考。根据反馈,它似乎是一个 WINAPI 问题,因此这是一个编辑/重写/重新标记,但出于这个原因,大部分测试和背景参考 .Net。

概括

IcmpSendEcho2如果 ping 超时参数设置为低于 1000 毫秒(1 秒),则WINAPI ping 函数似乎存在计时错误。这会导致它返回间歇性假超时错误。而不是成比例的“较低超时 = 更多失败”行为,它似乎是 >=1000ms+ 的预期行为,<=999ms 触发错误超时,通常以交替的成功/失败/成功/失败模式。

我称它们为假超时,因为我有 WireShark 数据包捕获显示回复数据包在超时内很好地返回,部分原因是当回复通常有 500-800 毫秒的余量时,1 毫秒的变化不应该是很长的时间,部分原因是因为我可以同时运行两组具有不同超时的 ping 并看到两者之间的不同行为。

在我原来的 .Net 问题的评论中,@wOxxOm 有:

和@Lieven Keersmaekers 调查并发现了超出我技能水平的东西来解释:

  • “我可以认为这是一个潜在的 WINAPI 问题。在成功和超时调用之后IPHLPAPI!IcmpSendEcho2Ex000003e7参数在堆栈上,两者都设置一个事件并返回到IPHLPAPI!IcmpEchoRequestComplete成功调用的 eax 寄存器包含00000000的差异和超时调用的 eax包含00000102(WAIT_TIMEOUT) 的寄存器

  • “编译 64 位 C# 版本,不再调用IPHLPAPI. 显示的一致的事情是clr.dll GetLastError()返回WSA_QOS_ADMISSION_FAILURE超时。在我的示例中也一致的是成功和超时调用之间的线程执行顺序略有不同。”

测试步骤:

选择一个远程主机并设置一些连续的 ping 运行。(我的示例中的 IP 属于百度.cn(中国)并且对我大约有 310 毫秒的 ping 回复)。所有这些的预期行为:几乎完全是 ping 回复,偶尔会因网络状况而下降。

PowerShell / .Net,超时 999 毫秒,实际结果是奇怪的回复/丢弃/回复/丢弃模式,比预期的丢弃多得多:

$Pinger = New-Object -TypeName System.Net.NetworkInformation.Ping
while($true) { 
    $Pinger.Send('111.13.101.208', 999)
    start-sleep -Seconds 1
}
Run Code Online (Sandbox Code Playgroud)

命令提示符 ping.exe超时999毫秒,实际结果更可靠(编辑:但后来的发现也对此提出了质疑):

ping 111.13.101.208 -t -w 999
Run Code Online (Sandbox Code Playgroud)

PowerShell / .Net,超时 1000 毫秒,实际结果如预期:

$Pinger = New-Object -TypeName System.Net.NetworkInformation.Ping
while($true) { 
    $Pinger.Send('111.13.101.208', 1000)
    start-sleep -Seconds 1
}
Run Code Online (Sandbox Code Playgroud)

它也可以用 C# 重复,但我已经编辑了该代码,现在它似乎是一个 WinAPI 问题。

这些并排运行的示例屏幕截图

  • 在左边,.Net 有 999 毫秒超时和 50% 失败。
  • 居中,命令提示符,几乎所有回复。
  • 右边是1000ms超时的.Net,几乎所有回复。

比较命令提示符 ping 和两个 powershell ping


早期调查/背景

我从 500 毫秒超时开始,假回复的数量似乎因远程主机的 ping 回复时间而异:

  • ping 一些 30 毫秒以外的东西会报告 TimedOut 大约 10 次 ping 中的 1 次
  • 在 100 毫秒外 ping 某个东西会报告 TimedOut 大约四分之一的 ping
  • ping 一些 300 毫秒以外的东西会报告 TimedOut 大约 2 次 ping 中的 1 次

在同一台计算机上,在同一互联网连接上,使用相同的 500 毫秒超时设置发送相同数量的数据(32 字节缓冲区),没有其他大量带宽使用。我在 Windows 10 默认设置之外没有运行防病毒网络过滤器,我认识的另外两个人已经确认了他们计算机上这种频繁的 TimedOut 行为(现在评论中还有两个),超时或多或少,所以它不应该是我的网卡或驱动程序或ISP。

WireShark 捕获 ping 回复的数据包,错误报告为超时

我手动对距离约 100 毫秒的主机运行了四次 ping,超时时间为 500 毫秒,并使用 WireShark 捕获网络流量。PowerShell 截图:

来自 PowerShell 的 Ping - 4 次发送,3 次回复

线鲨截图:

显示 4 次发送和 4 次回复的 WireShark ping 日志

请注意,WireShark 日志记录了 4 个请求离开,4 个回复返回,每个请求的时间差约为 0.11 秒(110 毫秒)——一切都在超时之内,但 PowerShell 错误地将最后一个报告为超时。

相关问题

谷歌搜索向我展示了一堆问题,System.Net.NetworkInformation.Ping但没有一个看起来相同,例如

Ping()文档警告了低超时可能仍然说成功,但我看不到它警告说如果设置低于 1 秒,超时可能会错误地报告失败。

编辑:现在我正在搜索 ICMPSendEcho2,我在 2015 年 5 月的一篇博客文章中发现了这个问题:https ://www.frameflow.com/ping-utility-flaw-in-windows-api- created-false-timeouts/ - 发现相同的行为,但没有进一步的解释。他们说这ping.exe受到了影响,而我最初认为不是。他们还说:

  • 在我们的测试中,我们无法在 Windows Server 2003 R2 和 Windows Server 2008 R2 上重现它。但是,它在 Windows Server 2012 R2 和最新版本的 Windows 10 中一致出现。

为什么?超时处理有什么问题使它完全忽略进入网络堆栈的 ping 响应?为什么 1000 毫秒是一个重要的截止时间?