linux之ipconntrack容易混淆的问题点滴 -电脑资料

电脑资料 时间:2019-01-01 我要投稿
【www.unjs.com - 电脑资料】

   

    《再次深入到ip_conntrack的conntrack full问题》最后的一个问题提示

    ip_conntrack有一个event机制,可以主动通报ip_conntrack的一些事件,包括追踪信息到期删除等事件,通知给谁呢?当然是通知给所有感兴趣的模块了,其中之一就是用户态进程,这样用户态进程得知可以采取一些措施,比如防火墙上设置一些放过规则等,这个通知机制使用了观察者设计模式,

linux之ipconntrack容易混淆的问题点滴

    Linux ip_conntrack的一些细节问题

    由于Linux的ip_conntrack具有大量的状态,而每一种状态都有一定的超时时间,这些状态中的个别可以和网络协议的不同状态建立映射关系,另一些则不能。如果协议本身是有状态的,那么就很方便建立一种映射关系,反之如果协议没有状态,那么就不能建立映射关系。有时候,对于无状态的协议而言,ip_conntrack的状态超时时间会带来一些令人郁闷的问题。

    总之,Linux的ip_conntrack机制如果深究起来还真的看点,如果搞防火墙开发,实属不可不察也。下面就举几个例子。

    例子举例

    例子1:

    对于UDP而言,它本身没有状态,无需建立连接,无需确认,纯粹就是一个数据报协议,因此ip_conntrack使用经验值来设定各个状态的超时时间,但是如果双方有一段时间没有发包,那么当初始接收端再发起一个数据包时,就会给防火墙上的基于ctstate的过滤规则带来影响,具体参见《再次深入到ip_conntrack的conntrack full问题》。

    例子2:

    对于UDP而言,如果在Linux防火墙上使用NAT,那么在数据通信期间,即使NAT规则被删除或者被修改,该数据流依然会使用老的NAT规则而不是不使用任何规则或者使用新的规则。

    例子3:

    在早期的内核上,加载ip_conntrack模块,然后ping一个可以ping通的地址,则在/proc/net/ip_conntrack中却看不到该连接的追踪信息,而ping一个根本不可达的地址,反而能看到一个反方向为UNREPLY的追踪信息。值得注意的是,起码在2.6.32内核上,这个问题不再存在,而在2.6.9内核上还是存在的,具体哪个版本修正了它,没有详细看内核的ChangeLog。

    例子4:

    对于TCP而言,只要一个连接断开了,/proc/net/ip_conntrack中的关于该连接的追踪信息将马上被删除,而不会像UDP那样保留。

    针对以上问题的一些解释

    例子1的解释:

    这个没有什么好说的,根本原因就是UDP本身没有状态,而ip_conntrack将establish状态强加给了一个UDP连接,所谓的ip_conntrack的establish状态对于所有的协议都是说有去有回的流,当然对于UDP更是这样。对于TCP而言,ip_conntrack将不是syn状态的流量都映射成了establisd状态(注意不是TCP的established状态),这也符合上述定义。在ip_conntrack处理的入口的最后:

    if (set_reply)

    set_bit(IPS_SEEN_REPLY_BIT, &ct->status);

    if (set_reply)

    set_bit(IPS_SEEN_REPLY_BIT, &ct->status);这说明只要set_reply为真就会修改ct的一个状态位,而set_reply在ip_conntrack_in的resolve_normal_ct调用中就会被设置。

    //只要收到反向的包,就会设置IP_CT_ESTABLISHED,且把set_reply设置为1,然后返回到ip_conntrack_in的时候,就会导致ct->status的IPS_SEEN_REPLY_BIT的设置

    if (DIRECTION(h) == IP_CT_DIR_REPLY) {

    *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;

    /* Please set reply bit if this packet OK */

    *set_reply = 1;

    } else {

    /* Once we've had two way comms, always ESTABLISHED. */

    //只要有IPS_SEEN_REPLY_BIT位被置位,那么就是IP_CT_ESTABLISHED

    if (test_bit(IPS_SEEN_REPLY_BIT, &h->ctrack->status)) {

    DEBUGP("ip_conntrack_in: normal packet for %p\n", h->ctrack);

    *ctinfo = IP_CT_ESTABLISHED;

    ...

    //只要收到反向的包,就会设置IP_CT_ESTABLISHED,且把set_reply设置为1,然后返回到ip_conntrack_in的时候,就会导致ct->status的IPS_SEEN_REPLY_BIT的设置

    if (DIRECTION(h) == IP_CT_DIR_REPLY) {

    *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;

    /* Please set reply bit if this packet OK */

    *set_reply = 1;

    } else {

    /* Once we've had two way comms, always ESTABLISHED. */

    //只要有IPS_SEEN_REPLY_BIT位被置位,那么就是IP_CT_ESTABLISHED

    if (test_bit(IPS_SEEN_REPLY_BIT, &h->ctrack->status)) {

    DEBUGP("ip_conntrack_in: normal packet for %p\n", h->ctrack);

    *ctinfo = IP_CT_ESTABLISHED;

    ...因此可见IP_CT_ESTABLISHED状态和具体的协议是无关的,对于TCP而言,所有SYN后面的包都会是IP_CT_ESTABLISHED状态,

电脑资料

linux之ipconntrack容易混淆的问题点滴》(https://www.unjs.com)。然而由于TCP本身拥有可以监控连接的状态,比如close-wait等,因此它在ip_conntrack中又有一些子状态,这个用于在适当的时候释放ip_conntrack数据结构,因此只要TCP的ip_conntrack的time-wait子状态到期,其ip_conntrack数据结构就会被当即释放,这一切正是因为TCP将其协议状态映射成了ip_conntrack的子状态,而这些子状态知道什么时候一个tcp流结束了。然而这一切对于UDP以及ICMP而言就没有这么幸运了,它们没有所谓的子状态,它们只能使用大胆的ip_conntrack状态。

    例子2的解释:

    Linux的iptables/Netfilter实现的NAT是有状态NAT,它只对一个流的头包查询NAT表,并将查询结果设置给属于该流的ip_conntrack数据结构中,一个流的头包此后的所有数据包都使用这个结果。加之UDP没有状态,ip_conntrack除非等到establish状态到期,否则无法释放(其实它根本不知道该UDP流什么时候会结束,就连establish的到期时间也是一个经验值)ip_conntrack数据结构,既然不释放该数据结构,那么头包保存的NAT结果就一直有效,因此才会出现这样的问题。对于ICMP而言,较特殊,不同的内核版本是不同的,这就是例子3中的情形。

最新文章