NAT模式是IPVS最常用的一种模式。相比于TUN和DR模式,NAT模式更容易部署,仅仅是需要更改真实服务器的默认网关配置。
IPVS是基于Netfilter实现的。它注册了4个Netfilter钩子函数,其中与NAT模式相关的是ip_vs_in和ip_vs_out两个钩子函数。前者处理了客户端-〉服务器的数据包,后者则针对服务器-〉客户端的数据包。
1、ip_vs_in钩子函数
ip_vs_in函数的处理流程非常清晰:
预校验--〉ip_vs_conn对象的查找或生成--〉更新统计信息--〉更新ip_vs_conn对象的状态--〉修改sk_buff并转发数据包--〉IPVS状态同步--〉结束
1.1、预校验
预校验的过程很简单,主要包括:
- 确保数据包的类型为PACKET_HOST
- 确保数据包不是loopback设备发出来的
- 确保数据包的协议类型是IPVS所支持的,目前IPVS支持TCP、UDP、AH和ESP协议
1.2、ip_vs_conn对象的查找或生成
既然有数据包来了,必然有对应的ip_vs_conn对象。首先根据数据包的<源地址-源端口-目的地址-目的端口>,查找当前的 ip_vs_conn对象列表。如果没有找到的话,说明这是一个新连接,于是运行调度过程,找到一个合适的真实服务器,然后再生成一个新的 ip_vs_conn对象。这里先不对调度过程进行展开描述。
1.3、更新统计信息
这里先看一下ip_vs_stats结构,其各个成员的作用很容易看出来:
|
这是一个专门用于统计的数据结构,每个ip_vs_service对象、每个ip_vs_dest对象都包含有这么一个结构,另外还有一个ip_vs_stats全局变量。
函数ip_vs_in_stats和ip_vs_out_stats分别统计两个方向的数据包流量,函数ip_vs_conn_stats用于统计新建连接数。
|
conns、inpkts、outpkts、inbytes和outbytes统计比较容易,只需简单的加1。但cps等统计起来就要复杂一些了,它是通过内核定时器来实现的。每个ip_vs_stats对象都对应有一个ip_vs_estimator结构:
|
所有的ip_vs_estimator结构形成一张链表,通过全局变量est_list可以遍历这个链表。定时器的时间间隔为2秒,对应的触发函数为:
|
以cps的统计为例,其计算过程很简单,cps = (rate+cps)/2,其中单位为2^10。
1.4、更新ip_vs_conn对象的状态
在TCP数据包处理过程中,每个ip_vs_conn对象对应于一个TCP连接,因此也必须有一个状态转换过程,才能够引导此TCP连接正常建立和终止。这个状态转换颇为复杂,在后续内容将IN/OUT结合一起,来看TCP连接的状态转换。
1.5、修改sk_buff并转发数据包
NAT模式下的数据包转发由ip_vs_nat_xmit函数完成。对sk_buff数据结构的操作不熟悉,略过。
1.6、IPVS状态同步
先判断此ip_vs_conn对象是否需要进行主备机同步。首先当前IPVS必须是MASTER,并且此ip_vs_conn对象的状态为ESTABLISHED。另外,满足这些条件时,并非每个Packet转发的时候都进行同步,而是每50个Packet,才同步一次。
同步过程由函数ip_vs_sync_conn完成:
|
1.7、ip_vs_out钩子函数
ip_vs_out函数处理 服务器-〉客户端 的数据包。相比于ip_vs_in函数,它要简单得多。这里不再描述。
2、TCP状态转换过程
IPVS支持TCP、UDP、AH和ESP四种协议。由于TCP协议的逻辑相对复杂一些,所以IPVS对TCP协议的特殊处理也更多。
IPVS针对TCP协议的处理主要是体现在TCP状态维护上,而TCP状态维护依赖于一个状态转换矩阵:
|
与NAT模式相关的为INPUT和OUTPUT两张表,其意思也较容易理解:
- sNO、sES等为TCP状态,tcp_state_name_table为状态名称表,而tcp_timeouts表指明了每个状态的维系时 间。此维系时间决定了ip_vs_conn对象的生命期,当维系时间内此连接无任何输入输出,则ip_vs_conn对象自动销毁,它是通过设置 ip_vs_conn对象的timeout来实现的。
- 当连接处于某状态时,在tcp_states矩阵中查找对应状态列,然后根据当前的输入(INPUT指客户端输入,OUTPUT指真实服务器输出),查找到下一个状态值。
状态转换矩阵也可以用一张状态转换图来表示: