IPVS支持协议的超时时间

以下ipvsadm命令用于显示和设置ipvs连接的超时时间,单位为秒(seconds)。

$ sudo ipvsadm -l --timeout
Timeout (tcp tcpfin udp): 900 120 300
$ 
$ sudo ipvsadm --set 3600 120 300
$ 
$ sudo ipvsadm -l --timeout      
Timeout (tcp tcpfin udp): 3600 120 300
$ 

三个值依次为TCP会话的超时时间,在接收到FIN报文后的TCP会话超时时间,以及UDP报文的超时时间。当其中某个值为0时,表示不设置此值,其将保留上一次设置的数值。

需要注意的是,这三个timeout值是针对ipvs网络命名空间的全局数值,而不只是针对某个虚拟服务。

内核函数ip_vs_set_timeout处理timeout的参数设置。有以下代码可见,前两个timeout设置到了IPPROTO_TCP协议数据的timeout_table数组,最后一个timeout值设置到了IPPROTO_UDP协议数据结构的timeout_table数组。这三个值分别对应的状态为:IP_VS_TCP_S_ESTABLISHED、IP_VS_TCP_S_FIN_WAIT 和 IP_VS_UDP_S_NORMAL。

static int ip_vs_set_timeout(struct netns_ipvs *ipvs, struct ip_vs_timeout_user *u)
{
#if defined(CONFIG_IP_VS_PROTO_TCP) || defined(CONFIG_IP_VS_PROTO_UDP)
    struct ip_vs_proto_data *pd;
#endif

#ifdef CONFIG_IP_VS_PROTO_TCP
    if (u->tcp_timeout) {
        pd = ip_vs_proto_data_get(ipvs, IPPROTO_TCP);
        pd->timeout_table[IP_VS_TCP_S_ESTABLISHED] = u->tcp_timeout * HZ;
    }
    if (u->tcp_fin_timeout) {
        pd = ip_vs_proto_data_get(ipvs, IPPROTO_TCP);
        pd->timeout_table[IP_VS_TCP_S_FIN_WAIT] = u->tcp_fin_timeout * HZ;
    }
#endif
#ifdef CONFIG_IP_VS_PROTO_UDP
    if (u->udp_timeout) {
        pd = ip_vs_proto_data_get(ipvs, IPPROTO_UDP);
        pd->timeout_table[IP_VS_UDP_S_NORMAL]  = u->udp_timeout * HZ;
    }
#endif
}

TCP协议超时时间

对于TCP协议,其协议数据结构在函数__ip_vs_tcp_init中初始化,tcp_timeouts是预先定义好的TCP相关timeout值。

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;
}
int *ip_vs_create_timeout_table(int *table, int size)
{
    return kmemdup(table, size, GFP_KERNEL);
}  

在tcp_timeouts数组中,内核为TCP协议的每个状态都定义了相应的超时时间,回忆以上的介绍,ipvsadm工具仅是可修改IP_VS_TCP_S_ESTABLISHED状态和IP_VS_TCP_S_FIN_WAIT状态的超时值,默认情况下分别为1560(900s)和260(120s)。

static const int tcp_timeouts[IP_VS_TCP_S_LAST+1] = {
    [IP_VS_TCP_S_NONE]      =   2*HZ,
    [IP_VS_TCP_S_ESTABLISHED]   =   15*60*HZ,
    [IP_VS_TCP_S_SYN_SENT]      =   2*60*HZ,
    [IP_VS_TCP_S_SYN_RECV]      =   1*60*HZ,
    [IP_VS_TCP_S_FIN_WAIT]      =   2*60*HZ,
    [IP_VS_TCP_S_TIME_WAIT]     =   2*60*HZ,
    [IP_VS_TCP_S_CLOSE]     =   10*HZ,
    [IP_VS_TCP_S_CLOSE_WAIT]    =   60*HZ,
    [IP_VS_TCP_S_LAST_ACK]      =   30*HZ,
    [IP_VS_TCP_S_LISTEN]        =   2*60*HZ,
    [IP_VS_TCP_S_SYNACK]        =   120*HZ,
    [IP_VS_TCP_S_LAST]      =   2*HZ,
};

内核函数set_tcp_state在处理TCP状态转换的同时,更新当前连接的超时时间。

static inline void set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, int direction, struct tcphdr *th)
{
    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协议,在函数ip_vs_tcp_conn_listen中也进行超时时间的更新,此函数主要由IPVS的FTP模块使用。

void ip_vs_tcp_conn_listen(struct ip_vs_conn *cp)
{
    struct ip_vs_proto_data *pd = ip_vs_proto_data_get(cp->ipvs, IPPROTO_TCP);

    spin_lock_bh(&cp->lock);
    cp->state = IP_VS_TCP_S_LISTEN;
    cp->timeout = (pd ? pd->timeout_table[IP_VS_TCP_S_LISTEN] : tcp_timeouts[IP_VS_TCP_S_LISTEN]);
    spin_unlock_bh(&cp->lock);
}

UDP协议超时时间

函数__udp_init负责初始化UDP协议数据结构中的timeout_table数组,udp_timeouts为预定义的超时时间。

static int __udp_init(struct netns_ipvs *ipvs, struct ip_vs_proto_data *pd)
{
    ip_vs_init_hash_table(ipvs->udp_apps, UDP_APP_TAB_SIZE);
    pd->timeout_table = ip_vs_create_timeout_table((int *)udp_timeouts,
                            sizeof(udp_timeouts));
    if (!pd->timeout_table)
        return -ENOMEM;
    return 0;
}

由于UDP是无状态的协议,此处只定义了一个IP_VS_UDP_S_NORMAL状态,其超时时间为5*60(300s)。可使用ipvsadm命令修改此值。

static const int udp_timeouts[IP_VS_UDP_S_LAST+1] = {
    [IP_VS_UDP_S_NORMAL]    =   5*60*HZ,
    [IP_VS_UDP_S_LAST]      =   2*HZ,
};

内核函数的UDP协议转换函数udp_state_transition中,修改当前连接的超时时间。

static void udp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, struct ip_vs_proto_data *pd)
{
    if (unlikely(!pd)) {
        pr_err("UDP no ns data\n");
        return;
    }

    cp->timeout = pd->timeout_table[IP_VS_UDP_S_NORMAL];
}

SCTP协议超时时间

函数__ip_vs_sctp_init初始化SCTP的协议数据结构的成员timeout_table数组,sctp_timeouts为预定义的SCTP相关超时时间。

static int __ip_vs_sctp_init(struct netns_ipvs *ipvs, struct ip_vs_proto_data *pd)
{
    ip_vs_init_hash_table(ipvs->sctp_apps, SCTP_APP_TAB_SIZE);
    pd->timeout_table = ip_vs_create_timeout_table((int *)sctp_timeouts, sizeof(sctp_timeouts));
    if (!pd->timeout_table)
        return -ENOMEM;
    return 0;
}

目前,命令行工具ipvsadm不能修改SCTP相关的超时时间。

#define IP_VS_SCTP_MAX_RTO  ((60 + 1) * HZ)

static const int sctp_timeouts[IP_VS_SCTP_S_LAST + 1] = {
    [IP_VS_SCTP_S_NONE]             = 2 * HZ,
    [IP_VS_SCTP_S_INIT1]            = (0 + 3 + 1) * HZ,
    [IP_VS_SCTP_S_INIT]             = IP_VS_SCTP_MAX_RTO,
    [IP_VS_SCTP_S_COOKIE_SENT]      = IP_VS_SCTP_MAX_RTO,
    [IP_VS_SCTP_S_COOKIE_REPLIED]   = IP_VS_SCTP_MAX_RTO,
    [IP_VS_SCTP_S_COOKIE_WAIT]      = IP_VS_SCTP_MAX_RTO,
    [IP_VS_SCTP_S_COOKIE]           = IP_VS_SCTP_MAX_RTO,
    [IP_VS_SCTP_S_COOKIE_ECHOED]    = IP_VS_SCTP_MAX_RTO,
    [IP_VS_SCTP_S_ESTABLISHED]      = 15 * 60 * HZ,
    [IP_VS_SCTP_S_SHUTDOWN_SENT]      = IP_VS_SCTP_MAX_RTO,
    [IP_VS_SCTP_S_SHUTDOWN_RECEIVED]  = IP_VS_SCTP_MAX_RTO,
    [IP_VS_SCTP_S_SHUTDOWN_ACK_SENT]  = IP_VS_SCTP_MAX_RTO,
    [IP_VS_SCTP_S_REJECTED]         = (0 + 3 + 1) * HZ,
    [IP_VS_SCTP_S_CLOSED]           = IP_VS_SCTP_MAX_RTO,
    [IP_VS_SCTP_S_LAST]             = 2 * HZ,
};

在函数SCTP状态变更函数set_sctp_state中,修改当前连接的超时时间值。

static inline void set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, int direction, const struct sk_buff *skb)
{
    if (likely(pd))
        cp->timeout = pd->timeout_table[cp->state = next_state];
    else    /* What to do ? */
        cp->timeout = sctp_timeouts[cp->state = next_state];
}

IPVS超时时间获取

内核函数__ip_vs_get_timeouts用于获取TCP协议相应的IP_VS_TCP_S_ESTABLISHED和IP_VS_TCP_S_FIN_WAIT状态的超时时间,以及UDP协议的IP_VS_UDP_S_NORMAL状态的超时时间。

static inline void __ip_vs_get_timeouts(struct netns_ipvs *ipvs, struct ip_vs_timeout_user *u)
{
#if defined(CONFIG_IP_VS_PROTO_TCP) || defined(CONFIG_IP_VS_PROTO_UDP)
    struct ip_vs_proto_data *pd;
#endif
    memset(u, 0, sizeof (*u));

#ifdef CONFIG_IP_VS_PROTO_TCP
    pd = ip_vs_proto_data_get(ipvs, IPPROTO_TCP);
    u->tcp_timeout = pd->timeout_table[IP_VS_TCP_S_ESTABLISHED] / HZ;
    u->tcp_fin_timeout = pd->timeout_table[IP_VS_TCP_S_FIN_WAIT] / HZ;
#endif
#ifdef CONFIG_IP_VS_PROTO_UDP
    pd = ip_vs_proto_data_get(ipvs, IPPROTO_UDP);
    u->udp_timeout = pd->timeout_table[IP_VS_UDP_S_NORMAL] / HZ;
#endif
}

内核版本 4.15

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页