[转]Iptables之nf_conntrack模块

nf_conntrack模块
nf_conntrack(在老版本的 Linux 内核中叫 ip_conntrack)是一个内核模块,用于跟踪一个连接的状态的。连接状态跟踪可以供其他模块使用,最常见的两个使用场景是 iptables 的 nat 的 state 模块。 iptables 的 nat 通过规则来修改目的/源地址,但光修改地址不行,我们还需要能让回来的包能路由到最初的来源主机。这就需要借助 nf_conntrack 来找到原来那个连接的记录才行。而 state 模块则是直接使用 nf_conntrack 里记录的连接的状态来匹配用户定义的相关规则。例如下面这条 INPUT 规则用于放行 80 端口上的状态为 NEW 的连接上的包。

iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT。

iptables中的状态检测功能是由state选项来实现iptable的。对这个选项,在iptables的手册页中有以下描述:

state

这个模块能够跟踪分组的连接状态(即状态检测)。

格式:--state XXXXX

这里,state是一个用逗号分割的列表,表示要匹配的连接状态。

在iptables中有四种状态:NEW,ESTABLISHED,RELATED,INVALID。

NEW,表示这个分组需要发起一个连接,或者说,分组对应的连接在两个方向上都没有进行过分组传输。NEW说明 这个包是我们看到的第一个包。意思就是,这是conntrack模块看到的某个连接第一个包,它即将被匹配了。比如,我们看到一个SYN包,是我们所留意 的连接的第一个包,就要匹配它。第一个包也可能不是SYN包,但它仍会被认为是NEW状态。比如一个特意发出的探测包,可能只有RST位,但仍然是 NEW。

ESTABLISHED,表示分组对应的连接已经进行了双向的分组传输,也就是说连接已经建立,而且会继续匹配 这个连接的包。处于ESTABLISHED状态的连接是非常容易理解的。只要发送并接到应答,连接就是ESTABLISHED的了。一个连接要从NEW变 为ESTABLISHED,只需要接到应答包即可,不管这个包是发往防火墙的,还是要由防火墙转发的。ICMP的错误和重定向等信息包也被看作是 ESTABLISHED,只要它们是我们所发出的信息的应答。

RELATED,表示分组要发起一个新的连接,但是这个连接和一个现有的连接有关,例如:FTP的数据传输连接 和控制连接之间就是RELATED关系。RELATED是个比较麻烦的状态。当一个连接和某个已处于ESTABLISHED状态的连接有关系时,就被认为 是RELATED的了。换句话说,一个连接要想是RELATED的,首先要有一个ESTABLISHED的连接。这个ESTABLISHED连接再产生一 个主连接之外的连接,这个新的连接就是RELATED的了,当然前提是conntrack模块要能理解RELATED。ftp是个很好的例子,FTP- data连接就是和FTP-control有RELATED的。还有其他的例子,

INVAILD,表示分组对应的连接是未知的,说明数据包不能被识别属于哪个连接或没有任何状态。有几个原因可以产生这种情况,比如,内存溢出,收到不知属于哪个连接的ICMP错误信息。一般地,我们DROP这个状态的任何东西。

nf_conntrack模块常用命令
查看nf_conntrack表当前连接数
cat /proc/sys/net/netfilter/nf_conntrack_count

查看nf_conntrack表最大连接数
cat /proc/sys/net/netfilter/nf_conntrack_max

通过dmesg可以查看nf_conntrack的状况:
dmesg |grep nf_conntrack

查看存储conntrack条目的哈希表大小,此为只读文件
cat /proc/sys/net/netfilter/nf_conntrack_buckets

查看nf_conntrack的TCP连接记录时间
cat /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established

通过内核参数查看命令,查看所有参数配置
sysctl -a | grep nf_conntrack

通过conntrack命令行工具查看conntrack的内容
yum install -y conntrack
conntrack -L

加载对应跟踪模块
[root@plop ~]# modprobe /proc/net/nf_conntrack_ipv4
[root@plop ~]# lsmod | grep nf_conntrack
nf_conntrack_ipv4 9506 0
nf_defrag_ipv4 1483 1 nf_conntrack_ipv4
nf_conntrack_ipv6 8748 2
nf_defrag_ipv6 11182 1 nf_conntrack_ipv6
nf_conntrack 79758 3 nf_conntrack_ipv4,nf_conntrack_ipv6,xt_state
ipv6 317340 28 sctp,ip6t_REJECT,nf_conntrack_ipv6,nf_defrag_ipv6

移除 nf_conntrack 模块
$ sudo modprobe -r xt_NOTRACK nf_conntrack_netbios_ns nf_conntrack_ipv4 xt_state
$ sudo modprobe -r nf_conntrack

查看当前的连接数:
grep nf_conntrack /proc/slabinfo

查出目前 nf_conntrack 的排名:
cat /proc/net/nf_conntrack | cut -d ' ' -f 10 | cut -d '=' -f 2 | sort | uniq -c | sort -nr | head -n 10

nf_conntrack会话表的内容解释
会话表样例
通过conntrack -L与/proc/net/nf_conntrack是完全一样的,除了少了前面的两列。
下面以cat /proc/net/nf_conntrack为例进行说明:

ipv4 2 tcp 6 25 SYN_SENT src=182.168.77.7 dst=42.236.9.57 sport=57430 dport=443 [UNREPLIED] src=42.236.9.57 dst=182.168.77.7 sport=443 dport=57430 mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2
ipv4 2 tcp 6 299 ESTABLISHED src=172.18.15.56 dst=172.18.15.96 sport=40248 dport=22 src=172.18.15.96 dst=172.18.15.56 sport=22 dport=40248 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2
ipv4 2 tcp 6 5 SYN_SENT src=182.168.77.7 dst=221.181.72.250 sport=57428 dport=443 [UNREPLIED] src=221.181.72.250 dst=182.168.77.7 sport=443 dport=57428 mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2
ipv4 2 tcp 6 1 SYN_SENT src=182.168.77.7 dst=221.181.72.250 sport=57427 dport=80 [UNREPLIED] src=221.181.72.250 dst=182.168.77.7 sport=80 dport=57427 mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2
每一列表达的意思
第一列:网络层协议名字。
第二列:网络层协议号。
第三列:传输层协议名字。
第四列:传输层协议号。
第五列:无后续包进入时无效的秒数,即老化时间。
第六列:不是所有协议都有,连接状态。
其它的列都是通过名字的方式(key与value对)表述,或和呈现标识([UNREPLIED], [ASSURED], …)。一行的不同列可能包含相同的名字(例如src和dst),第一个表示请求方,第二个表示应答方。
呈现标识含义
[ASSURED]: 在两个方面(即请求和响应)方向都看到了流量。
[UNREPLIED]: 尚未在响应方向上看到流量。如果连接跟踪缓存溢出,则首先删除这些连接。
请注意,某些列名仅出现在特定协议中(例如,TCP和UDP的sport和dport,ICMP的type和code)。 仅当内核使用特定选项构建时,才会显示其他列名称(例如mark)。

举例说明
ipv4 2 tcp 6 300 ESTABLISHED src=1.1.1.2 dst=2.2.2.2 sport=2000 dport=80 src=2.2.2.2 dst=1.1.1.1 sport=80 dport=12000 [ASSURED] mark=0 use=2
属于从主机1.1.1.2,端口2000到主机2.2.2.2,端口80的已建立的TCP连接,从中将响应发送到主机1.1.1.2,端口2000,在五分钟内超时。对于此连接,已在两个方向上看到数据包。

ipv4 2 icmp 1 3 src=1.1.1.2 dst=1.1.1.1 type=8 code=0 id=32354 src=1.1.1.1 dst=1.1.1.2 type=0 code=0 id=32354 mark=0 use=2
属于从主机1.1.1.2到主机1.1.1.1的ICMP回应请求数据包,具有从主机1.1.1.1到主机1.1.1.2的预期回应应答数据包,在三秒内超时。响应目标主机不一定与请求源主机相同,因为请求源地址可能已被响应目标主机伪装。

主要标识
请注意,以下信息可能不是最新信息!

Fields available for all entries:

bytes (if accounting is enabled, request and response)
delta-time (if CONFIG_NF_CONNTRACK_TIMESTAMP is enabled)
dst (request and response)
mark (if CONFIG_NF_CONNTRACK_MARK is enabled)
packets (if accounting is enabled, request and response)
secctx (if CONFIG_NF_CONNTRACK_SECMARK is enabled)
src (request and response)
use
zone (if CONFIG_NF_CONNTRACK_ZONES is enabled)
Fields available for dccp, sctp, tcp, udp and udplite transmission layer protocols:

dport (request and response)
sport (request and response)
Fields available for icmp transmission layer protocol:

code (request and response)
id (request and response)
type (request and response)
Fields available for gre transmission layer protocol:

dstkey (request and response)
srckey (request and response)
stream_timeout
timeout
Allowed values for the sixth field:

dccp transmission layer protocol
CLOSEREQ
CLOSING
IGNORE
INVALID
NONE
OPEN
PARTOPEN
REQUEST
RESPOND
TIME_WAIT

sctp transmission layer protocol
CLOSED
COOKIE_ECHOED
COOKIE_WAIT
ESTABLISHED
NONE
SHUTDOWN_ACK_SENT
SHUTDOWN_RECD
SHUTDOWN_SENT

tcp transmission layer protocol
CLOSE
CLOSE_WAIT
ESTABLISHED
FIN_WAIT
LAST_ACK
NONE
SYN_RECV
SYN_SENT
SYN_SENT2
TIME_WAIT

nf_conntrack相关内核参数和解释
参考内核帮助文档/usr/share/doc/kernel-doc-3.10.0/Documentation/networking/nf_conntrack-sysctl.txt
/proc/sys/net/netfilter/nf_conntrack_*:

nf_conntrack_acct
值类型:BOOLEAN
0 - disabled (default)
not 0 - enabled
启用连接跟踪流记帐。64位字节和数据包每个流量的计数器被添加。

nf_conntrack_buckets
值类型:INTEGER (read-only)
哈希表的大小。 如果在模块加载期间未指定为参数,则通过将总内存除以16384来计算默认大小以确定存储区的数量,但是哈希表将永远不会少于32并且限制为16384个存储区。 对于内存超过4GB的系统,它将是65536个桶。

nf_conntrack_checksum
值类型:BOOLEAN
0 - disabled
not 0 - enabled (default)
验证传入数据包的校验和。 具有错误校验和的数据包处于INVALID状态。 如果启用此选项,则不会考虑此类数据包进行连接跟踪。

nf_conntrack_count
值类型:INTEGER (read-only)
当前分配的流条目数。

nf_conntrack_events
值类型:BOOLEAN
0 - disabled
not 0 - enabled (default)
如果启用此选项,则连接跟踪代码将通过ctnetlink为用户空间提供连接跟踪事件。

nf_conntrack_events_retry_timeout
值类型:INTEGER (seconds)
default 15
此选项仅在使用“可靠连接跟踪事件”时才相关。 通常,ctnetlink是“有损”的,也就是说,当用户空间监听器无法跟上时,事件通常会被丢弃。
用户空间可以请求“可靠的事件模式”。 当此模式处于活动状态时,conntrack将仅在事件发布后销毁。 如果事件传递失败,则内核会定期重新尝试将事件发送到用户空间。
这是内核在重新尝试传递destroy事件时应使用的最大间隔。
数字越大意味着交付重试次数越少,处理待办事项的时间就越长。

nf_conntrack_expect_max
值类型:INTEGER
期望表的最大大小。 默认值为nf_conntrack_buckets / 256.最小值为1。

nf_conntrack_frag6_high_thresh
值类型:INTEGER
用于重组IPv6片段的最大内存。 当为达到重组目标时分配nf_conntrack_frag6_high_thresh字节的内存时,若超出此值片段处理程序将抛出数据包。

nf_conntrack_frag6_timeout
值类型:INTEGER (seconds)
default 60
ipv6分片在内存中保存的老化时间。

nf_conntrack_generic_timeout
值类型:INTEGER (seconds)
default 600
通用超时的默认值。 这指的是第4层未知/不支持的协议。

nf_conntrack_helper
值类型:BOOLEAN
0 - disabled
not 0 - enabled (default)
启用自动conntrack帮助程序分配。

nf_conntrack_icmp_timeout
值类型:INTEGER (seconds)
default 30
ICMP连接状态超时的默认值。

nf_conntrack_icmpv6_timeout
值类型:INTEGER (seconds)
default 30
ICMP6连接状态超时的默认值。

nf_conntrack_log_invalid
值类型:INTEGER
0 - disable (default)
1 - log ICMP packets
6 - log TCP packets
17 - log UDP packets
33 - log DCCP packets
41 - log ICMPv6 packets
136 - log UDPLITE packets
255 - log packets of any protocol
根据值类型记录指定类型无效数据包。

nf_conntrack_max
值类型:INTEGER
连接跟踪表的大小。 默认值为nf_conntrack_buckets值* 4。

nf_conntrack_tcp_be_liberal
值类型:BOOLEAN
0 - disabled (default)
not 0 - enabled
Be conservative in what you do, be liberal in what you accept from others.If it’s non-zero, we mark only out of window RST segments as INVALID.

nf_conntrack_tcp_loose
值类型:BOOLEAN
0 - disabled
not 0 - enabled (default)
如果它设置为零,我们将禁用拾取已建立的连接。
它的意思是,是否仅仅允许为经过TCP三次握手的流创建nf_conntrack表项还是说为任意收到的TCP数据包(有可能是一个构造出来的攻击包)查询未果后均创建新的nf_conntrack表项。

nf_conntrack_tcp_max_retrans
值类型:INTEGER
default 3
在未收到来自目标的(可接受)ACK的情况下可以重新传输的最大数据包数。 如果达到此数字,将启动更短的计时器。

nf_conntrack_tcp_timeout_close
值类型:INTEGER (seconds)
default 10

nf_conntrack_tcp_timeout_close_wait
值类型:INTEGER (seconds)
default 60

nf_conntrack_tcp_timeout_established
值类型:INTEGER (seconds)
default 432000 (5 days)
默认是432000=3600245即5天的超时时间,超时后清空对应的那条记录。

nf_conntrack_tcp_timeout_fin_wait
值类型:INTEGER (seconds)
default 120

nf_conntrack_tcp_timeout_last_ack
值类型:INTEGER (seconds)
default 30

nf_conntrack_tcp_timeout_max_retrans
值类型:INTEGER (seconds)
default 300

nf_conntrack_tcp_timeout_syn_recv
值类型:INTEGER (seconds)
default 60

nf_conntrack_tcp_timeout_syn_sent
值类型:INTEGER (seconds)
default 120

nf_conntrack_tcp_timeout_time_wait
值类型:INTEGER (seconds)
default 120

nf_conntrack_tcp_timeout_unacknowledged
值类型:INTEGER (seconds)
default 300

nf_conntrack_timestamp
值类型:BOOLEAN
0 - disabled (default)
not 0 - enabled
启用连接跟踪流时间戳。

nf_conntrack_udp_timeout
值类型:INTEGER (seconds)
default 30

nf_conntrack_udp_timeout_stream2
值类型:INTEGER (seconds)
default 180
如果检测到UDP流,将使用此扩展超时。

还有多少秒这条会话信息会从跟踪表清除,取决于超时参数的配置,以及是否有包传输,有包传输时,这个时间会重置为超时时间。

如何判断会话表是否满
当会话表中的记录大于内核设置nf_conntrack_max的值时,会导致会话表满。

nf_conntrack_max - INTEGER
Size of connection tracking table. Default value is
nf_conntrack_buckets value * 4.
错误例子: less /var/log/messages

Nov 3 23:30:27 digoal_host kernel: : [63500383.870591] nf_conntrack: table full, dropping packet.
Nov 3 23:30:27 digoal_host kernel: : [63500383.962423] nf_conntrack: table full, dropping packet.
Nov 3 23:30:27 digoal_host kernel: : [63500384.060399] nf_conntrack: table full, dropping packet.

会话表满的解决办法
nf_conntrack table full的问题,会导致丢包,影响网络质量,严重时甚至导致网络不可用。

解决方法举例:
1、排查是否DDoS攻击,如果是,从预防攻击层面解决问题。

2、清空会话表。

重启iptables,会自动清空nf_conntrack table。注意,重启前先保存当前iptables配置(iptables-save > /etc/sysconfig/iptables ; service iptables restart)。

3、应用程序正常关闭会话

设计应用时,正常关闭会话很重要。

4、加大表的上限(需要考虑内存的消耗)

例举故障原因
内核参数 net.nf_conntrack_max 系统默认值为”65536”,当nf_conntrack模块被装置且服务器上连接超过这个设定的值时,系统会主动丢掉新连接包,直到连接小于此设置值才会恢复。同时内核参数“net.netfilter.nf_conntrack_tcp_timeout_established”系统默认值为”432000”,代表nf_conntrack的TCP连接记录时间默认是5天,致使nf_conntrack的值减不下来,丢包持续时间长。

nf_conntrack模块在首次装载或重新装载时,内核参数net.nf_conntrack_max会重新设置为默认值“65536”,并且不会调用sysctl设置为我们的预设值。

触发nf_conntrack模块首次装载比较隐蔽,任何调用IPtable NAT功能的操作都会触发。当系统没有挂载nf_conntrack模块时,iptables 相关命令(iptables -L -t nat)就成触发nf_conntrack模块装置,致使net.nf_conntrack_max 重设为65536。

触发nf_conntrack模块重新装载的操作很多,CentOS6 中“service iptables restart”,CentOS7中“systemctl restart iptables.service”都会触发设置重置,致使net.nf_conntrack_max 重设为65536。

重要的几个配置文件
nf_conntrack_max决定连接跟踪表的大小,当nf_conntrack模块被装置且服务器上连接超过这个设定的值时,系统会主动丢掉新连接包,直到连接小于此设置值才会恢复。
nf_conntrack_buckets决定存储conntrack条目的哈希表大小,若是单方面修改nf_conntrack_max,而不修改nf_conntrack_buckets,只是影响查找速度,挂在不了桶上的新跟踪项目,会挂在到桶中的链表上(原理为hash表结构)。
nf_conntrack_tcp_timeout_established系统默认值为”432000”,代表nf_conntrack的TCP连接记录时间默认是5天,致使nf_conntrack的值减不下来,丢包持续时间长。
通过修改这两个值即可,但是nf_conntrack_buckets时个只读文件,无法进行修改。

修改参数
或通过sysctl命令进行修改:
$ sysctl -w net.netfilter.nf_conntrack_max=1048576
$ sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600
$ sysctl -p #使生效
或是直接永久性修改永久生效
vi /etc/sysctl.conf
net.netfilter.nf_conntrack_max=1048576
net.netfilter.nf_conntrack_tcp_timeout_established=3600
对于上述解决方案无法修改nf_conntrack_buckets的参数,因为此为只读文件,通过上述nf_conntrack-sysctl.txt文件可知知,可以通过模块加载的时候设置参数。此时可以采用下面方案进行修改:
通过系统初始化脚本创建配置文件”/etc/modprobe.d/nf_conntrack.conf”, 内容为“options nf_conntrack hashsize=262144”,通过nf_conntrack模块挂接参数”hashsize”自动设置“net.nf_conntrack_max=2097152”(nf_conntrack_max=hashsize*8),保证后续新初始化服务器配置正确。

通过自动化部署工具全网推送配置文件”/etc/modprobe.d/nf_conntrack.conf”, 内容为“options nf_conntrack hashsize=262144”,保证nf_conntrack模块在首次装载或重新装载时“net.nf_conntrack_max”内核参数设置为我们预期的“2097152”。

更新系统初始化脚本,设置“net.netfilter.nf_conntrack_tcp_timeout_established=1800”,减少nf_conntrack的TCP连接记录时间。

如果并不需要nf_conntrack及其相关模块可以在/etc/modprobe.d目录新建文件blacklist.conf ,文件中加入:install nf_conntrack /bin/false 这样做的副作用是无法再使用Iptables NAT相关功能。

计算公式
可以增大 conntrack 的条目(sessions, connection tracking entries) CONNTRACK_MAX 或者增加存储 conntrack 条目哈希表的大小 HASHSIZE
默认情况下,CONNTRACK_MAX 和 HASHSIZE 会根据系统内存大小计算出一个比较合理的值:
对于 CONNTRACK_MAX,其计算公式:
CONNTRACK_MAX = RAMSIZE (in bytes) / 16384 / (ARCH / 32)
比如一个 64 位 48G 的机器可以同时处理 48*1024^3/16384/2 = 1572864 条 netfilter 连接。对于大于 1G 内存的系统,默认的 CONNTRACK_MAX 是 65535。

对于 HASHSIZE,默认的有这样的转换关系:
CONNTRACK_MAX = HASHSIZE * 8
这表示每个链接列表里面平均有 8 个 conntrack 条目。其真正的计算公式如下:
HASHSIZE = CONNTRACK_MAX / 8 = RAMSIZE (in bytes) / 131072 / (ARCH / 32)
比如一个 64 位 48G 的机器可以存储 48*1024^3/131072/2 = 196608 的buckets(连接列表)。对于大于 1G 内存的系统,默认的 HASHSIZE 是 8192。

参考链接:
解决 nf_conntrack: table full, dropping packet 的几种思路
Linux服务器丢包故障的解决思路及引申的TCP/IP协议栈理论
net.nf_conntrack_max 设置异常问题

(原文地址:https://clodfisher.github.io/2018/09/nf_conntrack/)

posted @ 2020-03-23 16:11  HelloSUN  阅读(1829)  评论(0编辑  收藏  举报