☰
Linux conntrack 连接跟踪表实战:查看、删除、监听与调优
2026/10/16 10:22:55 网站建设 项目流程

1. 从一次丢包排查说起:conntrack到底在管什么

很多人第一次接触conntrack,都是在一次莫名其妙的丢包现场。业务方反馈"服务时通时不通",你登上机器ping网关是通的,telnet目标端口偶尔能连上,抓包看请求发出去了、回包也回来了,但应用层就是报超时。这时候如果dmesg里恰好飘过一行nf_conntrack: table full, dropping packet,那基本就锁定方向了——问题不在路由,也不在应用,而在内核那张看不见的"连接跟踪表"。

conntrack这个命令,本质上是给用户态提供的一个窗口,让你能查看、删除、监听内核 netfilter 的连接跟踪表(conntrack table)。这张表记录的是"一条连接的五元组状态",而不是简单的路由信息。这里要先纠正一个常见误解:标题里说的"路由连接跟踪表",严格讲 conntrack 跟踪的不是路由,而是经过本机的、被 netfilter 纳入跟踪的连接。路由决定包往哪走,conntrack 决定这条连接在内核里被怎么标记、怎么匹配、什么时候老化删除。两者是配合关系,不是一回事。

它解决的问题非常具体:NAT 转换要靠它、状态防火墙(state/conntrack匹配)要靠它、连接复用和会话保持也依赖它。代价是它要占内存,而且是有上限的。一旦表满,新连接直接被丢,表现就是"老连接好好的,新连接全挂"。所以会用conntrack命令,是每个跟网络打交道的运维和开发的基本功。

这篇内容适合三类人:一是被 conntrack 表满坑过的运维;二是写防火墙规则、做 NAT 网关的开发;三是想搞懂"为什么我的长连接会莫名断开"的后端同学。下面我会从表的结构讲起,再到查看、删除、监听、调优,把这条命令的实战用法和踩坑经验一次讲透。

2. 连接跟踪表里到底存了什么:字段逐项拆解

2.1 一条 conntrack 记录的完整字段

直接上命令,先看一条真实记录长什么样:

conntrack -L

输出大概是这样(我做了脱敏和简化):

tcp 6 431999 ESTABLISHED src=10.0.0.5 dst=10.0.0.9 sport=52344 dport=3306 src=10.0.0.9 dst=10.0.0.5 sport=3306 dport=52344 [ASSURED] mark=0 use=1

这一行信息量很大,逐段拆:

  • tcp 6:协议名和协议号,6 就是 TCP。
  • 431999:这条记录的剩余存活时间(秒),不是已存活时间。这个值会随着流量刷新,也会随时间递减,归零就被回收。
  • ESTABLISHED:连接状态。TCP 常见的有SYN_SENT、SYN_RECV、ESTABLISHED、FIN_WAIT、TIME_WAIT、CLOSE_WAIT等,和 TCP 状态机对应但不等同。
  • 两组src/dst/sport/dport:这是关键。第一组是原始方向(original),第二组是回复方向(reply)。NAT 场景下这两组地址会不一样,这正是 conntrack 能做地址转换的核心。
  • [ASSURED]:表示这条连接已经被"确认",即使表满也不会被优先淘汰。看到这个标记说明连接已经双向通信过。
  • mark=0:连接标记,配合iptables -m mark或策略路由用。
  • use=1:引用计数,有多少地方在引用这条记录。

提示:conntrack -L默认只显示 IPv4。要看 IPv6 得加-f ipv6,这个参数很多人第一次会漏。

2.2 original 和 reply 两组地址为什么重要

这是理解 conntrack 的钥匙。假设内网机器10.0.0.5:52344访问外网203.0.113.9:80,中间经过一台做 SNAT 的网关,网关出口 IP 是198.51.100.1。那么在网关上看到的记录是:

src=10.0.0.5 dst=203.0.113.9 sport=52344 dport=80 src=203.0.113.9 dst=198.51.100.1 sport=80 dport=某临时端口

注意 reply 方向的dst变成了网关出口 IP,dport变成了网关分配的临时端口。回包到达网关时,内核就是靠这张表把dst=198.51.100.1反查回10.0.0.5,完成反向转换。所以 conntrack 表一旦被清空或记录丢失,已建立的 NAT 连接就会立刻断掉——这也是为什么绝对不能在业务高峰期随便conntrack -F清表,这个坑我后面会专门讲。

2.3 表容量和内存占用怎么算

表的上限由内核参数nf_conntrack_max决定,当前值可以这样看:

sysctl net.netfilter.nf_conntrack_max cat /proc/sys/net/netfilter/nf_conntrack_count

count是当前已用条目数。内存占用有个经验公式:每条记录大约占 300 字节左右(不同内核版本有差异,IPv6 会更大)。所以如果max设成 100 万,理论上要预留 300MB 左右的内核内存。这个数字在做容量规划时很关键——我曾经见过有人把 max 调到 200 万,结果小内存机器直接 OOM,因为内核内存不是随便能换出来的。

哈希表大小由nf_conntrack_buckets控制,一般建议是 max 的 1/4 到 1/2。bucket 太小会导致哈希冲突严重,查找变慢,即使没满性能也会掉。

3. 查看与过滤:把几万条记录缩小到你要的那几条

3.1 基础过滤参数组合

生产机器上conntrack -L动辄几万行,直接刷屏没意义。必须学会过滤。常用参数:

参数作用示例
-p按协议过滤conntrack -L -p tcp
-s按源地址conntrack -L -s 10.0.0.5
-d按目的地址conntrack -L -d 10.0.0.9
--sport源端口conntrack -L --sport 52344
--dport目的端口conntrack -L --dport 3306
-f地址族conntrack -L -f ipv6
-o输出格式conntrack -L -o timestamp

组合起来,比如查某台机器到数据库的所有连接:

conntrack -L -p tcp -s 10.0.0.5 --dport 3306

-o timestamp会额外打印记录的创建时间戳,排查"这条连接是什么时候建立的"特别有用。默认输出没有时间,只能看剩余存活时间,反推建立时间很麻烦。

3.2 用 extended 格式看更多细节

conntrack -L -o extended

extended 格式会多出[UNREPLIED]、[ASSURED]、delta-time等信息。[UNREPLIED]表示只有原始方向有包、回复方向没包,这种记录往往是扫描或者单向探测留下的,排查异常流量时是重点对象。

3.3 统计视角:不逐条看,先看分布

有时候你不需要具体记录,只想知道"谁在疯狂建连接"。这时候配合awk做聚合:

conntrack -L -p tcp 2>/dev/null | awk '{print $5}' | sort | uniq -c | sort -rn | head -20

这条命令统计的是源地址出现次数,能快速定位连接数异常的机器。注意2>/dev/null,因为 conntrack 在读取过程中表在变化,会往 stderr 打一些提示,不影响结果但会干扰阅读。

注意:conntrack -L是快照式读取,读的过程中表在变,所以统计结果不是严格一致的。做精确统计建议用conntrack -C看总数,或者用-E事件模式采样。

4. 删除记录:什么时候该删,怎么删才不出事

4.1 删除单条与批量删除

删除是conntrack最危险也最常用的功能。基本用法:

conntrack -D -p tcp --dport 3306

这会删掉所有目的端口 3306 的 TCP 记录。删完之后,这些连接在内核里就"失忆"了,后续包会被当成新连接处理。对于 NAT 场景,这意味着已建立的会话直接断掉。

删除单条精确记录,要把五元组写全:

conntrack -D -p tcp -s 10.0.0.5 --sport 52344 -d 10.0.0.9 --dport 3306

4.2 为什么删记录要慎之又慎

我踩过最惨的一次坑:某台做 NAT 的网关,因为要"清理僵尸连接",有人写了个定时任务每小时conntrack -D -p tcp。结果每到整点,所有经过这台网关的长连接(包括数据库连接池、消息队列的持久连接)全部重建,业务出现周期性抖动,排查了两天才定位到这个定时任务。

正确的做法是:只删明确有问题的记录。比如某条连接卡在SYN_RECV很久、或者某个源 IP 在疯狂建连接需要临时压制,才定向删除。全量删除只在维护窗口做,而且要提前通知业务方。

4.3 删除后连接为什么可能"复活"

有个反直觉的现象:你删了一条记录,过一会儿conntrack -L又看到它了。原因通常是——连接还在,包还在来。你删掉记录后,下一个包到达时内核发现没有对应记录,如果这个包是 SYN 就会新建,如果是已建立连接的普通数据包,可能被丢弃也可能触发重建,取决于规则。所以删除不是"一劳永逸",源头流量不停,记录就会再生。要真正解决,得从流量侧或防火墙规则入手。

5. 监听事件:实时抓连接的生老病死

5.1 -E 事件模式怎么用

conntrack -E是实时监听模式,内核每新建、更新、销毁一条记录,都会往这里推事件:

conntrack -E

输出类似:

[NEW] tcp 6 120 SYN_SENT src=10.0.0.5 dst=10.0.0.9 sport=52344 dport=3306 [UPDATE] tcp 6 431999 ESTABLISHED src=... [DESTROY] tcp 6 120 TIME_WAIT src=...

NEW是新建,UPDATE是状态或超时刷新,DESTROY是销毁。这个模式对排查"连接为什么频繁重建"极其有用——你能亲眼看到连接在几秒内 NEW 了又 DESTROY,说明对端在快速断开重连。

5.2 过滤事件流,避免刷屏

-E同样支持过滤参数:

conntrack -E -p tcp --dport 3306

只监听数据库端口的事件。生产环境强烈建议加过滤,否则高并发机器上事件流能把终端刷爆,而且会消耗 CPU。

5.3 事件模式配合脚本做告警

一个实用玩法:统计单位时间内某端口的 NEW 事件数,超过阈值就告警。可以用简单管道:

timeout 10 conntrack -E -p tcp --dport 3306 2>/dev/null | grep -c NEW

10 秒内新建了多少条到 3306 的连接。这个数字如果远超正常业务量,基本就是连接泄漏或者被扫了。timeout是必须的,否则-E会一直挂着。

提示:-E模式在部分内核上需要nf_conntrack模块带事件通知支持,极老的内核可能不可用。遇到Operation not supported先确认内核版本。

6. 表满了怎么办:从应急到根治的完整链路

6.1 表满的典型症状与确认

症状很典型:新连接超时、dmesg出现nf_conntrack: table full, dropping packet、conntrack_count逼近nf_conntrack_max。确认命令:

cat /proc/sys/net/netfilter/nf_conntrack_count sysctl net.netfilter.nf_conntrack_max dmesg | grep -i conntrack | tail

6.2 应急处理:临时扩容

最快的止血是临时调大上限:

sysctl -w net.netfilter.nf_conntrack_max=1048576

但这只是把天花板抬高,如果连接泄漏的根因没解决,迟早还会满。而且调大之前要确认内存够,前面算过,100 万条约 300MB。

6.3 根治方向:找出谁在泄漏连接

扩容之后必须查根因。用第 3 节的聚合命令找出连接数最多的源 IP,再看这些连接的状态分布:

conntrack -L -p tcp -s 10.0.0.5 2>/dev/null | awk '{print $4}' | sort | uniq -c

如果大量是TIME_WAIT或CLOSE_WAIT,说明是应用没正确关闭连接;如果大量是SYN_SENT,可能是对端不可达导致连接堆积。定位到具体应用后,从代码或配置层面修,才是真正的解决。

6.4 缩短超时时间,加速回收

对于短连接为主的场景,可以适当缩短各状态的超时:

sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30 sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600

established默认是 5 天(432000 秒),对很多业务来说太长了。改成 1 小时能显著降低表占用,但要注意:改小 established 超时会让空闲的长连接被提前回收,如果你的业务有长时间空闲的持久连接,这个值不能调太小。这是典型的"调优即取舍"。

7. 几个容易翻车的细节和我的实操心得

7.1 容器环境下的 conntrack 是另一回事

在容器里跑conntrack -L,看到的往往是宿主机命名空间的表,或者干脆看不到。因为 conntrack 是 per-network-namespace 的。要查某个容器的连接,得进到对应的 netns:

nsenter -t <pid> -n conntrack -L

<pid>是容器主进程在宿主机上的 PID。这个坑我在做容器网络排查时踩过不止一次,明明容器里连接异常,宿主机conntrack -L却干干净净,就是因为看错了命名空间。

7.2 conntrack 和 iptables 规则的联动

iptables里的-m conntrack --ctstate匹配,用的就是这张表。如果你在规则里写了--ctstate ESTABLISHED,RELATED -j ACCEPT,那么 conntrack 表一旦异常,这条规则就匹配不上,已建立的连接也会被后续规则处理。所以调 conntrack 参数时,要连带考虑防火墙规则的行为变化。

7.3 别把 conntrack 当连接数监控用

有人喜欢用conntrack_count当连接数指标。它能反映趋势,但不等于真实连接数。因为一条 TCP 连接在 conntrack 里可能对应多条记录(比如 FTP 这种多端口协议),而且 UDP 也有记录(UDP 是无连接的,但 conntrack 会为它建伪连接)。做容量监控可以,做精确计费或审计不行。

7.4 我的参数配置参考

最后给一份我在中等规模网关上用过的配置,仅供参考,务必按自己环境调整:

net.netfilter.nf_conntrack_max = 524288 net.netfilter.nf_conntrack_buckets = 131072 net.netfilter.nf_conntrack_tcp_timeout_established = 7200 net.netfilter.nf_conntrack_tcp_timeout_time_wait = 60 net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60 net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 60 net.netfilter.nf_conntrack_udp_timeout = 30 net.netfilter.nf_conntrack_udp_timeout_stream = 120

buckets设成 max 的 1/4,哈希冲突在可接受范围。TCP 各超时按业务空闲特征收紧,UDP 流超时给到 120 秒是为了兼容一些基于 UDP 的长会话协议。这套配置在那台网关上跑了很久没出过表满问题,但换到短连接更密集的场景,max 还得往上加。

真正把 conntrack 用明白,靠的不是背参数,而是理解"它是一张有容量、有生命周期、和 NAT/防火墙深度绑定的状态表"。你每次-L、-D、-E,动的都是内核里活生生的连接状态,手要稳,心要细。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询