确定哪个 IP 地址收到了数据包

Posted

技术标签:

【中文标题】确定哪个 IP 地址收到了数据包【英文标题】:Determine which IP address received a packet 【发布时间】:2020-10-28 15:44:02 【问题描述】:

我有一个侦听所有 IPv4 地址的 UDP 服务器,即 INADDR_ANY 或 0.0.0.0。对于从客户端接收到的每个数据包,它都应使用接收数据包的确切网络接口的 IP 地址进行响应。从概念上讲,它应该将数据包的源地址放入数据包的有效负载中。

在使用sendto() 时,我如何知道哪个源地址将用于向特定目标地址发送数据包?我是否需要手动枚举所有网络接口,将网络前缀(或子网掩码的 IPv4 地址)与目标地址匹配?如果是这样,怎么做?我会对涉及 Boost.Asio 的跨平台解决方案感兴趣,但如果这不可能,我需要使用 Winsock。将来我可能也需要针对 Linux 的解决方案。

【问题讨论】:

【参考方案1】:

对于从客户端接收到的每个数据包,它都应使用接收数据包的确切网络接口的 IP 地址进行响应。

这不是您通常需要手动处理的事情。如果您只是让sendto() 将数据包发送到recvfrom() 报告的发件人的IP/端口,操作系统将根据哪个接口有到该发件人的可用路由为您路由数据包。

在使用sendto()时,我如何知道哪个源地址将用于向特定目标地址发送数据包?

您可以使用setsockopt() 启用侦听UDP 套接字上的IP_PKTINFO 选项,然后使用WSARecvMsg() (Windows) 或recvmsg() (Linux) 代替recvfrom()。这将使您能够访问 in_pktinfo 结构,该结构告诉您接收到的数据包发送到的目标 IP,以及实际接收到的接口。

【讨论】:

这是个好消息!我不知道recvmsg()/WSARecvMsg()。我想这也适用于非 IPV6_V6ONLY 套接字并返回 IPv4 映射地址,对吧?我这样做的原因是为了支持一些糟糕的协议,否则我只会按照你的建议使用recvfrom()sendto() @purefanatic 用于 IPv6 套接字(无论您是否启用了 IPV6_V6ONLY),您需要启用 IPV6_PKTINFO (Windows) 或 IPV6_RECVPKTINFO (Linux) 选项,然后是 @987654339 @/recvmsg() 会给你一个in6_pktinfo 结构而不是in_pktinfo 我明白了。不过我忘了提到一个用例:客户端可能使用广播地址来访问服务器。在这种情况下,服务器仍应发送单播响应,但从recvmsg() 获得的信息不会很有帮助,因为它会返回原始广播地址,或者不会? @purefanatic 它会给你使用的广播IP,是的。但它仍会为您提供接收广播的特定接口,并且您仍将拥有发送者的 IP/端口。因此,如果需要,您可以将一个单独的套接字绑定到该接口并使用它发送回该发送者。否则,就像我说的,只需使用接收套接字发送回发送者,让操作系统为您处理路由。

以上是关于确定哪个 IP 地址收到了数据包的主要内容,如果未能解决你的问题,请参考以下文章

第一条arp报文如何知道需要解析哪个ip地址

IP 地址与MAC硬件地址

wireshark捕获/过滤指定ip地址数据包

TCP / IP协议 --- 用户层面

002-规划IP地址和MAC地址数据包和数据帧

路由老是发送ARP,查找自己MAC地址的IP,得到自己确定,又继续发送?这是怎么回事啊?