ip_conntrack与printk-messages-suppressed
http://heavendw.blog.51cto.com/695019/274392
http://michaelkang.blog.51cto.com/1553154/978828
20120830发现日本服务器 27和28 有报错日志。详细如下:
Aug 31 18:25:36 collect-28 kernel: printk: 58 messages suppressed.
Aug 31 18:25:36 collect-28 kernel: Out of socket memory
故障排查分析:
第一条日志分析:
查找信息,Aug 31 18:25:36 collect-28 kernel: printk: 58 messages suppressed. 此报错需要修改内核信息如下;
(1) 加大 ip_conntrack_max 值:
查出原本的 ip_conntrack_max 值,指令: cat /proc/sys/net/ipv4/ip_conntrack_max
写入理想的数值 (每一个 ip_conntrack buffer 会占用 292 Bytes)
指令: echo "数值" > /proc/sys/net/ipv4/ip_conntrack_max
例如: echo "163840" >/proc/sys/net/ipv4/ip_conntrack_max
这个效果是暂时的, 如果要每次开机都使用新的数值, 需将上述指令写入 /etc/rc.d/rc.local
或是在 /etc/sysctl.conf 加入: net.ipv4.ip_conntrack_max = 数值
或使用指令: sysctl -w net.ipv4.ip_conntrack_max=数值
(2): 降低 ip_conntrack timeout 时间
重设:ip_conntrack_tcp_timeout_established (原值: 432000, 单位: 秒)
指令:echo "数值" > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_established
例如:echo "180" > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_established
开机自动设置的作法同方法(1).
(3):开启 tcp_syncookies
重设:tcp_syncookies (默认值 0)
例如:echo '1'> /proc/sys/net/ipv4/tcp_syncookies
修改内核配置文件,报错日志无效,
第二条报错日志分析:
查找信息,Aug 31 18:25:36 collect-28 kernel: Out of socket memory,此报错需要修改内核信息如下;
两种情况会出发 "Out of socket memory" 的信息:
1.有很多的孤儿套接字(orphan sockets)
2.tcp socket 用尽了给他分配的内存
首先看看情况 2。对于 TCP socket 来说,使用 pages 来计数的,而非 bytes,一般情况下 1 page = 4096 bytes。page 大小可以通过下面命令获得:
$ getconf PAGESIZE
4096
查看内核分配了多少的内存给 TCP:
$ cat /proc/sys/net/ipv4/tcp_mem
69618 92825 139236
第一个数字表示,当 tcp 使用的 page 少于 69618 时,kernel 不对其进行任何的干预
第二个数字表示,当 tcp 使用了超过 92825 的 pages 时,kernel 会进入 “memory pressure”
第三个数字表示,当 tcp 使用的 pages 超过 139236 时,我们就会看到题目中显示的信息
查看 tcp 实际用的内存:
$ cat /proc/net/sockstat
sockets: used 116
TCP: inuse 3 orphan 0 tw 4 alloc 4 mem 110
UDP: inuse 1 mem 1
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0
可以看到,实际使用的 mem(110) 远远小于 69618,所以,“Out of socket memory”的错误是由于第一种情况引起的。
关于 orphan socket 的解释,请看这里。orphan socket 对于应用程序来说,意义不大,这也是内核要限制被 orphan socket 消耗内存的原因。而对于 web server 来说,有大量的 orphan socket 也属正常,那么多的连接放在那儿了。
查看 orphan socket 限制:
$ cat /proc/sys/net/ipv4/tcp_max_orphans
对比当前系统中的:
$ cat /proc/net/sockstat
sockets: used 14565
TCP: inuse 35938 orphan 21564 tw 70529 alloc 35942 mem 1894
由于内核代码中有个位运算,所以实际的跟最大的是 2x 或者是 4x 的关系。现在根据实际情况,将 tcp_max_orphans 调到一个合理的值就可以了。原则上该值建议只增大,另外,每个 orphan 会消耗大概 64KB 的内存。
还有个叫 tcp_orphan_retries 参数,对于 web server,可以减小。
修改了上面的一些配置参数,报错日志还是在一直往外报。
####################################################################################################
net.ipv4.tcp_max_orphans = 18000
最后修改值到18000,Out of socket memory报错解决。
net.ipv4.tcp_max_tw_buckets = 10000
最后修改值到net.ipv4.tcp_max_tw_buckets = 10000,kernel: printk: xxx messages suppressed.报错解决。
记得参数修改完成执行 sysctl -p
#######################################################################################################
本文出自 “康建华” 博客,请务必保留此出处http://michaelkang.blog.51cto.com/1553154/978828
http://leejia.blog.51cto.com/4356849/1416407
ip_conntrack模块:
1,基本概念:
-允许的最大跟踪连接条目:CONNTRACK_MAX
-存储跟踪连接条目列表的哈西表的大小:HASHSIZE
-每个哈西表的条目(叫一个bucket),包含了一个链接起来的跟踪连接条目
-哈希表大小HASHSIZE,表现为 条目bucket的多少,在iptables启动时在日志中会显示。
kernel 用 ip_conntrack 模块来记录 iptables 网络包的状态,并把每条记录保存到 table 里(这个 table 在内存里,可以通过/proc/net/ip_conntrack 查看当前已经记录的总数),如果网络状况繁忙,比如高连接,高并发连接等会导致逐步占用这个 table 可用空间,一般这个 table 很大不容易占满并且可以自己清理,table 的记录会一直呆在 table 里占用空间直到源 IP 发一个 RST 包,但是如果出现被攻击、错误的网络配置、有问题的路由/路由器、有问题的网卡等情况的时候,就会导致源 IP 发的这个 RST 包收不到,这样就积累在 table 里,越积累越多直到占满,满了以后 iptables 就会丢包,出现外部无法连接服务器的情况。内核会报如下错误信息:kernel: ip_conntrack: table full, dropping packet.
在/proc/net/ip_conntrack中,每一个跟踪连接表会占用304字节的内核存储空间,时间一长就会把默认的空间填满,那么默认空间时多少?我以redhat为例在内存为64MB的机器上时4096,内存为128MB是 8192,内存为256MB是16376,可以通过查看/proc/sys/net/ipv4/ip_conntrack_max文件得知系统最大值。
案例如下:
[root@yd_82_231 netfilter]# cat /var/log/messages|grep conn
Nov 28 13:18:20 yd_82_231 kernel: ip_conntrack version 2.4 (8192 buckets, 65536 max) - 304 bytes per conntrack
Nov 29 14:48:25 yd_82_231 kernel: ip_conntrack version 2.4 (8192 buckets, 65536 max) - 304 bytes per conntrack
Nov 29 14:50:13 yd_82_231 kernel: ip_conntrack version 2.4 (8192 buckets, 65536 max) - 304 bytes per conntrack
需要注意的事项:
-conntrack最大数量.叫做conntrack_max
-存储这些conntrack的hash表的大小,叫做hashsize
当conntrack入口数大于conntrack_max时,在hash表中每一个conntrack list中的存储的入口将不可控.(conntrack_mark/hashsize 为每个list所能存储的入口的数量)
hash表存在于固定的的不可swap的内存中. conntrack_mark决定占用多少这些不可swap的内存.
缺省的hashsize
——————————–
conntrack_max=hashsize*8
i386中 hashsize=conntrack_max/8=ramsize(in bytes)/131072=ramsize(in MegaBytes)*8.
所以32位pc,512M内存可以存512*1024^2/128/1024=512*8=4096(连接池list)
但是正确的算法是:
hashsize=conntrack_max/8=ramsize(in bytes)/131072/(x/32)
2,当使用iptables时,启用如下两种功能会启用ip_conntrack模块:
1,使用nat功能
2 , 使用 -m stat选项
3,当在/etc/sysconfig/iptables-config或者其他配置文件加载如下模块:IPTABLES_MODULES="ip_conntrack_netbios_ns"
3,和ip_conntrack相关的内核参数及相关作用(如下这些文件,当ip_conntrack模块启用时才有)
/proc/net/ip_conntrack 这个内存中的文件保存了所有的conntrack记录
/proc/sys/net/ipv4/ip_conntrack_max 或者/proc/sys/net/ipv4/netfilter/ip_conntrack_max 2.6内核中,这两个文件改变任何一个都生效。系统支持的conntrack的最大记录数
/proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_established 系统默认全部清空conntrack记录的时间
/proc/sys/net/ipv4/netfilter/ip_conntrack_buckets 系统默认的buckets数
4,这个模块导致的问题
如果是高吞吐的服务,最好不要使用这个模块,因为启用这个模块之后/proc/net/ip_conntrack文件的记录数很快会超限,然后系统就会出现丢包,/var/log/messages中也会出现table full,dropping packets的报错。一般缓解办法为:
适当调大/proc/sys/net/ipv4/ip_conntrack_max 的值及减少/proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_established的值。
参考wiki: http://wiki.khnet.info/index.php/Conntrack_tuning
本文出自 “佳” 博客,请务必保留此出处http://leejia.blog.51cto.com/4356849/1416407
http://blog.csdn.net/dog250/article/details/9732185
1.不能马上生效NAT问题;
2.需要confirm才能用的问题;
3.conntrack cache面对save/restore mark问题
4.双向NAT问题
5.filter表DROP掉的流头包所属的流无法被conntrack
...
这些问题,最终让我”发明“出很多小技巧,以下是我的handle方案:
针对问题1.写出了平滑生效NAT的模块;
这个是我在在《Linux系统如何平滑生效NAT》http://blog.csdn.net/dog250/article/details/9394853中提出的,后续又进行了一些修正。
针对问题2.马上着手解决,本文目的
有时候,你可能希望使用conntrack工具监控新到的数据包,于是你写出了:
conntrack -E -e NEW
但是即使有数据包进来,可能也没有任何事件结果输出,因为这些数据包被filter给DROP掉了,于是这个问题就是问题5。
针对问题3.”提出“依赖condition match和conntrack timeout的慢速匹配模式
我们不能保证总能从save/restore mark的优化中获益,一般而言,我们设置下面的经典规则:
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -m mark ! --mark 0 -j ACCEPT
iptables -t mangle $以下都是逐条的慢速匹配规则,匹配到就打mark
启用了ip_conntrack,任何数据包都要绑定到一个唯一的conntrack,由于ip_conntrack的保存是基于超时时间(UDP,ICMP而言)或者协议(TCP而言)的,因此就会导致在上述规则增加前的一个流的头包过去以后,新增加的依赖ip_conntrack的上述iptables规则不能生效,必须等到该conntrack过期或者协议关闭之后,下一次重新建立conntrack流时才能生效,这会引起很多莫名其妙的问题。通过一个condition match,当新iptables规则被添加时,将其置为0,关闭IPMARK带来的优化,强行让数据包匹配所有的策略。关闭时间设计为(max_timeout+5)秒,其中max_timeout秒为conntrack项过期的最长时间,5秒是一个误差修正值。为了使conntrack项的最长过期时间为max_timeout秒,需要对sysctl参数进行conntrack的timeout调整,且需要禁用掉conntrack的协议限制。最终,引入了两种策略匹配模式,在新规则添加时,会进入慢速匹配模式,所谓慢速匹配模式,就是不依赖ip_conntrack流状态的规则完全遍历匹配模式,反之快速匹配模式则是依赖ip_conntrack状态的匹配模式,直接从conntrack结构体中获取上次的匹配结果。
针对问题4.模仿Cisco风格写出了双向静态NAT模块
但是没有和conntrack关联,这是一种遗憾,以至于每个包都不得不去查找静态NAT哈希,其实有更好的方案,那就是和conntrack关联,新增一个static NAT的extension加入conntrack,无非就是对两个方向发起的流均查一次static NAT表,而不是查iptables设置的NAT rule表。
针对问题5.马上着手解决,本文目的
ip_conntrack有一个confirm逻辑,即当数据流的头包离开协议栈的时候,会被confirm,只有被confirm的conntrack才会加入到conntrack哈希,目前来讲,离开协议栈的地点有两个,第一个是被forward出去,即从一个网卡发出去,第二个是进入用户态,即被本地socket接收。而被DROP的包不会到达这两个点,所以就不会到达confirm点,进而永远都不会建立conntrack条目,也就是说,被filter DROP的数据流头包代表的整个流都无法使用conntrack的任何特性。此时你可能想到了用save/restore mark的方式,然而这也不行,因为没有被confirm,所以就根本就没有conntrack被加入哈希,试问,你能将mark save到哪里去呢?
然而这样做是不合理的,要知道,被DROP也是一种离开协议栈的方式啊!实际上也需要confirm的。如此一来,我就可以将”被DROP“这个事实,记录在conntrack的extension里面,然后在下一个包进来的时候,在PREROUTING中取出这个结果,直接DROP而不用再去匹配filter ruleset,提高了效率。到底应该怎么做呢?实际上并不需要做一个extension,仅仅在数据流头包被DROP的时候将其conntrack给confirm一下即可。余下的事情就可以交给IPMARK了,比如可以在mangle表和filter表设置以下的ruleset:
mangle:
iptables -t mangle -I PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -m mark ! --mark 0 -j ACCEPT
iptables -t mangle -A PREROUTING -m state --state ESTABLISHED -m condition --condition fastmatch -j ACCEPT
....数百条mangle规则,匹配则打mark
iptables -t mangle -A PREROUTING -m mark ! --mark 0 -j CONNMARK --save-mark
filter:
iptables -A FORWARD -m mark ! --mark 0 -j DROP
iptables-A FORWARD -m state --state ESTABLISHED -m condition --condition fastmatch -j ACCEPT
...成百上千条filter规则,匹配则打mark;
iptables -A FORWARD -m mark ! --mark 0 -j CONNMARK --save-mark
iptables -A FORWARD -m mark ! --mark 0 -j DROP
如果说为了判断该数据包是否要DROP掉就去遍历匹配成百上千条规则,那就会大大影响效率,何不用conntrack的policy cache呢?遗憾的是,数据包被DROP导致一个流无法confirm,因此无法使用conntrack的特性,如果被DROP的数据包也能绑定到一个conntrack,那么上述的ruleset就能省大事儿了,需要做的仅仅是在DROP的时候confirm一下而已,代码修改非常简单,我一般喜欢抓住最小交集做最小的改动,需要修改的地方只有两个:
a>第一处修改:$K/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c中增加一个notifier_block,本来我想新增一个HOOK点的,可是为了不把Netfilter搞乱,我还是用了体制外的一种方法,那就是notifier_block,因为我太喜欢Netfilter的设计了,多一点就多了,少一点就残了,所以我不对它的5个HOOK点进行任何拓展和想象,具体的修改方式如下:
- //初始化list
- BLOCKING_NOTIFIER_HEAD(conn_notify_list);
- EXPORT_SYMBOL(conn_notify_list);
- //定义notifier_block
- static struct notifier_block nf_filter_drop_notifier = {
- .notifier_call = nf_confirm_handler,
- };
- //在nf_conntrack_l3proto_ipv4_init中注册这个notifier_block
- blocking_notifier_chain_register(&conn_notify_list,
- &nf_filter_drop_notifier);
b>第二处修改:$K/net/ipv4/netfilter/ip_tables.c的ipt_do_table的最后几行修改一下:
- #ifdef DEBUG_ALLOW_ALL
- return NF_ACCEPT;
- #else
- if (hotdrop)
- return NF_DROP;
- else return verdict;
- #endif
改为:
- #ifdef DEBUG_ALLOW_ALL
- return NF_ACCEPT;
- #else
- if (hotdrop || verdict == NF_DROP) {
- blocking_notifier_call_chain(&conn_notify_list, hook, skb);
- return NF_DROP;
- }
- else return verdict;
- #endif
最后给出nf_confirm_handler的实现:
- static int
- nf_confirm_handler(struct notifier_block *this, unsigned long hook, void *argv)
- {
- struct sk_buff *skb = (struct sk_buff *)argv;
- switch (hook) {
- case NF_INET_FORWARD:
- case NF_INET_LOCAL_IN:
- //本应该将in,out,skb等封装在一个struct里面传过来的,但是希望早点看到结果
- //就省略了,反正就用到一个skb而已,其它的暂时不管了。
- ipv4_confirm(hook, skb, NULL, NULL, NULL);
- break;
- default:
- break;
- }
- return NOTIFY_DONE;
- }
另外别忘了声明一下这个conn_notify_list,我是在#include <linux/netfilter_ipv4.h>中声明的:
- extern struct blocking_notifier_head conn_notify_list;
结语:conntrack主导下的整体方案(和硬件接口)
我看过BSD的Netgraph,接口要比Netfilter的好用,于是我就想把Netfilter的HOOK函数的调用机制更改一下,不再用协议栈调用,而是通过event的方式来触发!在所有的HOOK的所有event中,都可以统一使用ip_conntrack,即一切策略均可以保存在conntrack,实现数据流头包的一次匹配,后续包的conntrack查找,策略提取,动作执行的序列化操作。
基本思想就是“一切均可缓存在conntrack”,其中可以缓存的类别包括:
转发策略:接受还是丢弃
路由策略:从哪个接口发出
NAT策略:如何实现地址转换
流控策略:和流控相关的配置
封装策略:VPN或者GRE
感兴趣流匹配策略:是否感兴趣流
以上的这些都可以仅仅针对一个流的流头进行匹配,然后将结果cache到conntrack,后续的包就不必再去匹配了,而是仅仅需要查找到对应的conntrack,取出conntrack中缓存的策略,直接使用,本文以上的DROP notifiler就是为实现这个目标完成的第一步。现在我们看看现有的Netfilter有什么问题,其实没有什么问题,一切都很好,唯一的问题是可能很多人都不知道如何从一个HOOK点直接调用另一个HOOK点的HOOK函数,人们认为只有协议栈才可以调用HOOK点,也就是说被PRE/POST/IN/OUT/FORWARD等字面意义迷惑了,而实际上,NF_HOOK是在任何地方都可以调用的,看看bridge的实现,bridge-call-iptables就是这么玩的,到处都是从一个HOOK点函数直接调用NF_HOOK的例子。
好了,有了这个前提,我所谓的event机制其实早就已经有模板了,无非就是在一个HOOK函数中去触发另一个HOOK的函数,所以改变的就是一个名字,一个HOOK函数就是一个event!下面是一个例子。我们可以仅仅针对一个流头进行路由查找,然后将dst_entry缓存在conntrack里面,后续的包只要对应到该conntrack,就可以取出dst_entry,此时就不必继续进入PREROUTING-ROUTING-FORWARDING...了,而是直接触发POSTROUTING的转发事件,直接转发,这样其实也就类似BSD的Netgraph了,只是名字叫法不一样而已。Netfilter其实早就把框架给搭好了,现在需要做的就是写几个HOOK函数而已,当然,并不一定非要和iptables关联,和procfs,sysfs等关联均可,无非就是一个内核态和用户态的沟通方式而已!
最后是一个硬件接口问题,我之所以考虑上面那些,就是为了提高效率,要说效率,再好的算法也比不上三流算法注入的硬件,以上的想法和硬件结合是最好的了,所有的查表,转发等全部由硬件实现。由于硬件不灵活,所以更是需要接口的灵活,可以互相调用构成一张图,这才能发挥硬件最大的作用。
所有的查询全部归为conntrack的查询,流头慢速匹配规则,然后缓存到conntrack,或者慢速匹配模式(见上述的condition match实现的慢速匹配)匹配非流头,然后缓存到conntrack,总是,二者是不见不散,类似Linux kernel的dirver和device之间的probe关系一样。