我们在 FreeBSD 网络服务器上实际用的 sysctl 调优

每台新装的 FreeBSD 网络服务器,我们都会往 /etc/sysctl.conf 里放一份固定的调优清单。它不是从哪篇帖子里抄来一股脑贴上的——里面每一项要么是为了提高吞吐,要么是为了收紧攻击面,少数几项还依赖内核编译选项。这篇把这份清单原样列出来,并说清楚其中真正重要的几类参数为什么在、以及哪些要小心。

一句前提:这些值适用于带宽充足、以服务端连接为主的机器。如果你的机器跑在丢包严重的链路上,或者是面向拨号/弱网用户的接入侧,有几项(下面会点名)反而会帮倒忙,别照抄。

收发缓冲区

把 TCP/UDP 的收发缓冲区从默认值放大,是高带宽链路上吃满带宽时延积(BDP)的前提——缓冲区太小,窗口撑不开,单连接吞吐就上不去。

# 最大的待发送 TCP 数据缓冲区空间
net.inet.tcp.sendspace=65536
# 最大的接收 TCP 缓冲区空间
net.inet.tcp.recvspace=65536
# 最大的接收 UDP 缓冲区大小
net.inet.udp.sendspace=65535
# 最大的发送 UDP 数据缓冲区大小
net.inet.udp.maxdgram=65535
# 本地套接字连接的数据发送空间
net.local.stream.sendspace=65535
# 最大的套接字缓冲区
kern.ipc.maxsockbuf=2097152

kern.ipc.maxsockbuf 是套接字缓冲区的硬上限,它必须足够大,上面那些 per-socket 的值才可能真正生效;2MB 给窗口自动扩展留了足够空间。

加快网络性能的 RFC 开关

net.inet.tcp.rfc1323=1
net.inet.tcp.rfc1644=1
net.inet.tcp.rfc3042=1
net.inet.tcp.rfc3390=1

这里面最关键的是 rfc1323(窗口缩放和时间戳)——没有它,TCP 窗口被 64KB 卡死,在高 BDP 链路上再大的 sendspace 也白搭。rfc3390 放大初始拥塞窗口,让短连接更快进入正常传输。rfc1644(T/TCP)比较老,现代环境用处不大,留着无害但别指望它。

文件描述符

高并发服务要开很多 socket 和文件,默认的描述符上限很快就不够:

# 系统中允许的最多文件数量
kern.maxfiles=65536
# 每个进程能够同时打开的最大文件数量
kern.maxfilesperproc=32768

TCP 行为与 ICMP 加固

# 关闭延迟 ACK:高速低负载下略微提速。
# 但注意——在链路较差时对方收不到应答会反复重连,反而降速,弱网环境应保持默认开启。
net.inet.tcp.delayed_ack=0

# 屏蔽 ICMP 重定向
net.inet.icmp.drop_redirect=1
net.inet.icmp.log_redirect=1
net.inet.ip.redirect=0
net.inet6.ip6.redirect=0

# 防止 ICMP 广播风暴
net.inet.icmp.bmcastecho=0
net.inet.icmp.maskrepl=0

# 限制系统发送 ICMP 速率
net.inet.icmp.icmplim=100

delayed_ack=0 是把双刃剑,特意标出来:它在干净的数据中心内网里能省一点延迟,但面向公网弱网用户时会放大重传,适得其反。我们只在内部高速链路上关它。

关掉 ICMP 重定向和广播回应是基础加固,防止攻击者用重定向篡改路由、或用广播 echo 做放大。

需要内核选项配合的安全参数

下面两项必须在编译内核时加了 options TCP_DROP_SYNFIN 才能用,否则设了也不生效:

net.inet.icmp.icmplim_output=0
net.inet.tcp.drop_synfin=1

drop_synfin=1 丢弃同时带 SYN 和 FIN 的畸形包,这类包常被用于操作系统指纹识别,丢掉能降低被扫描探测的信息量。

清理死连接与队列

# 帮助系统清除没有正常断开的 TCP 连接。会增加少量带宽占用,
# 但能最终识别并清掉死连接——拨号用户常直接断 modem 而不正常关连接,这类问题尤其明显。
net.inet.tcp.always_keepalive=1

# 若看到 net.inet.ip.intr_queue_drops 在增长,就调大这个值,目标是把 drops 压到 0
net.inet.ip.intr_queue_maxlen=1000

intr_queue_maxlen 是少数应该“看指标再调”的参数:平时放着,一旦监控里 net.inet.ip.intr_queue_drops 开始涨,说明网络中断处理队列溢出了,把它加大。

防 DoS 与端口黑洞

# 防 DoS:缩短 msl,默认 30000
net.inet.tcp.msl=7500

# 收到发往已关闭端口的包直接 drop;2 表示 TCP/UDP 都处理,1 仅针对 TCP
net.inet.tcp.blackhole=2
# 收到发往已关闭端口的 UDP 包直接 drop
net.inet.udp.blackhole=1

# 为网络数据连接提供缓冲(inflight 限速)
net.inet.tcp.inflight.enable=1

# 防 DoS
net.inet.tcp.syncookies=1

blackhole 很值得开:默认情况下访问一个关闭的端口会回 RST,这既给扫描器确认了端口状态,也在被 SYN 泛洪时浪费资源;设成 2 让内核直接静默丢弃,端口扫描看到的是“无响应”而不是“关闭”。syncookies=1 让机器在半开连接队列被打满时仍能继续接受合法连接,是抗 SYN 泛洪的标配。

转发、polling 与并发

# 打开后每个目标地址转发成功就记进路由表和 arp 表,省路由计算时间,
# 但会吃大量内核内存来存路由表。我们保持关闭。
net.inet.ip.fastforwarding=0

## 内核编译打开 options POLLING 才能用,高负载下使用,低负载不推荐
## SMP 不能和 polling 一起用
#kern.polling.enable=1

# 并发连接数(listen backlog 上限),默认 128,推荐 1024-4096,越大越吃内存
kern.ipc.somaxconn=32768

fastforwarding 我们默认关掉——它是纯路由器场景的优化,对一台存储/服务节点没必要,还白占内存。polling 整段注释掉:现代多核 + 中断聚合(如 Mellanox 这类网卡)下,polling 基本是上个时代的东西,而且和 SMP 冲突,这里只作为历史记录留着。somaxconn 调大是为了让高并发短连接服务的 accept 队列不被打满。

进程与内核安全

# 禁止用户查看其他用户的进程
security.bsd.see_other_uids=0
# 设置 kernel 安全级别
kern.securelevel=0
# 记录任何 TCP / UDP 连接尝试
net.inet.tcp.log_in_vain=1
net.inet.udp.log_in_vain=1
# 校验 UDP 包,防畸形 UDP 攻击
net.inet.udp.checksum=1
# 程序崩溃时不生成 core dump
kern.coredump=0

log_in_vain=1 会把打到无监听端口的连接尝试记进日志,对排查扫描和探测有用;但在被持续扫描时日志会刷得很快,生产上要配合日志轮转。kern.coredump=0 关掉 core dump,避免敏感内存被写到磁盘,也省空间。

共享内存(IPC)

# 仅为线程提供物理内存支持,需要 256MB 以上内存
kern.ipc.shm_use_phys=1
# 线程可使用的最大共享内存
kern.ipc.shmmax=67108864
# 最大线程数量
kern.ipc.shmall=32768

这几项主要是给用共享内存的服务(比如数据库)准备的,shm_use_phys=1 把共享内存锁在物理内存里、不进 swap,对数据库类负载有实际收益。

本地套接字与 MSS

# lo 本地数据流接收和发送空间
net.local.stream.recvspace=65536
net.local.dgram.maxdgram=16384
net.local.dgram.recvspace=65536

# 数据包数据段大小,ADSL 为 1452
net.inet.tcp.mssdflt=1460
# 为网络数据连接提供缓冲
net.inet.tcp.inflight_enable=1
# 数据包数据段最小值,ADSL 为 1452
net.inet.tcp.minmss=1460

# 本地数据最大数量
net.inet.raw.maxdgram=65536
# 本地数据流接收空间
net.inet.raw.recvspace=65536

mssdflt=1460 对应标准以太网 1500 MTU;如果你的链路是 PPPoE/ADSL,这里应该用 1452,否则会触发分片。我们的机器都在以太网内网,所以用 1460。

防火墙动态规则

# ipfw 防火墙动态规则数量,默认 4096,增大可防止某些病毒发大量 TCP 连接把表打满
net.inet.ip.fw.dyn_max=65535
# ipf 防火墙 TCP 连接空闲保留时间,默认 8640000(120 小时)
net.inet.ipf.fr_tcpidletimeout=864000

怎么用这份清单

我们不建议把上面所有行一次性全贴进 /etc/sysctl.conf 就完事。正确的做法是:缓冲区、RFC 开关、文件描述符、somaxconn、黑洞和 syncookies 这几组可以放心用,是通用的;delayed_ack=0、fastforwarding、polling 以及依赖内核选项的 drop_synfin 这几项有前提,要对照自己的场景判断;intr_queue_maxlen 和防火墙动态规则则应该看着监控指标来调。改完用 sysctl -a 确认生效,然后在真实流量下观察有没有丢包或异常——调优是个看数据迭代的过程,不是贴一次就完的事。

Published At (Updated At) Tagged with