深入理解ip_conntrack的都知道,ip-conntrack本身对于TCP维护了一个状态机,值得注意的是,该状态机和TCP协议本身的状态机相似但不相同。其区别如下:
TCP状态机:为TCP的两端分别维护一个状态机,TCP连接的主动发起/被动发起和主动关闭/被动关闭的状态机转换是不同的。
ip_conntrack的TCP状态机:只有一个概念,即conntrack本身,因此也就只有一个状态机,并不区分主动和被动。ip_conntrack并没有发送端和接收端的概念,因此也就没有双工的概念,而TCP是全双工的传输层协议,所以在连接开始和结束的时候才会分别有两个状态机转换图。
那么,如何将TCP的状态机映射到ip_conntrack的TCP状态机呢?实际上很简单,ip_conntrack的TCP状态机要比TCP本身的状态机简单得多,它完全基于前一个状态以及收到的TCP段的标志位决定要转换到的状态,没有主动端和被动端一说,所以它可以实现成一个多维数组的方式!Linux内核中的实现是三维数组,第一维表示方向,第二维表示TCP段的标志,第三维表示前一个状态。其具体实现就不多说了,前面也写过一篇文章。
本文主要说一下ip_conntrack的TCP状态机面临TCP数据段时的表现以及其和几个恼人的TCP状态的关系。首先是一幅图,展示TCP状态和ip_conntrack的TCP状态:
ip_conntrack的TCP状态机_第1张图片下面我直接瞄向TIME_WAIT!ip_conntrack的TCP状态中也有TIME_WAIT,它和TCP本身的TIME_WAIT之间会不会有联动呢?先给出答案:没有!
1.TIME_WAIT状态的conntrack在接收到syn的时候,会立即将该conntrack删除,然后建立一个新的conntrack
这不会带来任何问题。虽然conntrack的TW状态并没有过期,但是conntrack本身并不处理TW状态的任何语义规范。正如conntrack的名称所示,它没有任何处理逻辑,只是有一个记录的功能,确认不管是什么协议,TCP也好,UDP也好,确保它们雁过留声。当然,你可以基于留下的足迹来做一些工作,比如NAT。
事实上,TCP只在终端系统的第四层根据详细的TCP规范被处理!TCP当初在设计的时候就被设计成一个端到端的协议,这是它的本质。TCP之所以复杂是因为它经得起被复杂化。TCP并不是一开始就很复杂的,它的核心超级简单!一个经得起被任意程度复杂化的协议只能是端到端的协议,否则一个分布式的协议遇到的很多状态同步问题以及功能叠加重复的问题会阻碍协议复杂化的进程。想一下一个分布式的复杂协议OSPF的发展就知道了。
因此,不管从原则上还是从实际实现上,conntrack都不过问TCP规范,也不强制保持状态过期时间,所有这一切都在端系统的传输层处理(不考虑SYN代理!),conntrack只是单纯的维持状态机运转,即简单的“来了什么数据,转到什么状态,过了时间就超时删除”。其任何状态都有一个超时时间,过期后的行为就是简单的“被删除”!
2.ip_conntrack不处理TCP,是的,但是并不绝对
conntrack不应该处理TCP,但是有时不得不处理,因为它首先要保证自身的状态不会乱掉。一个例子就是,conntrack处理逻辑会丢掉一个TCP连接中窗口外的数据。