TCP同时打开和自连接预防
TCP标准具有“同时开放”的特性。
该功能的含义是,当端口来自 短暂范围,偶尔可以连接到自身(请参阅此处)。
所以客户端认为它连接到服务器,但实际上它连接到它自己。从另一边来看,服务器无法打开其服务器端口,因为它被客户端占用/窃取。
我正在使用 RHEL 5.3,我的客户端不断尝试连接到本地服务器。 最终客户端连接到自己。
我想阻止这种情况的发生。我看到该问题有两种可能的解决方案:
你觉得怎么样? 你如何处理这个问题?
PS 1
除了我显然正在寻找的解决方案之外, 我希望您能分享您在现实生活中遇到的问题的经验。
当我找到问题的原因时,我对我的工作场所的人并不熟悉感到“惊讶”。恕我直言,通过定期连接来轮询服务器是常见的做法, 那么为什么这个问题并不为人所知。
TCP standard has "simultaneous open" feature.
The implication of the feature, client trying to connect to local port, when the port is from ephemeral range, can occasionally connect to itself (see here).
So client think it's connected to server, while it actually connected to itself. From other side, server can not open its server port, since it's occupied/stolen by client.
I'm using RHEL 5.3 and my clients constantly tries to connect to local server.
Eventually client connects to itself.
I want to prevent the situation. I see two possible solutions to the problem:
- Don't use ephemeral ports for server ports.
Agree ephemeral port range and configure it on your machines (see ephemeral range) - Check connect() as somebody propose here.
What do you thinks?
How do you handle the issue?
P.S. 1
Except of the solution, which I obviously looking for,
I'd like you to share your real life experience with the problem.
When I found the cause of the problem, I was "astonished" on my work place people are not familiar with it. Polling server by connecting it periodically is IMHO common practice,
so how it's that the problem is not commonly known.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
当我偶然发现这一点时,我大吃一惊。我可以算出传出的
端口号意外匹配传入端口号,但不是 TCP 的原因
握手(SYN SYN-ACK ACK)将会成功(问问自己:谁在发送 ACK,如果
没有人执行listen() 和accept()???)
Linux 和FreeBSD 都显示这种行为。
无论如何,一种解决方案是远离服务器的大范围端口号。
我注意到达尔文通过不允许传出端口来回避这个问题
与目的港相同。他们一定也被这个咬伤了......
展示这种效果的一个简单方法如下:
然后等待一分钟左右,你就会和自己聊天......
无论如何,它是很好的工作面试材料。
When I stumbled into this I was flabbergasted. I could figure out that the outgoing
port number accidentally matches the incoming port number, but not why the TCP
handshake (SYN SYN-ACK ACK) would succeed (ask yourself: who is sending the ACK if
there is nobody doing a listen() and accept()???)
Both Linux and FreeBSD show this behavior.
Anyway, one solution is to stay out of the high range of port numbers for servers.
I noticed that Darwin side-steps this issue by not allowing the outgoing port
to be the same as the destination port. They must have been bitten by this as well...
An easy way to show this effect is as follows:
And wait for a minute or so and you will be chatting with yourself...
Anyway, it makes good job interview material.
将客户端套接字绑定到端口 0(系统分配),检查系统分配的端口,如果它与本地服务器端口匹配,则您已经知道服务器已关闭并且可以跳过 connect()。
Bind the client socket to port 0 (system assigns), check the system assigned port, if it matches the local server port you already know the server is down and and can skip connect().
对于服务器,您需要将套接字绑定到端口。一旦 addr:port 对绑定了套接字,它将不再用于 connect() 中的隐式绑定。
没问题,没麻烦。
For server you need to bind() socket to port. Once addr:port pair had socket bound, it will no longer be used for implicit binding in connect().
No problem, no trouble.
请注意,这个解决方案是理论上的,我没有亲自测试过。我以前没有经历过(或者没有意识到),希望我不会再经历它。
我假设您既不能编辑客户端源代码也不能编辑服务器源代码。另外,我假设真正的问题是服务器无法启动。
使用启动应用程序启动服务器。如果服务器将绑定的目标端口正在被任何进程使用,请使用原始套接字创建 RST(重置数据包)。
下面的文章简要描述了 RST 数据包是什么(取自 http: //forum.soft32.com/linux/killing-socket-connection-cmdline-ftopict473059.html)
在我们的例子中,我们不需要使用嗅探器,因为我们知道以下信息:
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 数据包。
Note that this solution is theoretical and I have not tested it on my own. I've not experienced it before (or did not realize) and hopefully I won't experience it anymore.
I'm assuming that you cannot edit neither the client source code nor the server source. Additionally I'm assuming the real problem is the server which cannot start.
Launch the server with a starter application. If the target port that the server will bind is being used by any process, create an RST (reset packet) by using raw sockets.
The post below briefly describes what an RST packet is (taken from http://forum.soft32.com/linux/killing-socket-connection-cmdline-ftopict473059.html)
In our case we don't need to use a sniffer since we know the information below:
X = Y and B's IP address is localhost
Tutorial on http://mixter.void.ru/rawip.html describes how to use Raw Sockets.
NOTE that any other process on the system might also steal our target port from ephemeral pool. (e.g. Mozilla Firefox) This solution will not work on this type of connections since X != Y B's IP address is not localhost but something like 192.168.1.43 on eth0. In this case you might use netstat to retrieve X, Y and B's IP address and then create a RST packet accordingly.
嗯,这是一个奇怪的问题。如果你在同一台机器上有一个客户端/服务器,并且它总是在同一台机器上,那么共享内存、Unix 域套接字或其他形式的 IPC 可能是更好的选择。
其他选项是在固定端口上运行服务器,在固定源端口上运行客户端。比如说,服务器在 5000 上运行,客户端在 5001 上运行。如果有其他东西绑定到它们,那么您确实会遇到绑定到其中任何一个的问题。
您可以在偶数端口号上运行服务器,并强制客户端使用奇数端口号。在短暂范围内选择一个随机数,将其与 1 或,然后用它调用 bind() 。如果bind()因EADDRINUSE而失败,则选择不同的奇数端口号并重试。
Hmm, that is an odd problem. If you have a client / server on the same machine and it will always be on the same machine perhaps shared memory or a Unix domain socket or some other form of IPC is a better choice.
Other options would be to run the server on a fixed port and the client on a fixed source port. Say, the server runs on 5000 and the client runs on 5001. You do have the issue of binding to either of these if something else is bound to them.
You could run the server on an even port number and force the client to an odd port number. Pick a random number in the ephemeral range, OR it with 1, and then call bind() with that. If bind() fails with EADDRINUSE then pick a different odd port number and try again.
这是一个有趣的问题!如果您最关心服务器是否正在运行,则始终可以在服务器本身中实现心跳机制,以向另一个进程报告状态。或者您可以编写一个脚本来检查服务器进程是否正在运行。
如果您更关心与服务器的实际连接是否可用,我建议将您的客户端移至另一台计算机。通过这种方式,您可以验证您的服务器至少具有一些网络连接。
That's an interesting issue! If you're mostly concerned that your server is running, you could always implement a heartbeat mechanism in the server itself to report status to another process. Or you could write a script to check and see if your server process is running.
If you're concerned more about the actual connection to the server being available, I'd suggest moving your client to a different machine. This way you can verify that your server at least has some network connectivity.
在我看来,这是 TCP 规范中的一个错误;侦听套接字不应该能够发送未经请求的 SYN,并且在发送后接收 SYN(而不是 SYN+ACK)应该是非法的并导致重置,这将很快让客户端关闭不幸选择的套接字本地端口。但没有人询问我的意见;)
正如您所说,明显的答案是不要在临时端口范围内监听。如果您知道将连接到本地计算机,则另一种解决方案是设计协议,以便服务器发送第一条消息,并在客户端接收该消息时设置短暂的超时。
In my opinion, this is a bug in the TCP spec; listening sockets shouldn't be able to send unsolicited SYNs, and receiving a SYN (rather than a SYN+ACK) after you've sent one should be illegal and result in a reset, which would quickly let the client close the unluckily-chosen local port. But nobody asked for my opinion ;)
As you say, the obvious answer is not to listen in the ephemeral port range. Another solution, if you know you'll be connecting to a local machine, is to design your protocol so that the server sends the first message, and have a short timeout on the client side for receiving that message.
您遇到的实际问题似乎是,当服务器关闭时,其他东西可以使用您期望服务器的临时端口作为传出连接的源端口。这种情况如何发生的细节与实际问题是分开的,并且它可能以与您描述的方式不同的方式发生。
该问题的解决方案是在套接字上设置 SO_REUSEADDR。这将允许您在具有当前传出连接的端口上创建服务器。
如果您确实关心该端口号,则可以使用特定的操作方法来阻止将其分配为临时端口。
The actual problem you are having seems to be that while the server is down, something else can use the ephemeral port you expect for your server as the source port for an outgoing connection. The detail of how that happens is separate to the actual problem, and it can happen in ways other than the way you describe.
The solution to that problem is to set SO_REUSEADDR on the socket. That will let you create a server on a port that has a current outgoing connection.
If you really care about that port number, you can use operating specific methods to stop it being allocated as an ephemeral port.
大多数 TCP 中实际上并未实现此选项。您有实际问题吗?
This option isn't actually implemented in most TCPs. Do you have an actual problem?