当使用 curl 和 wget 时,如果未手动设置源端口(例如使用 curl 中的 --local-port),则源 TCP 端口始终是偶数,并且将增加 2,而不是 1。
例如:在 tcpdump 中,当我建立连接时,我看到源端口 45080 被使用,然后下一个连接将使用 45082,而不是 45801。使用本地端口我可以强制它使用奇数端口,并且 tcpdump 确认这些端口正在成功使用。
这导致我在网络测试平台上遇到问题,我无论如何也想不出是什么在控制隐式 TCP 端口选择。我可以更改范围,但无法更改“增量”。
使用 centos7 和“3.10.0-514.el7.x86_64”内核。
在 tcpdump 中,我看到使用 wget 和 curl 的相同行为,这使我相信这不是 curl 特定的问题,而是用于选择端口的底层机制。
另外,如果我看到 curl 使用端口 45080,我知道它将使用的下一个端口是 45082。如果我强制 --local-port 为 50000,然后再次 curl 而不使用 --local-port,它将是 45082,就像一个单独的计数器增加了它,不受“最后”使用的端口的影响。
或者,如果执行与上述步骤相同的操作,但不是强制使用端口 50000,而是强制它使用 45082(它本来应该选择的端口),然后我再次使用 curl 而不强制使用本地端口,它将选择 45083,然后如果再次选择 45084,然后选择 45086...
让它自然选择奇数的唯一其他方法是将范围限制为奇数个端口。
是否存在系统调用,或者某种用于选择源端口的内核操作,有没有办法改变它?
谢谢!
答案1
我相信我能够证明内核本身是通过这种方式增加端口的。
Strace 显示 wget 和 curl 都调用 connect() 而没有调用 bind() 来明确设置源端口。
我编写了一个 python 脚本来模拟同样的事情,似乎 3.1 内核增加了 2,而 2.6 内核没有。
有趣的是,这仅在使用 connect() 时才成立,如果您绑定在端口 0 上,从而让内核选择下一个可用端口,这几乎是随机的。
我的脚本:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('10.28.0.2', 80))
addr = s.getsockname()
print addr[1]
s.close()
输出:
$ python port_test.py
45008
$ python port_test.py
45010
$ python port_test.py
45012