我有一个“理解”问题,但它确实有一个我已经简化的真实案例。考虑这个示例网络(使用简化的 IP): 从左到右:
- 三台主机 S1-S3 (IPs),分别运行三个程序 R1-R3;一台有 3 个 VLAN 的交换机和一个连接到第 4 台主机 S4 的 eth0 的 trunk 端口。所有这些都是 Ubuntu 20.04 LTS,但这可能无关紧要。
- S4 正在运行 P1,它绑定到三个 UDP 套接字,分别位于 S1、2、3 各自网络上的三个 VLAN 接口上
- P1以 10-100Hz 的速率向每个套接字上的多个端口发送单播和
OUTPUT
多播 UDP 消息。有一个 netfilter链控制这些流。
每个红色区域代表可能出现“故障”的情况。除一种情况外,所有其他情况下,UDP 数据报都会被立即丢弃:
- 虚拟接口
V10
链路断开;进程R2
未运行;或OUTPUT
链式DROP
传输 - 在每种情况下,数据包要么被 S2 默默丢弃,要么向 P1 报告错误并丢弃数据包。所有其他组播继续传输。 - 对于 S3 的情况,移除阻止后
OUTPUT
,tx
套接字的缓冲区v12
会迅速被发往的单播数据包填满12.1
,从而阻止任何进一步的单播和多播写入。
在此状态下,strace
onP1
可能看起来像这样(修剪后):
15:38:27 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = 31674
15:38:27 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = 31674
15:38:27 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = 31674
15:38:27 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = -1 EAGAIN (Resource temporarily unavailable)
15:38:30 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = 31674
15:38:30 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = 31674
15:38:30 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = 31674
15:38:30 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = -1 EAGAIN (Resource temporarily unavailable)
并ss
显示问题:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
UNCONN 0 148992 0.0.0.0:51047 0.0.0.0:* users:(("task",pid=381888,fd=9))
skmem:(r0,rb212992,t148992,tb131070,f0,w0,o0,bl0,d0)
因此,我们向套接字缓冲区 (默认 120kb) 写入了 4 * ~30kb,并EAGAIN
在缓冲区已满时报告第 4 次写入。缓冲区不断被填满,而操作系统却在等待永远不会到来的ARP
响应。S3
以上这些背后有两个问题:
- UDP 本质上是不可靠的。我们的应用程序很高兴看到数据包被丢弃,那么为什么
tx
在内核尝试解析 ARP 时会以这种方式排队呢?(考虑一下 S3 可能只是偶尔连接但其他主机可能仍然可以访问的情况v12
。) - 缓冲区似乎每 3 秒清空一次(然后迅速被新写入填充)。结果之一是,进入同一
tx
队列的任何多播都会以小爆发的方式发出,而不是以稳定的发送速率发出。当然,我们可以打开更多发送套接字,但这个值在哪里设置,这种行为是否可以调整sysctl
?