第 07 章:raw 表与连接跟踪优化
第 07 章:raw 表与连接跟踪优化
本章目标:理解 raw 表在连接跟踪体系中的位置,掌握 NOTRACK 的使用方法,学会优化大规模环境下的连接跟踪性能。
7.1 raw 表概述
raw 表是 iptables 中优先级最高的表,它在连接跟踪(conntrack)模块处理之前执行。其核心用途是控制哪些数据包需要进行连接跟踪。
7.1.1 raw 表在数据包处理中的位置
数据包进入内核
│
▼
┌──────────────┐
│ raw 表 │ ← 优先级最高,在 conntrack 之前
│ PREROUTING │ 可以决定是否跳过连接跟踪
└──────┬───────┘
│
▼
┌──────────────┐
│ 连接跟踪 │ ← nf_conntrack 处理
│ (conntrack) │ 记录连接状态
└──────┬───────┘
│
▼
┌──────────────┐
│ mangle 表 │
│ nat 表 │
│ filter 表 │
└──────────────┘
7.1.2 raw 表支持的链
| 链 | 用途 |
|---|---|
PREROUTING | 在入站数据包进入 conntrack 之前处理 |
OUTPUT | 在本机产生的数据包进入 conntrack 之前处理 |
注意:raw 表只有两条链,不支持 INPUT、FORWARD、POSTROUTING。
7.2 NOTRACK 目标
7.2.1 NOTRACK 的作用
NOTRACK(或 --jump NOTRACK)告诉内核不对匹配的数据包进行连接跟踪。被标记为 NOTRACK 的数据包:
- 不进入 conntrack 表:不占用 conntrack 条目
- 没有连接状态:不能使用
-m conntrack --ctstate匹配 - 不能使用 NAT:NAT 依赖连接跟踪,NOTRACK 的包不能做 NAT
- 性能提升:减少了内核处理开销
7.2.2 NOTRACK 基本用法
# 不跟踪所有 UDP 53 流量(DNS 查询通常很短)
iptables -t raw -A PREROUTING -p udp --dport 53 -j NOTRACK
iptables -t raw -A OUTPUT -p udp --dport 53 -j NOTRACK
# 不跟踪特定主机的流量
iptables -t raw -A PREROUTING -s 10.0.0.100 -j NOTRACK
iptables -t raw -A OUTPUT -d 10.0.0.100 -j NOTRACK
# 不跟踪 NFS 流量(高吞吐场景)
iptables -t raw -A PREROUTING -p tcp --dport 2049 -j NOTRACK
iptables -t raw -A PREROUTING -p udp --dport 2049 -j NOTRACK
iptables -t raw -A OUTPUT -p tcp --dport 2049 -j NOTRACK
iptables -t raw -A OUTPUT -p udp --dport 2049 -j NOTRACK
7.2.3 NOTRACK 的影响
使用 NOTRACK 前:
┌───────────┐ ┌───────────┐ ┌───────────────┐
│ 数据包 │────→│ conntrack │────→│ mangle/nat/ │
│ │ │ 记录连接 │ │ filter 处理 │
└───────────┘ └───────────┘ └───────────────┘
│
▼
conntrack 表占用内存
可使用 -m conntrack --ctstate
可使用 NAT
使用 NOTRACK 后:
┌───────────┐ ┌───────────────────┐
│ 数据包 │────→│ mangle/nat/filter │
│ │ │ 处理 │
└───────────┘ └───────────────────┘
│
▼
不经过 conntrack
不能使用 -m conntrack --ctstate
不能使用 NAT
节省内核内存和 CPU
7.3 连接跟踪机制
7.3.1 conntrack 模块简介
连接跟踪(Connection Tracking)是 Netfilter 的核心模块,它为每个经过防火墙的网络连接维护一条记录,包括:
| 字段 | 说明 |
|---|---|
| 源/目的 IP | 连接双方的地址 |
| 源/目的端口 | 连接双方的端口 |
| 协议 | TCP/UDP/ICMP 等 |
| 状态 | NEW/ESTABLISHED/RELATED/INVALID |
| 超时时间 | 不同协议的超时时间不同 |
| NAT 信息 | SNAT/DNAT 映射关系 |
7.3.2 查看 conntrack 信息
# 查看所有连接跟踪条目
conntrack -L
# 按协议过滤
conntrack -L -p tcp
# 实时监控新建连接
conntrack -E
# 统计连接跟踪条目数量
conntrack -C
# 查看 conntrack 模块参数
sysctl -a | grep net.netfilter.nf_conntrack
7.3.3 conntrack 表大小
# 查看当前连接跟踪表大小
sysctl net.netfilter.nf_conntrack_max
# net.netfilter.nf_conntrack_max = 65536
# 查看当前已使用的条目数
cat /proc/sys/net/netfilter/nf_conntrack_count
# 查看 conntrack 表占用的内存
grep -i conntrack /proc/slabinfo
7.4 连接跟踪性能优化
7.4.1 调整 conntrack 表大小
# 计算所需内存(每条记录约 300 字节)
# 100 万条连接 ≈ 300MB 内存
# 26 万条连接 ≈ 78MB 内存
# 增大连接跟踪表(适合高并发服务器)
sysctl -w net.netfilter.nf_conntrack_max=262144
# 持久化配置
echo "net.netfilter.nf_conntrack_max = 262144" >> /etc/sysctl.conf
# 增大 hash 表桶数(应为 max 的 1/4 到 1/8)
echo 65536 > /sys/module/nf_conntrack/parameters/hashsize
# 或
echo "options nf_conntrack hashsize=65536" >> /etc/modprobe.d/conntrack.conf
7.4.2 调整超时时间
# 查看当前超时设置
sysctl -a | grep net.netfilter.nf_conntrack.*timeout
# TCP 超时参数
sysctl -a | grep net.netfilter.nf_conntrack_tcp_timeout
| 参数 | 默认值 | 建议值 | 说明 |
|---|---|---|---|
tcp_timeout_syn_sent | 120 | 30 | SYN 等待时间 |
tcp_timeout_syn_recv | 60 | 15 | SYN+ACK 等待时间 |
tcp_timeout_established | 432000 | 3600 | 已建立连接的超时(5天→1小时) |
tcp_timeout_fin_wait | 120 | 30 | FIN_WAIT 状态超时 |
tcp_timeout_close_wait | 60 | 15 | CLOSE_WAIT 状态超时 |
tcp_timeout_time_wait | 120 | 30 | TIME_WAIT 状态超时 |
udp_timeout | 30 | 10 | UDP 流超时 |
udp_timeout_stream | 180 | 60 | UDP 流超时 |
icmp_timeout | 30 | 10 | ICMP 超时 |
generic_timeout | 600 | 120 | 其他协议超时 |
# 优化 TCP 超时(高并发 Web 服务器)
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_fin_wait=30
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_close_wait=15
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_syn_sent=30
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_syn_recv=15
# 优化 UDP 超时
sysctl -w net.netfilter.nf_conntrack_udp_timeout=10
sysctl -w net.netfilter.nf_conntrack_udp_timeout_stream=60
7.4.3 监控 conntrack 状态
#!/bin/bash
# conntrack 监控脚本
# 获取当前使用量和最大值
COUNT=$(cat /proc/sys/net/netfilter/nf_conntrack_count)
MAX=$(cat /proc/sys/net/netfilter/nf_conntrack_max)
USAGE=$((COUNT * 100 / MAX))
echo "conntrack 使用率: $COUNT / $MAX ($USAGE%)"
# 如果使用率超过 80%,发出警告
if [ $USAGE -gt 80 ]; then
echo "WARNING: conntrack 使用率过高!"
# 记录到系统日志
logger -t conntrack-monitor "WARNING: Usage $USAGE% ($COUNT/$MAX)"
fi
# 查看连接状态分布
echo ""
echo "连接状态分布:"
conntrack -L 2>/dev/null | awk '{print $4}' | sort | uniq -c | sort -rn | head -10
7.5 业务场景优化方案
7.5.1 高并发 Web 服务器
#!/bin/bash
# ═══════════════════════════════════════════════════
# 高并发 Web 服务器连接跟踪优化
# ═══════════════════════════════════════════════════
# ─── 1. 调整内核参数 ───
# 增大 conntrack 表
sysctl -w net.netfilter.nf_conntrack_max=524288
echo 131072 > /sys/module/nf_conntrack/parameters/hashsize
# 缩短超时时间
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=1800
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=15
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_fin_wait=15
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_close_wait=10
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_syn_sent=15
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_syn_recv=10
# ─── 2. raw 表跳过不需要跟踪的流量 ───
# 跳过内网心跳检测(如 keepalived VRRP)
iptables -t raw -A PREROUTING -p vrrp -j NOTRACK
iptables -t raw -A OUTPUT -p vrrp -j NOTRACK
# 跳过内网集群通信(如 Redis 哨兵)
iptables -t raw -A PREROUTING -p tcp --dport 26379 -j NOTRACK
iptables -t raw -A OUTPUT -p tcp --dport 26379 -j NOTRACK
7.5.2 DNS 缓存服务器
#!/bin/bash
# DNS 服务器不需要对 UDP 53 做连接跟踪
# DNS 查询通常是无状态的 UDP 一问一答
# 跳过 DNS 查询的连接跟踪
iptables -t raw -A PREROUTING -p udp --dport 53 -j NOTRACK
iptables -t raw -A OUTPUT -p udp --sport 53 -j NOTRACK
# 注意:如果使用了 NAT,不能对 DNS 流量使用 NOTRACK
7.5.3 视频流媒体服务器
#!/bin/bash
# 视频流媒体服务器的优化
# 大量 UDP 流媒体流量会快速填满 conntrack 表
# 跳过 RTMP 流量的连接跟踪
iptables -t raw -A PREROUTING -p tcp --dport 1935 -j NOTRACK
iptables -t raw -A OUTPUT -p tcp --dport 1935 -j NOTRACK
# 跳过 HLS/DASH 分片下载(大量短连接)
iptables -t raw -A PREROUTING -p tcp --dport 8080 -j NOTRACK
iptables -t raw -A OUTPUT -p tcp --dport 8080 -j NOTRACK
# 注意:如果使用了 NOTRACK,必须在 filter 表中显式添加允许规则
# 因为不能使用 -m conntrack --ctstate ESTABLISHED,RELATED
iptables -A INPUT -p tcp --dport 1935 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
7.6 raw 表与其他表的协作
7.6.1 NOTRACK 后的规则编写
当数据包被 NOTRACK 标记后,它没有连接状态,因此:
# ❌ 这条规则不会匹配 NOTRACK 的数据包
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# ✅ 正确做法:显式匹配双向流量
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 80 -j ACCEPT
7.6.2 混合模式:部分流量跟踪,部分不跟踪
#!/bin/bash
# ═══════════════════════════════════════════════════
# 混合模式示例
# Web 流量:使用连接跟踪(需要 conntrack 状态匹配)
# NFS 流量:跳过连接跟踪(高性能需求)
# ═══════════════════════════════════════════════════
# ─── raw 表:跳过 NFS 的连接跟踪 ───
iptables -t raw -A PREROUTING -p tcp --dport 2049 -j NOTRACK
iptables -t raw -A PREROUTING -p udp --dport 2049 -j NOTRACK
iptables -t raw -A OUTPUT -p tcp --sport 2049 -j NOTRACK
iptables -t raw -A OUTPUT -p udp --sport 2049 -j NOTRACK
# ─── filter 表 ───
# 已建立连接(仅对跟踪的流量生效)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 回环接口
iptables -A INPUT -i lo -j ACCEPT
# Web 流量(被连接跟踪)
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
# NFS 流量(未被连接跟踪,需要双向显式允许)
iptables -A INPUT -p tcp --dport 2049 -j ACCEPT
iptables -A INPUT -p udp --dport 2049 -j ACCEPT
# SSH
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 默认拒绝
iptables -P INPUT DROP
7.7 conntrack 常见问题排查
7.7.1 conntrack 表满
症状:dmesg 中出现以下日志:
nf_conntrack: table full, dropping packet
排查步骤:
# 1. 查看当前条目数
conntrack -C
# 2. 查看最大值
sysctl net.netfilter.nf_conntrack_max
# 3. 查看是否有大量半开连接
conntrack -L | grep SYN_SENT | wc -l
# 4. 查看是否有大量 TIME_WAIT
conntrack -L | grep TIME_WAIT | wc -l
解决方案:
# 临时方案:增大表
sysctl -w net.netfilter.nf_conntrack_max=262144
# 长期方案:
# 1. 缩短超时时间
# 2. 对不需要跟踪的流量使用 NOTRACK
# 3. 应用层使用连接池减少短连接
7.7.2 conntrack 导致的 NAT 问题
症状:修改了 DNAT 规则后,已有连接仍然走旧规则
原因:conntrack 表中缓存了旧的 NAT 映射
解决方案:
# 清除 conntrack 表中的相关条目
conntrack -D -p tcp --dport 80
# 或清除所有条目(会导致已有连接断开)
conntrack -F
7.8 raw 表的高级用法
7.8.1 配合 nftables 使用 raw 表
# nftables 等价写法
nft add table inet raw_table
nft add chain inet raw_table prerouting { type filter hook prerouting priority -300 \; }
nft add rule inet raw_table prerouting udp dport 53 notrack
7.8.2 使用 TPROXY 进行透明代理
# TPROXY 需要 raw 表中的 NOTRACK 配合
# 在 raw 表中跳过连接跟踪
iptables -t raw -A PREROUTING -p tcp --dport 80 -j NOTRACK
# 在 mangle 表中使用 TPROXY
iptables -t mangle -A PREROUTING -p tcp --dport 80 \
-j TPROXY --tproxy-mark 0x1/0x1 --on-port 3129
# 策略路由
ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
7.9 注意事项
⚠️ NOTRACK 与 NAT 不兼容:被 NOTRACK 标记的数据包不能使用 NAT。如果流量需要 NAT,不要使用 NOTRACK。
⚠️ NOTRACK 与状态匹配不兼容:被 NOTRACK 标记的数据包没有连接状态,不能使用
-m conntrack --ctstate ESTABLISHED,RELATED。必须显式允许双向流量。
⚠️ 清空 conntrack 表的风险:
conntrack -F会清除所有连接跟踪记录,导致所有已有连接的状态信息丢失,可能引起网络中断。
⚠️ hashsize 的设置:hash 表的桶数应该大于
conntrack_max / 4,否则哈希冲突会导致性能下降。
7.10 扩展阅读
| 资源 | 说明 |
|---|---|
man conntrack | conntrack 工具手册 |
man conntrackd | 连接跟踪同步守护进程手册 |
| Netfilter conntrack 文档 | 内核文档中的连接跟踪部分 |
cat /proc/net/nf_conntrack | 直接查看 conntrack 表 |
本章小结
| 概念 | 要点 |
|---|---|
| raw 表 | 优先级最高,在 conntrack 之前执行 |
| NOTRACK | 跳过连接跟踪,节省内存和 CPU |
| conntrack_max | 连接跟踪表的最大条目数 |
| 超时优化 | 缩短不必要长的超时时间 |
| hashsize | 哈希桶数,影响查找性能 |
| NOTRACK 限制 | 不能使用 NAT 和状态匹配 |
下一章:第 08 章:扩展匹配模块,将学习各种高级匹配条件的用法。