TCP同时打开和自连接预防

发布于 2024-10-19 07:11:56 字数 968 浏览 1 评论 0原文

TCP标准具有“同时开放”的特性。

该功能的含义是,当端口来自 短暂范围,偶尔可以连接到自身(请参阅此处)。

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

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

我想阻止这种情况的发生。我看到该问题有两种可能的解决方案:

  1. 不要将临时端口用作服务器端口。 同意临时端口范围并在您的计算机上配置它(请参阅临时范围
  2. 检查connect(),因为有人建议这里。

你觉得怎么样? 你如何处理这个问题?

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:

  1. Don't use ephemeral ports for server ports.
    Agree ephemeral port range and configure it on your machines (see ephemeral range)
  2. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

陈独秀 2024-10-26 07:11:56

当我偶然发现这一点时,我大吃一惊。我可以算出传出的
端口号意外匹配传入端口号,但不是 TCP 的原因
握手(SYN SYN-ACK 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?

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

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:

while true
do
    telnet 127.0.0.1 50000 
done

And wait for a minute or so and you will be chatting with yourself...

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?

Anyway, it makes good job interview material.

信愁 2024-10-26 07:11:56

将客户端套接字绑定到端口 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().

隔岸观火 2024-10-26 07:11:56

对于服务器,您需要将套接字绑定到端口。一旦 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.

冷…雨湿花 2024-10-26 07:11:56

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

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

使用启动应用程序启动服务器。如果服务器将绑定的目标端口正在被任何进程使用,请使用原始套接字创建 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 向 B 发送数据包到端口 X。

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

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

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

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

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

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

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)

You have to look at a "raw socket" packet generator.
And you have to be superuser.
You probably need a network sniffer as well.

http://en.wikipedia.org/wiki/Raw_socket
http://kerneltrap.org/node/3072 - TCP RST attacks
http://search.cpan.org/dist/Net-RawIP/lib/Net/RawIP.pm - a Perl module
http://mixter.void.ru/rawip.html - raw IP in C

In the C version, you want a TH_RST packet.

RST is designed to handle the following case.

A and B establish a connection.
B reboots, and forgets about this.
A sends a packet to B to port X from port Y.

B sends a RST packet back, saying "what are you talking about? I don't
have a connection with you. Please close this connection down."

So you have to know/fake the IP address of B, and know both ports X
and Y. One of the ports will be the well known port number. The other
you have to find out. I thnk you also need to know the sequence
number.

Typically people do this with a sniffer. You could use a switch with a
packet mirroring function, or run a sniffer on either host A or B.

As a note, Comcast did this to disable P2P traffic.
http://www.eff.org/wp/packet-forgery-isps-report-comcast-affair

In our case we don't need to use a sniffer since we know the information below:

So you have to know/fake the IP address of B, and know both ports X
and Y

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.

西瑶 2024-10-26 07:11:56

嗯,这是一个奇怪的问题。如果你在同一台机器上有一个客户端/服务器,并且它总是在同一台机器上,那么共享内存、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.

蓝色星空 2024-10-26 07:11:56

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

如果您更关心与服务器的实际连接是否可用,我建议将您的客户端移至另一台计算机。通过这种方式,您可以验证您的服务器至少具有一些网络连接。

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.

如日中天 2024-10-26 07:11:56

在我看来,这是 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.

云裳 2024-10-26 07:11:56

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

该问题的解决方案是在套接字上设置 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.

悲念泪 2024-10-26 07:11:56

大多数 TCP 中实际上并未实现此选项。您有实际问题吗?

This option isn't actually implemented in most TCPs. Do you have an actual problem?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文