网络端口预留

最近这些天我在做内核的一个新功能:/proc/sys/net/ipv4/ip_local_reserved_ports,这个文件的作用是告诉内核保留一些指定的端口,这样以来对于那些使用固定端口号的第三方应用程序来说就能保证它们总是能成功使用这些端口,而不是被内核自动分配端口时抢占。

我们知道在调用比如 bind(2) 时指定端口0其实是告诉内核自动去分配一个可用的端口,这个端口是随机的,而且范围是在 /proc/sys/net/ipv4/ip_local_port_range 指定的之内。不光 bind(2),你调用 connect(2) 时也会自动获得一个端口,它也是这样得到的。所以,如果我们都使用端口0去让内核分配端口世界会很和谐,各个程序相安无事,但是很多服务器程序需要有个固定的端口的,随机分配的端口是不能接受的,于是就有了那些已知的固定端口号。问题还没解决,如果我们的服务器程序的端口号不在里面怎么办?去 IANA 申请吧,不至于,可能你的程序不够知名,或者 IANA 没有批准等等申请不上,怎么办?

一个方法是使用那个 ip_local_port_range ,把范围调整到不包含你的端口的范围。这样做有个明显的缺点,如果你的端口号正好在当前 ip_local_port_range 的正中间,那样会有一半的端口都要被排除掉了,而且很明显 ip_local_port_range 的本意也不让你做这种事的。于是就有了 ip_local_reserved_ports,引入它的目的就是为了让你在这种情况下预留端口。

我写的最初的补丁只支持像 ip_local_port_range 那样的输入格式,比如:”50000 50100”,但是 Octavian 觉得这种格式灵活性不高,如果能支持指定多个任意范围的端口号那就更好了。于是他接过我的补丁继续做,从我的 v2 一直到现在的 v6,期间经过了多次讨论和测试,现在基本上已经成熟了。如果不出意外,应该可以汇入到主线内核中。新的 ip_local_reserved_ports 可以支持“50000,50100-50200” 这种格式,可以非常方便地指定要预留的单个端口或者端口范围。

注意,ip_local_reserved_ports 和 ip_local_port_range 关系不大,前者里的端口完全可以在后者的范围之外,这么设计一是为了简单,二是为了防止竞争,你可以先把 ip_local_port_range 调整到你预留的端口范围之外,然后等写好预留端口的列表之后再把前面的范围调回去。比如我们可以这么写代码:
[bash]
from=cut -f1 /proc/sys/net/ipv4/ip_local_port_range
to=cut -f2 /proc/sys/net/ipv4/ip_local_port_range
if [ “$1” -lt $to ] && [ “$1” -gt $from ]; then
echo “$from $[$1-1]” > /proc/sys/net/ipv4/ip_local_port_range
fi
original=cat /proc/sys/net/ipv4/ip_local_reserved_ports
original=”$original, $1”
echo “$original” > /proc/sys/net/ipv4/ip_local_reserved_ports
echo “$from $to” > /proc/sys/net/ipv4/ip_local_port_range
[/bash]