Linux 中 TCP 连接异常缓慢

z0l*_*pka 7 c sockets network-programming tcp linux-kernel

我编写了基于 Berkeley 套接字的用户模式客户端 - 服务器应用程序,这些套接字通过一些专用网络进行交互。
情况肯定很奇怪。有时,在某些模糊的情况下,连接会变得非常缓慢。在我的例子中,正常的 TCP 数据交换是每段大约 10-25 KB 的有效载荷,但有时它变成每段大约 200-500 字节。

经过一些故障排除后,我意识到这个问题对于其他网络服务是不可重现的,因此看起来应该归咎于我的服务。但我想不通,怎么了。它在 3.10 Linux 内核上运行良好,但在 4.4 上有这种奇怪的行为。是否是一些内部内核更改导致了此类问题?

我尝试使用 Linuxsysctl设置:

net.ipv4.tcp_congestion_control
net.ipv4.tcp_sack
net.ipv4.route.flush
Run Code Online (Sandbox Code Playgroud)

但这没有帮助。

似乎问题出现在侦听套接字端。在tcpdumpTCP 窗口大小在握手时是确定的。但是在第一个传入数据包窗口大小减小后(在侦听器方面)。

UPD
这是我的服务器端代码片段:

 serv_fd = socket(AF_INET, SOCK_STREAM, 0); 
 if (serv_fd == -1) {
      perror("socket");
      return;
 }   

 server.sin_family = AF_INET;
 server.sin_port = htons(LISTEN_PORT);
 server.sin_addr.s_addr = htonl(INADDR_ANY);

 #ifdef SET_BUF
 if (setsockopt(serv_fd, SOL_SOCKET, SO_RCVBUF, &buflen, sizeof(int)) == -1) {
      perror ("setsockopt");
      return;
 }   
 if (setsockopt(serv_fd, SOL_SOCKET, SO_SNDBUF, &buflen, sizeof(int)) == -1) {
      perror ("setsockopt");
      return;
 }   
 #endif // SET_BUF

 if (bind(serv_fd, (struct sockaddr *) &server, sizeof(server)) == -1) {
      perror("bind");
      return;
 }   

 if (listen(serv_fd, 3)) {
      perror("listen");
      return;
 }   

 printf("Server is listening on %u\n", LISTEN_PORT);
Run Code Online (Sandbox Code Playgroud)

有人可以对我的问题有所了解吗?我会很感激!
是否与最近的一些 Linux 内核修改有关?我是否需要调整一些 Linux 内核设置或检查一些用户模式设置(fe 套接字选项或其他)?

PS 问题不稳定。

更新:

tcpdump 的输出:

IP 10.0.0.34.31334 > 10.0.0.99.12345: Flags [S], seq 426261790, win 43690, options [mss 65495,sackOK,TS val 799180610 ecr 0,nop,wscale 7], length 0
IP 10.0.0.99.12345 > 10.0.0.34.31334: Flags [S.], seq 803872704, ack 426261791, win 65483, options [mss 65495,sackOK,TS val 799180567 ecr 799180610,nop,wscale 0], length 0
IP 10.0.0.34.31334 > 10.0.0.99.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 799180610 ecr 799180567], length 0
IP 10.0.0.34.31334 > 10.0.0.99.12345: Flags [P.], seq 1:1301, ack 1, win 342, options [nop,nop,TS val 799180610 ecr 799180567], length 1300
IP 10.0.0.34.31334 > 10.0.0.99.12345: Flags [P.], seq 1301:1804, ack 1, win 342, options [nop,nop,TS val 799181412 ecr 799180610], length 503
IP 10.0.0.99.12345 > 10.0.0.34.31334: Flags [.], ack 1804, win 512, options [nop,nop,TS val 799181412 ecr 799181412], length 0
Run Code Online (Sandbox Code Playgroud)

10.0.0.34.31334 是客户端,10.0.0.99.12345 是服务器。注意win 512最后一行中的意外。

UPD2:我在 dmesg 中看到了几条关于 SYN-cookies 的消息,例如:

possible SYN flooding on port 12345. Sending cookies.
Run Code Online (Sandbox Code Playgroud)

但它们与慢速传输的时间关系不大。

red*_*0ct 8

我不确定这正是您的情况,但看起来很相似。似乎这是一个已知问题

原因

许多情况会导致这样的 Linux 内核行为:

  • SYN-cookies 上下文中内核连接处理的特异性,连接具有零窗口比例(或者如果 WS 以某种其他方式修改)。
  • 零窗口缩放你挑起setsockopt()SO_RCVBUF(见tcp_select_initial_window()
  • 非常backlog

解释

关于“慢”传输:
视窗缩放选项是在[计算- SYN + ACK SYN由两台主机的阶段。粗略地说,主机 A 说“在未来交换期间在 N 上暗示我的 TCP 窗口大小”(SYN)然后主机 B 说“在未来交换期间暗示我在 M 上的 TCP 窗口大小”(SYN+ACK)——这里 N 和 M 可能相同. 因此,在正常情况下,这些系数被存储并最终在数据交换时使用。
但是TCP SYN-cookies技术意味着忘记连接的 [ SYN - SYN+ACK ] 阶段(某些声明的选项,包括 WS 将在 SYN+ACK 后丢失)。在这种情况下,Linux 内核会重新计算 WS当 ACK 到达时(如果 ACK 已到达,则需要创建常规连接)。但是第二次重新计算可能会有所不同,因为setsockopt()不会影响它(出于某些客观原因)。在这里,您面临的情况是,当您的服务器使用 SYN+ACK 发送零窗口缩放选项,然后忘记它,然后重新生成连接(当 ACK 到达时),就像使用某些默认窗口缩放(例如 7)一样并使用小窗口暗示客户端将其乘以 128。但客户端不会忘记 WS 是 0 并将小窗口大小视为真实的 - 因此它发送了一小部分数据 - 因此你的“慢”连接占据了舞台。

关于 SYN-flood:
当您有这么小的积压时,简单的 3 次 SYN 重传可以引发 SYN-cookie(即会填满您的积压队列)。顺便说一句,你在 tcpdump 中看到重传了吗?
ip-sysctl.txt

Note, that syncookies is fallback facility.
It MUST NOT be used to help highly loaded servers to stand
against legal connection rate. If you see SYN flood warnings
in your logs, but investigation shows that they occur
because of overload with legal connections, you should tune
another parameters until this warning disappear.
See: tcp_max_syn_backlog, tcp_synack_retries, tcp_abort_on_overflow.

syncookies seriously violate TCP protocol, do not allow
to use TCP extensions, can result in serious degradation
of some services (f.e. SMTP relaying), visible not by you,
but your clients and relays, contacting you. While you see
SYN flood warnings in logs not being really flooded, your server
is seriously misconfigured.
Run Code Online (Sandbox Code Playgroud)

因此,如果您的 LAN 中没有 SYN-flood 攻击 - 您的服务器配置严重错误。SYN-cookies 应该只在出现 SYN-flood 攻击时才起作用。


解决方案

最后,可以有一些活动来消除这个问题:

  1. 如果您的网络中存在真正的 SYN 泛滥 - SYN cookie 可以部分解决此信息安全问题。对于真正的攻击,没有时间考虑慢速连接。这是紧急情况。
  2. 如果没有,即某些 SYN 重传引发了 SYN cookie:
    • 深思熟虑地增加积压以消除这种情况;
    • 不做setsockopt()SO_RCVBUF上监听套接字。这没有多大意义。如果不这样做,setsockopt()您可以降低内核在上述场景中进行不同 WS 计算的概率。顺便说一句,SO_RCVBUF如果需要,您可以在接受的套接字上设置。

再现

hping3在近似条件下使用简单的客户端和服务器重现了您的问题。因此,您可以使用以下内容填充服务器的积压队列:

hping3 -c 3 -S -p 12345 --fast 10.0.0.99
Run Code Online (Sandbox Code Playgroud)

然后从客户端启动连接 -至少在 4.4 内核上,连接将在所谓的“SYN-cookies 上下文”中打开。您还可以检查它在3.10内核增加-c,从3X达到成功繁殖。