关闭

我和ip_conntrack不得不说的一些事

18008人阅读 评论(1) 收藏 举报
面对让人无语的ip_conntrack,我有一种说不出的感觉!自从接触它到现在,已经两年多了,其间我受到过它的恩惠,也被它蹂躏过,被它玩过,但是又不忍心舍弃它,因为我找不到更好的替代。工作中,学习中,用到了ip_conntrack的几乎所有特性,然而这些都不能拿来主义得使用,过程中多少有些美中不足,多少会留下一些遗憾,总结下来,我遇到的典型而非全部的问题如下所列:
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;
编译十分顺利,用起来效果也十分明显,在5000条iptables规则下,性能几乎没有任何损耗,而且也没有报任何错误,因为它简单,所以它好用。
结语: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关系一样。
0
0
查看评论

linux内核netfilter之ip_conntrack模块的作用--抽象总结

<br />nat规则将数据流的地址信息进行转换,转换了之后需要将转换后的地址信息写入ip_conntrack结构体中,经过nat之后目的地址无非两个方向,一个是本机(redirect target),一个是其它机器(网关上的nat一般都这样),于是netfilter需要对这两个方向的转...
  • dog250
  • dog250
  • 2010-06-25 21:47
  • 6397

再次深入到ip_conntrack的conntrack full问题

增加nf_conntrack_max固然可以缓解这个问题,或者说减小conntrack表项占据内核内存的时间也可以缓解之,然而这种补救措施都是治标不治本的.注解:不要过度减小NEW以及TCP的establish的CT状态的timeout的原因尽量不要减小NEW状态时间,因为对于某些恶劣的网络,一个数...
  • dog250
  • dog250
  • 2012-02-15 21:48
  • 14182

ip_conntrack的TCP状态机

深入理解ip_conntrack的都知道,ip-conntrack本身对于TCP维护了一个状态机,值得注意的是,该状态机和TCP协议本身的状态机相似但不相同。其区别如下:TCP状态机:为TCP的两端分别维护一个状态机,TCP连接的主动发起/被动发起和主动关闭/被动关闭的状态机转换是不同的。ip_co...
  • dog250
  • dog250
  • 2013-11-09 08:21
  • 5177

不要盲目增加ip_conntrack_max-理解Linux内核内存

1.由ip_conntrack引出的Linux内存映射有很多文章在讨论关于ip_conntrack表爆满之后丢弃数据包的问题,对此研究深入一些的知道Linux有个内核参数ip_conntrack_max,在拥有较大内存的机器中默认65536,于是疯狂的增加这个参数,比如设置成10000…00,只要不...
  • dog250
  • dog250
  • 2011-12-27 23:58
  • 29622

我和ip_conntrack不得不说的一些事

面对让人无语的ip_conntrack,我有一种说不出的感觉!自从接触它到现在,已经两年多了,其间我受到过它的恩惠,也被它蹂躏过,被它玩过,但是又不忍心舍弃它,因为我找不到更好的替代。工作中,学习中,用到了ip_conntrack的几乎所有特性,然而这些都不能拿来主义得使用,过程中多少有些美中不足,...
  • stonesharp
  • stonesharp
  • 2015-04-28 21:51
  • 616

Linux协议栈-netfilter(2)-conntrack

连接跟踪(conntrack)用来跟踪和记录一个连接的状态,它为经过协议栈的数据包记录状态,这为防火墙检测连接状态提供了参考,同时在数据包需要做NAT时也为转换工作提供便利。 本文基于Linux内核2.6.31实现的conntrack进行源码分析。 1. conntrack模块初始化 1....
  • jasonchen_gbd
  • jasonchen_gbd
  • 2015-04-04 17:37
  • 5125

conntrack-tools的使用

conntrack-tools 是一套Linux用户空间连接跟踪工具,用于系统管理员进行交互连接跟踪系统,该模块提供了iptables的状态数据包检查。它包括了用户空间的守护进程conntrackd和命令行界面conntrack。详细信息参看 manual http://conntrack-to...
  • viewsky11
  • viewsky11
  • 2016-10-28 15:31
  • 1850

netfilter之conntrack笔记

一、控制结构 sk_buff 和网络报文的存储空间    二、分片的网络报文与scatter/gather IO 网络报文在内存中不一定是连续存储的,同一个网络报文有可能被分成几片存放在内存的不同位置(不要和IP分片混淆,IP分片是将一个网络报文分成多个...
  • appletreesujie
  • appletreesujie
  • 2011-10-13 18:39
  • 6255

kernel nf_conntrack: table full, dropping packet 解决办法

kernel nf_conntrack: table full, dropping packet 解决办法 Posted on 2012-02-09 by york_gu 最近很长一段时间,千寻影视的服务器状况不佳,一到晚上高峰时间便访问经常出问题,症状...
  • susubuhui
  • susubuhui
  • 2016-04-12 15:51
  • 2263

conntrack命令安装分享

在论坛中看到一个帖子关于ip_conntrack参数相关的,后来打开了个关于man命令conntrack的连接。 在此分享,如对你有一点帮主也是一点荣幸 http://linux.die.net/man/8/conntrack    man连接地址 正文: ...
  • wuwenhuahua
  • wuwenhuahua
  • 2015-01-05 17:54
  • 1811
    个人资料
    • 访问:7038838次
    • 积分:84476
    • 等级:
    • 排名:第18名
    • 原创:1434篇
    • 转载:2篇
    • 译文:0篇
    • 评论:3112条
    文章存档
    最新评论