【问题标题】:Unusually slow TCP-connection in LinuxLinux 中异常缓慢的 TCP 连接
【发布时间】:2020-05-03 14:30:55
【问题描述】:

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

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

我尝试使用 Linux sysctl 设置:

net.ipv4.tcp_congestion_control
net.ipv4.tcp_sack
net.ipv4.route.flush

但这并没有帮助。

似乎问题出现在监听套接字端。在 tcpdump 中,握手时 TCP 窗口大小正常。但是在第一个传入数据包窗口大小减小后(在侦听器方面)。

UPD
这是我的服务器端代码 sn-p:

 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);

有人能解释一下我的问题吗?我将不胜感激!
它可能与最近的一些 Linux 内核修改有关吗?我是否需要调整一些 Linux 内核设置或检查一些用户模式设置(例如套接字选项或其他)?

附:问题不稳定。

更新:

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

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

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

possible SYN flooding on port 12345. Sending cookies.

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

【问题讨论】:

  • 很奇怪的情况。如果“问题不稳定”,你怎么能确定问题没有出现在 3.10 内核上。看看dmesg 输出。传输慢的时候有什么不寻常的地方吗?
  • 请注意,Cloudflare 博客条目 SYN packet handling in the wild 指出“在 4.3 之前的内核中,SYN 队列长度的计算方式不同。”我没有仔细看它,但我认为你的积压 3 在 4.3 之前被四舍五入到 16。这也表明将您的 backlog 增加到至少 16 可能会使您的性能类似于您在 3.10 中看到的性能。更改链接到博客here
  • 您还应该检查是否在 3.10 部署中启用了 SYN cookie。如果不是,SYN 将被丢弃,客户端将重新传输。 Here 是其他人在启用时间戳时观察到窗口大小和同步 cookie 的类似问题。 (如果您看不懂中文,请在 Chrome 中加载,右键单击并选择翻译,这会做得不错)。根据 cloudflare 博客,他们禁用了时间戳(这是存储 wscale 的位置),因此看不到这个问题。
  • @z0lupka 查看this change,它消除了nr_table_entries,它曾经具有最小值8+1,向上取整为2 的幂=16。我实际上并没有尝试从listen() 遵循backlog 的值。要真正证明基于 backlog 值为 3 的 SYN 队列长度是 16 需要更多调查。
  • @z0lupka 有人通过here 完成了这项工作

标签: c c sockets tcp linux-kernel network-programming


【解决方案1】:

我不确定这是否正是您的情况,但看起来很相似。似乎是known problem

原因

许多情况都可能导致这种 Linux 内核行为:

  • SYN-cookies 上下文中内核连接处理的特殊性,连接具有零窗口比例(或者如果 WS 以其他方式修改)。
  • 零窗口比例setsockopt()SO_RCVBUF 引起的(请参阅tcp_select_initial_window()
  • 非常backlog

说明

关于“慢”传输:
Windows Scaling option 由两个主机在 [SYN - SYN+ACK] 阶段计算。粗略地说,主机 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-cookies(即会填充你的积压队列)。顺便说一句,您在 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.

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


解决方案

总之,可以通过一些活动来消除这个问题:

  1. 如果您的网络中存在真正的 SYN-flood - 部分 SYN-cookie 解决这个信息安全问题。有了真正的攻击,有 没有时间考虑慢速连接。这是紧急情况。
  2. 如果不是,即某些 SYN 重传会引发 SYN cookie:
    • thoughtfully increase backlog 消除此类情况;
    • 不要在监听套接字上使用setsockopt()SO_RCVBUF。这没有多大意义。在不执行setsockopt() 的情况下,您可以在上述场景中降低内核进行不同 WS 计算的概率。顺便说一句,如果需要,您可以在接受的套接字上设置 SO_RCVBUF

复制

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

hping3 -c 3 -S -p 12345 --fast 10.0.0.99

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

【讨论】:

  • 好的,但是 3.10 内核呢?这是我的生产代码,所以我必须注意可能出现的不良行为。
  • 是的,我的 LAN 中没有 SYN-flood,但是有几次 syn 重传
  • @z0lupka 您可以通过hping3 实用程序模拟此类 SYN 重新传输并检查 3.10 内核的行为。 hping3 -d 120 -S -p 12345 10.0.0.99 --fast 之类的东西会立即从客户端发起连接。
  • 非常感谢!积压增加有所帮助。
猜你喜欢
  • 2016-03-24
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 2023-03-27
  • 2014-08-31
相关资源
最近更新 更多