Windows将本地连接网络上的主机的TCP连接发送到默认网关

时间:2019-03-28 14:51:09

标签: windows tcp routing ip

我正在调查Windows上的一个奇怪问题,不知道发生了什么。

我有一个运行Windows 10的Windows主机。该主机连接到两个网络,即我们的LAN和WLAN。该系统上的程序正在通过WLAN与连接到同一WLAN网络的嵌入式系统进行通信。通信通常可以正常工作,并使用HTTP和一些自定义TCP协议。

在嵌入式系统上执行软件更新时,它必须执行重新引导。 Windows程序通过HTTP将命令发送到嵌入式系统。该HTTP事务顺利完成,嵌入式系统执行了重新启动。

HTTP事务完成后,Windows程序等待10秒钟,通过执行TCP连接开始轮询嵌入式设备。使用一秒钟的超时时间,并在120秒后重试TCP重新连接。

网络跟踪显示,在27次尝试建立TCP连接失败之后,Windows系统突然停止在WLAN接口上发送TCP SYN帧,但开始将它们发送到LAN接口上的默认路由器。

我必须知道,为什么Windows突然使用默认路由器尝试到达本地连接的网络上的节点。 :-(

过一会儿,Windows再次开始使用WLAN接口。这似乎是由嵌入式系统的ARP条目超时引起的。 Windows在WLAN接口上发送ARP答复,嵌入式系统发送ARP响应,然后Windows再次将WLAN接口用于TCP SYN帧。不幸的是,那时的120s重试期已经到期。

有人知道,是什么原因导致Windows出现这种奇怪的行为,在哪里可以找到有关它的文档以及如何阻止Windows执行此操作?

在此先感谢

马里奥

更新

今天,我检查了Windows 7上的问题,并且Windows 7的行为与Windows 10相同。我还观察到源自Internet Explorer的HTTP连接请求被发送到默认网关,而不是正确的接口。我在行为异常期间检查了路由表和ARP表,发现路由表是正确的,但是ARP表缺少嵌入式系统的条目。

似乎是某些原因导致Windows丢弃ARP表条目,而不是在路由表中找到的接口上发出ARP请求以使用默认路由。

下一个更新

这似乎是预期的行为(至少根据https://superuser.com/a/743329/941271https://superuser.com/a/743329/941271

我了解到,arp -a命令窗口在窗口:-(上没有显示完整的ARP表。使用netsh interface ipv4 show neighbors时,我可以看到目标IP地址的条目标记为不可访问。

我尚未在Microsoft文档中找到有关此“功能”的任何文档。

又一次更新

我仍然没有从Microsoft找到关于此行为的任何描述,但是我找到了一个用于操作邻居表的API:GetIpNetTable2()和DeleteIpNetEntry2()。在尝试连接目标设备之前调用DeleteIpNetEntry2()解决了我的问题。

这是我的代码:

#pragma comment(lib, "iphlpapi.lib")
static void PurgeUnreachableEntry(const boost::asio::ip::address &Addr)
{
    int AF;
    if      (Addr.is_v4()) AF=AF_INET;
    else if (Addr.is_v6()) AF=AF_INET6;
    else                   return;

    PMIB_IPNET_TABLE2 pipTable = NULL;
    unsigned long status = GetIpNetTable2(AF, &pipTable);
    if (status != NO_ERROR) 
    {
        printf("PurgeUnreachableEntry: GetIpNetTable() failed: %s\n", 
            boost::system::error_code(status, boost::system::system_category()).message().c_str());
        return;
    }
    boost::shared_ptr<MIB_IPNET_TABLE2> IpTable(pipTable, FreeMibTable);

    for (unsigned i= 0; i < pipTable->NumEntries; i++) {
        MIB_IPNET_ROW2 &Entry=pipTable->Table[i];

        if (!Entry.IsUnreachable) continue;

        boost::asio::ip::address EntryAddr;
        switch (Entry.Address.si_family)
        {
        case AF_INET:
            EntryAddr = boost::asio::ip::address_v4(*(boost::asio::ip::address_v4::bytes_type*)&Entry.Address.Ipv4.sin_addr);
            break;
        case AF_INET6:
            EntryAddr = boost::asio::ip::address_v6(*(boost::asio::ip::address_v6::bytes_type*)&Entry.Address.Ipv6.sin6_addr, Entry.Address.Ipv6.sin6_scope_id);
            break;
        default:
            continue;
        }

        if (Addr != EntryAddr) continue;

        DeleteIpNetEntry2(&Entry);
    }
}

0 个答案:

没有答案