ip_conntrack: table full问题
一、现象
今天web应用突然停了,ping服务器的地址发现也没有响应,而通过其内网的其他主机通过telnet可以正常连接上(只允许内网telnet的,以备紧急情况)。通过该服务器ping外网,发现正常。赶紧查看/var/log/message日志,发现报如下信息:
1Aug 28 09:19:50 web8 kernel: ip_conntrack: table full, dropping packet.
2Aug 28 09:19:51 web8 kernel: printk: 3 messages suppressed.
3Aug 28 09:19:51 web8 kernel: ip_conntrack: table full, dropping packet.
4Aug 28 11:42:24 web8 kernel: printk: 4 messages suppressed.
看到ip_conntrack这个熟悉的名字,知道一定是防火墙出现了问题。先停掉iptables,web应用恢复。重启启动iptables,web服务仍然正常。想到会不会是因为用了fail2ban造成了把公司的ip列到了黑名单里。不过看了下,该机并未设置fail2ban。
1netstat -an|grep tcp|wc -l
查看了下连接数也正常。再看last记录,看有没有异常IP入侵,也未见异常(有,别人也清掉了,不过这个步骤还是不能少的)。这便略微放下了心。从网上找出 错原因。
网上打到的结果其实在message日志里也已经告诉我们了,IP_conntrack表已满。而ip_conntrack是什么呢?
IP_conntrack表示连接跟踪数据库(conntrack database),代表NAT机器跟踪连接的数目,连接跟踪表能容纳多少记录是被一个变量控制的,他可由内核中的ip- sysctl函数配置。每一个跟踪连接表会占用350字节的内核存储空间,时间一长就会把默认的空间填满。
kernel 用 ip_conntrack 模块来记录 iptables 网络包的状态,并保存到 table 里(这个 table 在内存里),如果网络状况繁忙,比如高连接,高并发连接等会导致逐步占用这个 table 可用空间,一般这个 table 很大不容易占满并且可以自己清理,table的记录会一直呆在 table 里占用空间直到源 IP 发一个 RST 包,但是如果出现被攻击、错误的网络配置、有问题的路由/路由器、有问题的网卡等情况的时候,就会导致源 IP 发的这个 RST 包收不到,这样就积累在 table里,越积累越多直到占满,满了以后 iptables 就会丢包,出现外部无法连接服务器的情况。
二、解决思路
而解决方法,我总结了三种方法:
方法1、增加参数设置
增加 ip_conntrack_max的大小:
1echo 131072 > /proc/sys/net/ipv4/ip_conntrack_max
2或者
3sysctl -w net.ipv4.netfilter.ip_conntrack_max=131072
我看了我几台服务器,该值的默认大小都是65535,如果想重启后仍然有效,需要在sysctl.conf文件最后加入
1net.ipv4.netfilter.ip_conntrack_max=131072
还有一个参数 ip_conntrack_tcp_timeout_established 需要注意,默认情况下 timeout 是5天(432000秒):
1# cat /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_established
2432000
3# sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_established=180
4net.ipv4.netfilter.ip_conntrack_tcp_timeout_established = 180
我这里设置的是180秒,这个要根据应用不同,灵活设置。
注:上面的方法当时设置后生效,不过在我service iptables restart重启防火墙后,ip_conntrack_max又变成了默认65535 。
方法2、卸载 ip_conntrack 模块
到在 /etc/sysconfig/iptables-config 文件里删除或者注释掉 ip_conntrack_netbios_ns 后重启系统:
1 vi /etc/sysconfig/iptables-config
2#IPTABLES_MODULES="ip_conntrack_netbios_ns"
3 shutdown -r now
这个是从网上搜到的结果,个人感觉,不重启通过rmmod命令也可以卸载模块(需内核支持),然后重启iptables应该也可以的。
方法3、利用hping将ip连接状态设为closed
1#!/bin/bash
2if [ -z ] ; then
3exit
4fi
5grep -E "^tcp .ESTABLISHED src= " /proc/net/ip_conntrack | while read line ; do
6S_IP=`echo $line | awk ''`
7S_SOCK=`echo $line | awk ''`
8D_IP=`echo $line | awk ''`
9D_SOCK=`echo $line | awk ''`
10echo "$S_IP:$S_SOCK $D_IP:$D_SOCK"
11hping2 $D_IP -R -s $S_SOCK -p $D_SOCK -a $S_IP -k -c 1 >/dev/null 2>/dev/null &
12done
用法:sh clr_conns.sh x.x.x.x ,x.x.x.x为要置为closed连接的ip
查看ip_conntrack连接的方法:
1cat /proc/net/ip_conntrack |wc -l
2查看当前总的连接数
3cat /proc/net/ip_conntrack | cut -d ' ' -f 10 | cut -d '=' -f 2 | sort | uniq -c | sort -nr | head -n 5
4找出前五位连接最多的主机
通过另外一个方法也可以查看该表中记录的当前的连接:
1[root@web7 ~]# head /proc/slabinfo
2slabinfo - version: 2.1
3# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
4ip_conntrack_expect 0 0 136 28 1 : tunables 120 60 8 : slabdata 0 0 0
5ip_conntrack 29030 29653 304 13 1 : tunables 54 27 8 : slabdata 2281 2281 55
6blktapif_cache 0 0 200 19 1 : tunables 120 60 8 : slabdata 0 0 0
7blkif_cache 0 0 200 19 1 : tunables 120 60 8 : slabdata 0 0 0
8bridge_fdb_cache 31 59 64 59 1 : tunables 120 60 8 : slabdata 1 1 0
9rpc_buffers 8 8 2048 2 1 : tunables 24 12 8 : slabdata 4 4 0
10rpc_tasks 8 10 384 10 1 : tunables 54 27 8 : slabdata 1 1 0
11rpc_inode_cache 6 10 768 5 1 : tunables 54 27 8 : slabdata 2 2 0
其中29030即为当前的连数。
三、反思与查找问题的根源
利用找出前五位的方法查到当前表中记录的连接最多的主机:
1cat /proc/net/ip_conntrack | cut -d ' ' -f 10 | cut -d '=' -f 2 | sort | uniq -c | sort -nr | head -n 5
2 29457 192.168.1.38
3 260 111.1.44.25
4 189 113.119.105.24
5 155 11.236.174.164
6 150 58.246.37.238
7注:以上的公网IP是我伪造的,如有雷同,纯属巧合,切勿对号入座。
发现当前连接最多的竟是本机自身的内网IP地址(29457个),
1 more /proc/net/ip_conntrack
2tcp 6 72 TIME_WAIT src=192.168.1.38 dst=192.168.1.107 sport=52397 dport=3128 packets=9 bytes=822 src=192.168.1.107 dst=192.168..38 sport=3128 dport=52397 packets=9 byt
3es=4977 [ASSURED] mark=0 secmark=0 use=1
4tcp 6 119 TIME_WAIT src=192.168.1.38 dst=192.168.1.107 sport=47004 dport=3128 packets=6 bytes=706 src=192.168.1.107 dst=192.168.1.38 sport=3128 dport=47004 packets=6 by
5tes=1880 [ASSURED] mark=0 secmark=0 use=1
6tcp 6 104 TIME_WAIT src=192.168.1.38 dst=192.168.1.200 sport=47341 dport=5050 packets=5 bytes=1032 src=192.168.1.200 dst=192.168.1.38 sport=5050 dport=47341 packets=5 b
7ytes=1283 [ASSURED] mark=0 secmark=0 use=1
8tcp 6 104 TIME_WAIT src=192.168.1.38 dst=192.168.1.107 sport=39091 dport=3128 packets=5 bytes=737 src=192.168.1.107 dst=192.168.1.38 sport=3128 dport=39091 packets=5 by
9tes=464 [ASSURED] mark=0 secmark=0 use=1
10tcp 6 103 TIME_WAIT src=192.168.1.38 dst=192.168.1.107 sport=38981 dport=3128 packets=6 bytes=606 src=192.168.1.107 dst=192.168.1.38 sport=3128 dport=38981 packets=6 by
11tes=1772 [ASSURED] mark=0 secmark=0 use=1
可以看到其中主为是内容为1.107的主机和其该主机连,且状态主要为TIME_WAIT,而1.107为公司的squid服务器。通过脚本
1netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
发现squid服务器上有二万多TIME_WAIT。
而张宴的博文有一篇《减少Linux下Squid服务器的TIME_WAIT套接字数量》对squid的大量TIME_WAIT给出的说明如下:
Linux下高并发的Squid服务器,TCP TIME_WAIT套接字数量经常达到两、三万,服务器很容易被拖死。通过修改Linux内核参数,可以减少Squid服务器的TIME_WAIT套接字数量。
1vi /etc/sysctl.conf
2增加以下几行:
3net.ipv4.tcp_fin_timeout = 30
4net.ipv4.tcp_keepalive_time = 1200
5net.ipv4.tcp_syncookies = 1
6net.ipv4.tcp_tw_reuse = 1
7net.ipv4.tcp_tw_recycle = 1
8net.ipv4.ip_local_port_range = 1024 65000
9net.ipv4.tcp_max_syn_backlog = 8192
10net.ipv4.tcp_max_tw_buckets = 5000
说明:
- net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击, 默认为0,表示关闭;
- net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
- net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。net.ipv4.tcp_fin_timeout = 30 表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。
- net.ipv4.tcp_keepalive_time = 1200 表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。
- net.ipv4.ip_local_port_range = 1024 65000 表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000。
- net.ipv4.tcp_max_syn_backlog = 8192 表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
- net.ipv4.tcp_max_tw_buckets = 5000 表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。默认为180000,改为5000。对于Apache、Nginx等服务器,上几行的参数可以很好地减少TIME_WAIT套接字数量,但是对于Squid,效果 却不大。此项参数可以控制TIME_WAIT套接字的最大数量,避免Squid服务器被大量的TIME_WAIT套接字拖死。
执行以下命令使配置生效:/sbin/sysctl -p
四、总结
最终找到了问题的原因,是因为内部的squid服务器造成了本机的拒绝服务。而通过方法1和 sysctl内核参数优化该业务机及squid主机,通过观察发现其表记录稳 定在三万记录左右。
捐赠本站(Donate)
如您感觉文章有用,可扫码捐赠本站!(If the article useful, you can scan the QR code to donate))
- Author: shisekong
- Link: https://blog.361way.com/ip_conntrack-tablefull/1717.html
- License: This work is under a 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. Kindly fulfill the requirements of the aforementioned License when adapting or creating a derivative of this work.