Linux之ip_conntrack容易混淆的问题点滴


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

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

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处理的入口的最后:
  1. if (set_reply)  
  2.     set_bit(IPS_SEEN_REPLY_BIT, &ct->status);  
这说明只要set_reply为真就会修改ct的一个状态位,而set_reply在ip_conntrack_in的resolve_normal_ct调用中就会被设置。
  1. //只要收到反向的包,就会设置IP_CT_ESTABLISHED,且把set_reply设置为1,然后返回到ip_conntrack_in的时候,就会导致ct->status的IPS_SEEN_REPLY_BIT的设置   
  2. if (DIRECTION(h) == IP_CT_DIR_REPLY) {  
  3.     *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;  
  4.     /* Please set reply bit if this packet OK */  
  5.     *set_reply = 1;  
  6. else {  
  7.     /* Once we've had two way comms, always ESTABLISHED. */  
  8.     //只要有IPS_SEEN_REPLY_BIT位被置位,那么就是IP_CT_ESTABLISHED   
  9.     if (test_bit(IPS_SEEN_REPLY_BIT, &h->ctrack->status)) {  
  10.         DEBUGP("ip_conntrack_in: normal packet for %p\n", h->ctrack);  
  11.         *ctinfo = IP_CT_ESTABLISHED;  
  12. ...  
因此可见IP_CT_ESTABLISHED状态和具体的协议是无关的,对于TCP而言,所有SYN后面的包都会是IP_CT_ESTABLISHED状态。然而由于TCP本身拥有可以监控连接的状态,比如close-wait等,因此它在ip_conntrack中又有一些子状态,这个用于在适当的时候释放ip_conntrack数据结构,因此只要TCP的ip_conntrack的time-wait子状态到期,其ip_conntrack数据结构就会被当即释放,这一切正是因为TCP将其协议状态映射成了ip_conntrack的子状态,而这些子状态知道什么时候一个tcp流结束了。然而这一切对于UDP以及ICMP而言就没有这么幸运了,它们没有所谓的子状态,它们只能使用大胆的ip_conntrack状态。
  • 1
  • 2
  • 下一页

相关内容