欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

IPVS支持协议的超时时间

程序员文章站 2024-03-13 15:56:33
...

以下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

相关标签: ipvs