【问题标题】:TCP simultaneous open and self connect preventionTCP同时打开和自连接预防
【发布时间】:2011-02-28 08:50:36
【问题描述】:

TCP 标准具有“同时打开”功能。

该功能的含义,客户端尝试连接到本地端口,当端口来自ephemeral range时,偶尔可以连接到自己(参见here)。

所以客户端认为它连接到服务器,而它实际上连接到自己。另一方面,服务器无法打开其服务器端口,因为它被客户端占用/窃取。

我正在使用 RHEL 5.3,我的客户不断尝试连接到本地服务器。 最终客户端连接到自己。

我想阻止这种情况。我看到了两种可能的解决方案:

  1. 不要将临时端口用于服务器端口。 同意临时端口范围并在您的机器上进行配置(请参阅ephemeral range
  2. 检查 connect() 有人提议here

你怎么看? 你如何处理这个问题?

附注1

除了我显然在寻找的解决方案, 我希望你能分享你在这个问题上的真实经历。

当我找到问题的原因时,我对自己不熟悉的工作场所感到“惊讶”。通过定期连接来轮询服务器是恕我直言的常见做法, 所以这个问题是如何不为人知的。

【问题讨论】:

  • 我认为这个问题需要澄清。这是我对它的理解: - 您有一个服务器在临时范围内的(非保留)端口上运行。例如,端口 56789。 - 本地计算机上的客户端通过连接到“localhost:56789”来轮询服务器。 - 如果服务器已关闭,则出站端口也可能选择为 56789。我理解正确吗?在这种情况下 connect 调用的结果是什么?
  • 你理解正确。我已经编辑了问题以使其更清楚。

标签: c++ networking tcp


【解决方案1】:

当我偶然发现这一点时,我大吃一惊。我可以弄清楚传出 端口号意外匹配传入端口号,但不是 TCP 的原因 握手(SYN SYN-ACK ACK)会成功(问问自己:如果 没有人在做listen()和accept()???)

Linux 和 FreeBSD 都有这种行为。

无论如何,一种解决方案是远离服务器的高端口号范围。

我注意到达尔文通过不允许传出端口来回避这个问题 与目的端口相同。他们一定也被这个咬过……

显示这种效果的简单方法如下:

while true
do
    telnet 127.0.0.1 50000 
done

等待一分钟左右,你就会和自己聊天了……

Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refused
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refused
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refused
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
hello?
hello?

无论如何,它是很好的面试材料。

【讨论】:

  • @Marchel 喜欢你的 telnet 示例
  • “无论如何,它是很好的面试材料。” - 直到今天你才会雇用我 :)
  • 看一张TCP状态转换图。有一个主动打开,连接发送一个 SYN 并进入 SYN_SENT 状态,然后获取该 SYN 并发送一个 SYN + ACK 并进入 SYN_RCVD。 ssfnet.org/Exchange/tcp/tcpTutorialNotes.html#ST
【解决方案2】:

将客户端套接字绑定到端口 0(系统分配),检查系统分配的端口,如果它与本地服务器端口匹配,则您已经知道服务器已关闭并且可以跳过 connect()。

【讨论】:

  • 这在我的情况下不起作用,因为我有几个要连接的本地服务器。即使绑定显示不同的端口,因为另一个线程也在尝试连接这两个线程,最终可能会相互连接 - 而不是本地服务器(尚未启动)。这是一个罕见的情况,但它会发生。
【解决方案3】:

对于服务器,您需要将套接字绑定到端口。一旦 addr:port 对绑定了套接字,它将不再用于 connect() 中的隐式绑定。

没问题,没问题。

【讨论】:

  • 问题是本地服务器关闭时。然后客户端通过每隔几秒定期执行连接来轮询服务器。操作系统获取源端口(又名临时端口)。如果服务器端口来自临时端口范围,那么我连接到自己。
  • 所以只需在 connect() 之前执行 bind() 并检查分配的临时端口是否等于预期的服务器端口。如果没有,请执行 connect()。
  • bind() before connnect() 仅在只有一个端口号导致问题时才有效。如果客户端和服务器在多个端口上连接,那么如果服务器没有运行,这些端口最终可能会在客户端中相互连接。然后 bind() 需要检查每个正在使用的端口号,如果你想保持代码区域隔离,这有点麻烦。
【解决方案4】:

请注意,这个解决方案是理论上的,我没有自己测试过。我以前没有经历过(或没有意识到),希望我不会再经历过。

我假设您既不能编辑客户端源代码也不能编辑服务器源代码。此外,我假设真正的问题是无法启动的服务器。

使用启动应用程序启动服务器。如果服务器将绑定的目标端口正在被任何进程使用,请使用原始套接字创建一个 RST(重置数据包)。

下面的帖子简要描述了什么是 RST 数据包(取自http://forum.soft32.com/linux/killing-socket-connection-cmdline-ftopict473059.html

您必须查看“原始套接字”数据包生成器。
你必须是超级用户。
您可能还需要一个网络嗅探器。

http://en.wikipedia.org/wiki/Raw_socket
http://kerneltrap.org/node/3072 - TCP RST 攻击
http://search.cpan.org/dist/Net-RawIP/lib/Net/RawIP.pm - Perl 模块
http://mixter.void.ru/rawip.html - C 中的原始 IP

在 C 版本中,您需要一个 TH_RST 数据包。

RST 旨在处理以下情况。

A 和 B 建立连接。
B重新启动,并忘记了这一点。
A 从端口 Y 向端口 X 发送一个数据包。

B 发回一个 RST 数据包,说“你在说什么?我不知道
和你有联系。请关闭此连接。”

所以你必须知道/伪造 B 的 IP 地址,并且知道两个端口 X
和 Y。其中一个端口将是众所周知的端口号。其他
你必须找出答案。我想你还需要知道顺序
数字。

通常人们使用嗅探器来执行此操作。您可以使用带有
的开关 数据包镜像功能,或在主机 A 或 B 上运行嗅探器。

请注意,Comcast 这样做是为了禁用 P2P 流量。
http://www.eff.org/wp/packet-forgery-isps-report-comcast-affair

在我们的例子中,我们不需要使用嗅探器,因为我们知道以下信息:

所以你必须知道/伪造 B 的 IP 地址,并且知道两个端口 X 和是的

X = Y 和 B 的 IP 地址是 localhost

http://mixter.void.ru/rawip.html 上的教程描述了如何使用原始套接字。

注意系统上的任何其他进程也可能从临时池中窃取我们的目标端口。 (例如 Mozilla Firefox)此解决方案不适用于此类连接,因为 X != Y B 的 IP 地址不是 localhost,而是 eth0 上的 192.168.1.43 之类的地址。在这种情况下,您可以使用 netstat 检索 X、Y 和 B 的 IP 地址,然后相应地创建一个 RST 数据包。

【讨论】:

  • -1:您正在尝试解决一个与 OP 声明的问题只是模糊相关的问题。
【解决方案5】:

嗯,这是一个奇怪的问题。如果您在同一台机器上有一个客户端/服务器并且它总是在同一台机器上,那么共享内存或 Unix 域套接字或其他形式的 IPC 可能是更好的选择。

其他选项是在固定端口上运行服务器,在固定源端口上运行客户端。比如说,服务器在 5000 上运行,客户端在 5001 上运行。如果绑定了其他东西,您确实会遇到绑定到其中任何一个的问题。

您可以在偶数端口号上运行服务器,并强制客户端使用奇数端口号。在临时范围内选择一个随机数,或将其与 1 结合,然后调用 bind() 。如果 bind() 因 EADDRINUSE 失败,则选择一个不同的奇数端口号并重试。

【讨论】:

    【解决方案6】:

    这个选项实际上并没有在大多数 TCP 中实现。您有实际问题吗?

    【讨论】:

    • 我遇到了问题,因为我的客户每隔几秒钟就不断尝试连接到本地服务器。我正在使用基于内核 2.6.18 的 Red Hat Enterprise Linux 5.3,它确实实现了同时打开。
    【解决方案7】:

    这是一个有趣的问题!如果您最关心的是您的服务器正在运行,您总是可以在服务器本身中实现一个心跳机制来向另一个进程报告状态。或者您可以编写一个脚本来检查您的服务器进程是否正在运行。

    如果您更关心与服务器的实际连接是否可用,我建议您将客户端移动到另一台机器上。这样您就可以验证您的服务器是否至少有一些网络连接。

    【讨论】:

      【解决方案8】:

      在我看来,这是 TCP 规范中的一个错误;监听套接字不应发送未经请求的 SYN,并且在发送后接收 SYN(而不是 SYN+ACK)应该是非法的并导致重置,这将很快让客户端关闭不幸选择的本地港口。但是没有人征求我的意见;)

      正如您所说,显而易见的答案是不要在临时端口范围内进行侦听。如果您知道您将连接到本地计算机,另一种解决方案是设计您的协议,以便服务器发送第一条消息,并在客户端有一个短暂的超时来接收该消息.

      【讨论】:

        【解决方案9】:

        您遇到的实际问题似乎是,当服务器关闭时,其他东西可以使用您期望服务器的临时端口作为传出连接的源端口。发生这种情况的细节与实际问题是分开的,并且它可能以您描述的方式以外的方式发生。

        解决这个问题的方法是在套接字上设置 SO_REUSEADDR。这将允许您在具有当前传出连接的端口上创建服务器。

        如果您真的关心那个端口号,您可以使用特定的操作方法来阻止它被分配为临时端口。

        【讨论】:

          猜你喜欢
          • 2019-01-13
          • 1970-01-01
          • 1970-01-01
          • 2011-08-28
          • 1970-01-01
          • 2018-07-30
          • 1970-01-01
          • 2011-03-27
          • 1970-01-01
          相关资源
          最近更新 更多