nf_conntrack: table full, dropping packet 问题排查和解决

 

nf_conntrack模块在kernel 2.6.15(2006-01-03发布) 被引入,支持ipv4和ipv6,取代只支持ipv4的ip_connktrack,用于跟踪连接的状态,供其他模块使用。

 

最常见的使用场景是 iptables 的 nat 和 state 模块:

 

  • nat 根据转发规则修改IP包的源/目标地址,靠nf_conntrack的记录才能让返回的包能路由到发请求的机器。
  • state 直接用 nf_conntrack 记录的连接状态(NEW/ESTABLISHED/RELATED/INVALID)来匹配防火墙过滤规则。

 

 

 

iptables
 

 

 

 

nf_conntrack用1个哈希表记录已建立的连接,包括其他机器到本机、本机到其他机器、本机到本机(例如 ping 127.0.0.1也会被跟踪)。

 

如果连接进来比释放的快,把哈希表塞满了,新连接的数据包会被丢掉,此时netfilter变成了一个黑洞,导致拒绝服务。 这发生在3层(网络层),应用程序毫无办法。

 

各发行版区别:

 

  • CentOS (7.3) 默认加载该模块
  • Ubuntu (16.10+) 和 Kali Linux (2016.1+) 默认不加载,不会有这问题

 

为什么需要这种状态跟踪机制呢?比如你的80端口开启,而你的程序被植入反弹式木马,导致服务器主动从80端口向外部发起连接请求,这个时候你怎么控制呢。关掉80端口,那么你的网站也无法正常运行了。

但有了连接跟踪你就可以设置只允许回复关于80端口的外部请求(ESATBLISHED状态),而无法发起向外部的请求(NEW状态)。所以有了连接跟踪就可以做到在这种层面上的限制,慢慢往下看就明白了各状态的意义。

在内核中由Netfilter的特定框架做的连接跟踪称作conntrack(connection tracking)。conntrack可以作为模块安装,也可以作为内核的一部分。大部分情况下,我们想要也需要更详细的连接跟踪。

conntrack中有许多用来处理TCP,UDP或ICMP协议的部件。这些模块从数据包中提取详细的、唯一的信息,因此能保持对每一个数据流的跟踪。这些信息也告知conntrack流当前的状态。

例如,UDP流一般由他们的目的地址、源地址、目的端口和源端口唯一确定。

在以前的内核里,我们可以打开或关闭重组功能。然而连接跟踪被引入内核后,这个选项就被取消了。因为没有包的重组,连接跟踪就不能正常工作。现在重组已经整合入conntrack,并且在conntrack启动时自动启动。不要关闭重组功能,除非你要关闭连接跟踪。除了本地产生的包由OUTPUT链处理外,所有连接跟踪都是在PREROUTING链里进行处理的,意思就是, iptables会在PREROUTING链里从新计算所有的状态。

如果我们发送一个流的初始化包,状态就会在OUTPUT链里被设置为NEW,当我们收到回应的包时,状态就会在PREROUTING链里被设置为ESTABLISHED。如果第一个包不是本地产生的,那就会在PREROUTING链里被设置为NEW状态。综上,所有状态的改变和计算都是在nat表中的PREROUTING链和OUTPUT链里完成的。conntrack默认最大跟踪65536个连接

连接跟踪表溢出
连接跟踪表nf_conntrack,Linux为每个经过内核网络栈的数据包,生成一个新的连接记录项,当服务器处理的连接过多时,连接跟踪表被打满,服务器会丢弃新建连接的数据包。
如何确认
通过dmesg可以确认是否有该情况发生

dmesg | grep nf_conntrack

如果输出值中有“nf_conntrack: table full, dropping packet”,说明服务器nf_conntrack表已经被打满。

通过/proc文件系统查看nf_conntrack表实时状态:

跟踪连接详细信息:

cat /proc/net/nf_conntrack

查看nf_conntrack表最大连接数

cat /proc/sys/net/netfilter/nf_conntrack_max


查看nf_conntrack表当前连接数

cat /proc/sys/net/netfilter/nf_conntrack_count

查看nf_conntrack_buckets哈希表最大大小

cat /proc/sys/net/netfilter/nf_conntrack_buckets

查看netfilter相关的内核参数

sysctl -a | grep conntrack

如果确认服务器因连接跟踪表溢出而开始丢包,首先需要判断是否正遭受DDOS攻击,如果是正常的业务流量造成,可以考虑调整nf_conntrack的参数
nf_conntrack_max决定连接跟踪表的大小,默认值是65536,可以根据系统内存大小计算一个合理值:CONNTRACK_MAX = RAMSIZE(in bytes)/16384/(ARCH/32),如32G内存可以设置1048576;
一个连接占用312字节内存,65536大约25M内存占用,存储在内核空间,这部分内存不能swap,当CONNTRACK_MAX为 1048576,HASHSIZE buckets为 262144 时,最多占390MB内存开销,对现在的vps服务器来说毫无压力,你用512M的vps跑业务当我没说。
nf_conntrack_buckets决定存储conntrack条目的哈希表大小,默认值是nf_conntrack_max的1/4,延续这种计算方式:BUCKETS = CONNTRACK_MAX/4,如32G内存可以设置262144;
net.netfilter.nf_conntrack_tcp_timeout_established # 决定ESTABLISHED状态连接的超时时间,默认值是5天,可以缩短到1小时,即3600,这个值基本上没什么效果,还是加上吧。
net.netfilter.nf_conntrack_tcp_timeout_fin_wait # 默认 120 秒
net.netfilter.nf_conntrack_tcp_timeout_time_wait # 默认 120 秒,可以适当修改这个值
net.netfilter.nf_conntrack_tcp_timeout_close_wait # 默认 60 秒,CLOSE_WAIT是被动方收到FIN发ACK,然后会转到LAST_ACK发FIN,除非程序写得有问题,正常来说这状态持续时间很短。
net.netfilter.nf_conntrack_generic_timeout # 默认 600 秒(10分钟)通用超时设置,作用于4层(传输层)未知或不支持的协议
net.netfilter.nf_conntrack_tcp_timeout_max_retrans # 默认 300 秒
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged # 默认 300 秒
推荐bucket至少 262144,max至少 1048576,不够再继续加,或者缩短超时时间可以让netfilter更快地把跟踪的记录从哈希表里移除。
哈希表使用情况:

grep conntrack /proc/slabinfo

前面三个数字代表
当前活动对象数、当前使用对象总数、每个对象的内存占用大小(字节)

sysctl -w net.netfilter.nf_conntrack_max=1048576

echo 262144 > /sys/module/nf_conntrack/parameters/hashsize

sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600

通过sysctl -w和echo的命令,是临时生效的,重启会复原也可以编辑文件添加:

vim /etc/sysctl.conf

net.netfilter.nf_conntrack_max = 1048576

net.netfilter.nf_conntrack_tcp_timeout_established = 3600

修改后,执行sysctl -p 生效
和buckets表开机自动修改

echo "echo 262144 > /sys/module/nf_conntrack/parameters/hashsize" >> /etc/rc.d/rc.local

如果业务没有涉及到状态链路相关的应用,直接不加载这个模块就可以了。

 

 

 

 

 

 

 

 


 

posted @ 2019-04-25 12:01  屌丝的IT  阅读(5251)  评论(0编辑  收藏  举报