TCP状态初始化在函数__ip_vs_tcp_init中完成,此函数在ipvs网络命名空间初始化时执行,其将全局TCP状态数组tcp_states的地址赋值给TCP协议数据结构的成员tcp_state_table指针。
static int __ip_vs_tcp_init(struct netns_ipvs *ipvs, struct ip_vs_proto_data *pd)
{
ip_vs_init_hash_table(ipvs->tcp_apps, TCP_APP_TAB_SIZE);
pd->timeout_table = ip_vs_create_timeout_table((int *)tcp_timeouts, sizeof(tcp_timeouts));
if (!pd->timeout_table)
return -ENOMEM;
pd->tcp_state_table = tcp_states;
return 0;
}
全局TCP状态数组tcp_states定义如下:
static struct tcp_states_t tcp_states [] = {
/* INPUT */
/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI, sSA */
/*syn*/ {{sSR, sES, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSR }},
/*fin*/ {{sCL, sCW, sSS, sTW, sTW, sTW, sCL, sCW, sLA, sLI, sTW }},
/*ack*/ {{sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sCL, sLI, sES }},
/*rst*/ {{sCL, sCL, sCL, sSR, sCL, sCL, sCL, sCL, sLA, sLI, sSR }},
/* OUTPUT */
/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI, sSA */
/*syn*/ {{sSS, sES, sSS, sSR, sSS, sSS, sSS, sSS, sSS, sLI, sSR }},
/*fin*/ {{sTW, sFW, sSS, sTW, sFW, sTW, sCL, sTW, sLA, sLI, sTW }},
/*ack*/ {{sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sLA, sES, sES }},
/*rst*/ {{sCL, sCL, sSS, sCL, sCL, sTW, sCL, sCL, sCL, sCL, sCL }},
/* INPUT-ONLY */
/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI, sSA */
/*syn*/ {{sSR, sES, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSR }},
/*fin*/ {{sCL, sFW, sSS, sTW, sFW, sTW, sCL, sCW, sLA, sLI, sTW }},
/*ack*/ {{sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sCL, sLI, sES }},
/*rst*/ {{sCL, sCL, sCL, sSR, sCL, sCL, sCL, sCL, sLA, sLI, sCL }},
};
其索引由两部分组成。第一部分是偏移部分,参见数组tcp_state_off。偏移值为:0、4和8,对应于三种方向定义:TCP_DIR_INPUT、TCP_DIR_OUTPUT和TCP_DIR_INPUT_ONLY。
#define TCP_DIR_INPUT 0
#define TCP_DIR_OUTPUT 4
#define TCP_DIR_INPUT_ONLY 8
static const int tcp_state_off[IP_VS_DIR_LAST] = {
[IP_VS_DIR_INPUT] = TCP_DIR_INPUT,
[IP_VS_DIR_OUTPUT] = TCP_DIR_OUTPUT,
[IP_VS_DIR_INPUT_ONLY] = TCP_DIR_INPUT_ONLY,
};
第二部分为偏移后的内部索引,参见函数tcp_state_idx,TCP头部的四个标志位:RST、SYN、FIN和ACK,分别对应索引:3、0、1、和2。需要注意的是这里是有优先权区分的,顺序不能颠倒,比如RST标志优先级最高,此标志如果存在,内部索引为3。
static inline int tcp_state_idx(struct tcphdr *th)
{
if (th->rst)
return 3;
if (th->syn)
return 0;
if (th->fin)
return 1;
if (th->ack)
return 2;
return -1;
}
TCP状态转换数组是预定义的tcp_states,以下我们以IP_VS_DIR_INPUT方向为例看一下其初始化的内容。首先其子成员数组next_state的索引为TCP的状态值(总共11个状态);其次,对于接收到的SYN报文,如果当前TCP状态为sES(IP_VS_TCP_S_ESTABLISHED)状态不做变化,如果当前状态为sSS(IP_VS_TCP_S_SYN_SENT),表明为两端同时建立连接,下一个状态为sES(IP_VS_TCP_S_ESTABLISHED);其与任何情况下接收到SYN报文,下一个状态都是sSR(IP_VS_TCP_S_SYN_RECV)状态。最后,关于其它报文(FIN、ACK和RST)触发的状态变化,参见tcp_states数组。
struct tcp_states_t {
int next_state[IP_VS_TCP_S_LAST];
};
static struct tcp_states_t tcp_states [] = {
/* INPUT */
/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI, sSA */ /* TCP状态为索引 */
/*syn*/ {{sSR, sES, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSR }},
/*fin*/ {{sCL, sCW, sSS, sTW, sTW, sTW, sCL, sCW, sLA, sLI, sTW }},
/*ack*/ {{sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sCL, sLI, sES }},
/*rst*/ {{sCL, sCL, sCL, sSR, sCL, sCL, sCL, sCL, sLA, sLI, sSR }},
}
#define sNO IP_VS_TCP_S_NONE
#define sES IP_VS_TCP_S_ESTABLISHED
#define sSS IP_VS_TCP_S_SYN_SENT
#define sSR IP_VS_TCP_S_SYN_RECV
函数set_tcp_state完成TCP状态的变换。由于实际上仅有两个方向的流量,TCP_DIR_INPUT_ONLY计算所得。如果当前的连接标志变量中有IP_VS_CONN_F_NOOUTPUT标志,并且当前的状态偏移(即数据流向)也不等于TCP_DIR_OUTPUT,ipvs系统将这种情况归为TCP_DIR_INPUT_ONLY。
使用TCP协议数据结构中的成员tcp_state_table找到下一个新状态值。最后在改变连接状态值的同时,更新连接的超时时间。
static inline void set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, int direction, struct tcphdr *th)
{
int state_idx;
int new_state = IP_VS_TCP_S_CLOSE;
int state_off = tcp_state_off[direction];
if (cp->flags & IP_VS_CONN_F_NOOUTPUT) {
if (state_off == TCP_DIR_OUTPUT)
cp->flags &= ~IP_VS_CONN_F_NOOUTPUT;
else
state_off = TCP_DIR_INPUT_ONLY;
}
if ((state_idx = tcp_state_idx(th)) < 0)
goto tcp_state_out;
new_state = pd->tcp_state_table[state_off+state_idx].next_state[cp->state];
if (likely(pd))
cp->timeout = pd->timeout_table[cp->state = new_state];
else /* What to do ? */
cp->timeout = tcp_timeouts[cp->state = new_state];
}
TCP状态的active/inactive属性,由函数tcp_state_active进行判读,参数为TCP状态值。
static bool tcp_state_active(int state)
{
if (state >= IP_VS_TCP_S_LAST)
return false;
return tcp_state_active_table[state];
}
ipvs系统中使用全局数组tcp_state_active_table预定义了TCP状态所对应的active/inactive属性。如下所示,active属性的状态有4个:x_ESTABLISHED、x_SYN_SENT、x_SYN_RECV、x_SYNACK;其与的TCP状态都为inactive属性。
static const bool tcp_state_active_table[IP_VS_TCP_S_LAST] = {
[IP_VS_TCP_S_NONE] = false,
[IP_VS_TCP_S_ESTABLISHED] = true,
[IP_VS_TCP_S_SYN_SENT] = true,
[IP_VS_TCP_S_SYN_RECV] = true,
[IP_VS_TCP_S_FIN_WAIT] = false,
[IP_VS_TCP_S_TIME_WAIT] = false,
[IP_VS_TCP_S_CLOSE] = false,
[IP_VS_TCP_S_CLOSE_WAIT] = false,
[IP_VS_TCP_S_LAST_ACK] = false,
[IP_VS_TCP_S_LISTEN] = false,
[IP_VS_TCP_S_SYNACK] = true,
};
在以上接收的TCP状态变化函数set_tcp_state中,根据TCP状态的active/inactive属性设置当前连接的相应标志,已经记录连接数量信息。对于活动active的连接,如果其TCP状态转换为inactive属性的状态,需要递减activeconns的数量,递增inactconns数量,并为此连接设置IP_VS_CONN_F_INACTIVE标志。
对于不活动inactive的连接,如果其TCP状态转换为active属性的状态,执行与以上相反的操作。
static inline void set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, int direction, struct tcphdr *th)
{
if (new_state != cp->state) {
struct ip_vs_dest *dest = cp->dest;
if (dest) {
if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && !tcp_state_active(new_state)) {
atomic_dec(&dest->activeconns);
atomic_inc(&dest->inactconns);
cp->flags |= IP_VS_CONN_F_INACTIVE;
} else if ((cp->flags & IP_VS_CONN_F_INACTIVE) && tcp_state_active(new_state)) {
atomic_inc(&dest->activeconns);
atomic_dec(&dest->inactconns);
cp->flags &= ~IP_VS_CONN_F_INACTIVE;
}
}
}
}
内核版本 4.15